summaryrefslogtreecommitdiffstats
path: root/kicker/applets/launcher
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit4aed2c8219774f5d797760606b8489a92ddc5163 (patch)
tree3f8c130f7d269626bf6a9447407ef6c35954426a /kicker/applets/launcher
downloadtdebase-4aed2c8219774f5d797760606b8489a92ddc5163.tar.gz
tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kicker/applets/launcher')
-rw-r--r--kicker/applets/launcher/ChangeLog40
-rw-r--r--kicker/applets/launcher/Makefile.am27
-rw-r--r--kicker/applets/launcher/ToDo8
-rw-r--r--kicker/applets/launcher/configdlg.cpp101
-rw-r--r--kicker/applets/launcher/configdlg.h55
-rw-r--r--kicker/applets/launcher/configdlgbase.ui273
-rw-r--r--kicker/applets/launcher/easyvector.h147
-rw-r--r--kicker/applets/launcher/flowgridmanager.cpp316
-rw-r--r--kicker/applets/launcher/flowgridmanager.h99
-rw-r--r--kicker/applets/launcher/launcherapplet.kcfg77
-rw-r--r--kicker/applets/launcher/popularity.cpp424
-rw-r--r--kicker/applets/launcher/popularity.h127
-rw-r--r--kicker/applets/launcher/prefs.kcfgc6
-rw-r--r--kicker/applets/launcher/quickaddappsmenu.cpp67
-rw-r--r--kicker/applets/launcher/quickaddappsmenu.h51
-rw-r--r--kicker/applets/launcher/quickbutton.cpp322
-rw-r--r--kicker/applets/launcher/quickbutton.h124
-rw-r--r--kicker/applets/launcher/quickbuttongroup.h60
-rw-r--r--kicker/applets/launcher/quicklauncher.cpp1093
-rw-r--r--kicker/applets/launcher/quicklauncher.desktop140
-rw-r--r--kicker/applets/launcher/quicklauncher.h138
21 files changed, 3695 insertions, 0 deletions
diff --git a/kicker/applets/launcher/ChangeLog b/kicker/applets/launcher/ChangeLog
new file mode 100644
index 000000000..b86dbd7aa
--- /dev/null
+++ b/kicker/applets/launcher/ChangeLog
@@ -0,0 +1,40 @@
+2004-06-15 Dan Bullok <[email protected]>
+ * Fixed flicker on drop/arrange.
+ * Can now drop more than one item at a time
+ * Empty panel now has size>0, so user can still drop icons on it.
+ * Fixed gcc 3.4 incompatibilities in EasyVector
+ * Fixed Drag and Drop Bugs :
+ * Crash when non-url dropped on quicklauncher
+ * Crash panel button dropped on quicklauncher
+ * Able to drop objects when quicklauncher is locked
+ * Add application context menu now inserts new app at current location
+
+2004-06-14 Dan Bullok <[email protected]>
+ * Fixed License statements (added email & pointer to COPYING)
+ * GUI changes made in previous commit.
+ * continued separating QuickURL and QuickButton.
+ * Rearranged menu items to make more sense
+
+2004-06-13 Dan Bullok <[email protected]>
+ * Fixes Bugs #42278, #55625, #63506, #67891, #76868, #79848, #80530 - also makes bacon & eggs for breakfast (toast & jam available for vegetarians)
+ * v2.0alpha
+ * Replaced most of the innards of QuickLauncher class.
+ * Icons are positions evenly across available space.
+ * Drag and drop works for all kicker widths.
+ * Visual Feedback of drop location
+ * Icon Size is user-definable. Sensible(?) values are used by default (Icons grow slightly as panel size increases. #icons per row for given panel size is Tiny,Small: 1, Normal: 2, Large:3, 110pix: 4).
+ * User can lock icon drag and drop (prevents accidentally screwing things up).
+ * Icons can either take up exactly the defined amount of space (ConserveSpace=true - this is the default), or grow slightly to take advantage of unused space.
+ * Spouts tons of debugging info to kdDebug (for now).
+
+2004-06-12 Dan Bullok <[email protected]>
+ * Fixed bug #75351: Tooltips change to filenames after rearranging applications in quicklauncher.
+ * Moved the URL->(menuID,service,kurl) functionality from the QuickButton constructor to its own class: QuickURL. Very similar code is used elsewhere in kicker, and should eventually be merged.
+ * Renamed some methods in QuickButton (getId -> menuId, getURL -> url) This matches the predominant KDE naming style.
+ * Groundwork laid for variable-sized buttons.
+
+2004-06-12 Dan Bullok <[email protected]>
+ * Changed member variable names: myVar -> _myVar.
+
+2004-06-12 Dan Bullok <[email protected]>
+ * Fixed formatting only - no code changes. There were a few conflicting indenting styles. I picked the one that looked like it was the oldest, and applied it to all the files.
diff --git a/kicker/applets/launcher/Makefile.am b/kicker/applets/launcher/Makefile.am
new file mode 100644
index 000000000..a101c6ccf
--- /dev/null
+++ b/kicker/applets/launcher/Makefile.am
@@ -0,0 +1,27 @@
+
+INCLUDES = -I$(top_srcdir)/kicker/libkicker -I$(top_srcdir)/kicker/kicker/ui $(all_includes)
+
+kde_module_LTLIBRARIES = launcher_panelapplet.la
+
+launcher_panelapplet_la_SOURCES = quicklauncher.skel quicklauncher.cpp quickbutton.cpp quickaddappsmenu.cpp flowgridmanager.cpp popularity.cpp configdlgbase.ui prefs.kcfgc configdlg.cpp
+
+METASOURCES = AUTO
+noinst_HEADERS = quicklauncher.h quickbutton.h quickaddappsmenu.h easyvector.h quickbuttongroup.h flowgridmanager.h popularity.h configdlg.h
+
+lnkdir = $(kde_datadir)/kicker/applets
+lnk_DATA = quicklauncher.desktop
+
+EXTRA_DIST = $(lnk_DATA)
+
+launcher_panelapplet_la_LDFLAGS = -module $(KDE_RPATH) $(all_libraries) -avoid-version -no-undefined
+launcher_panelapplet_la_LIBADD = ../../kicker/core/libkicker_core.la ../../kicker/buttons/libkicker_buttons.la \
+ ../../kicker/ui/libkicker_ui.la ../../libkicker/libkickermain.la $(LIB_KIO) \
+ $(LIB_KSYCOCA) $(LIB_KDEUI) $(LIB_KUTILS)
+
+kde_kcfg_DATA = launcherapplet.kcfg
+
+messages: rc.cpp
+ $(XGETTEXT) *.cpp *.h -o $(podir)/quicklauncher.pot
+
+srcdoc:
+ kdoc -a -p -H -d $$HOME/web/src/quicklauncher quicklauncher *.h -lqt -lkdecore -lkdeui
diff --git a/kicker/applets/launcher/ToDo b/kicker/applets/launcher/ToDo
new file mode 100644
index 000000000..c7c852a36
--- /dev/null
+++ b/kicker/applets/launcher/ToDo
@@ -0,0 +1,8 @@
+TODO:
+ X * Geometry isn't always updated when panel size changes.
+ x * Remove debugging code (makes everything slow).
+ * Tooltip or other popup to alert user that buttons are locked (with a "don't show this again" checkbox).
+ * Docs.
+ * Use old quicklauncher config file if found. #77959
+ X * "Add application" context menu should add the new application at the index of the button that summoned the context menu.
+ X * Make sure to always have a little space to drop things on, even when there are no buttons.
diff --git a/kicker/applets/launcher/configdlg.cpp b/kicker/applets/launcher/configdlg.cpp
new file mode 100644
index 000000000..9950dd2a9
--- /dev/null
+++ b/kicker/applets/launcher/configdlg.cpp
@@ -0,0 +1,101 @@
+/*****************************************************************
+
+Copyright (c) 2005 Fred Schaettgen <[email protected]>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+
+#include <qcombobox.h>
+#include <klocale.h>
+#include <kdebug.h>
+
+#include "prefs.h"
+#include "configdlg.h"
+#include "configdlgbase.h"
+
+ConfigDlg::ConfigDlg(QWidget *parent, const char *name, Prefs *config,
+ int autoSize, KConfigDialog::DialogType dialogType,
+ int dialogButtons) :
+ KConfigDialog(parent, name, config, dialogType, dialogButtons),
+ m_settings(config),
+ m_autoSize(autoSize)
+{
+ m_ui = new ConfigDlgBase(this->plainPage());
+ addPage(m_ui, i18n("Configure"), "config");
+
+ m_ui->iconDim->clear();
+ m_ui->iconDim->insertItem(i18n("Automatic"));
+ for (int n=0; n<int(m_settings->iconDimChoices().size()); ++n)
+ {
+ m_ui->iconDim->insertItem(QString::number(
+ m_settings->iconDimChoices()[n]));
+ }
+ connect(m_ui->iconDim, SIGNAL(textChanged(const QString&)),
+ this, SLOT(updateButtons()));
+ updateWidgets();
+ m_oldIconDimText = m_ui->iconDim->currentText();
+ updateButtons();
+}
+
+void ConfigDlg::updateSettings()
+{
+ kdDebug() << "updateSettings" << endl;
+ KConfigDialog::updateSettings();
+ if (!hasChanged())
+ {
+ return;
+ }
+ m_oldIconDimText = m_ui->iconDim->currentText();
+ if (m_ui->iconDim->currentText() == i18n("Automatic"))
+ {
+ m_settings->setIconDim(m_autoSize);
+ }
+ else
+ {
+ m_settings->setIconDim(m_ui->iconDim->currentText().toInt());
+ }
+ settingsChangedSlot();
+}
+
+void ConfigDlg::updateWidgets()
+{
+ KConfigDialog::updateWidgets();
+ if (m_settings->iconDim() == m_autoSize)
+ {
+ m_ui->iconDim->setEditText(i18n("Automatic"));
+ }
+ else
+ {
+ m_ui->iconDim->setEditText(QString::number(m_settings->iconDim()));
+ }
+}
+
+void ConfigDlg::updateWidgetsDefault()
+{
+ KConfigDialog::updateWidgetsDefault();
+}
+
+bool ConfigDlg::hasChanged()
+{
+ return m_oldIconDimText != m_ui->iconDim->currentText() ||
+ KConfigDialog::hasChanged();
+}
+
+#include "configdlg.moc"
diff --git a/kicker/applets/launcher/configdlg.h b/kicker/applets/launcher/configdlg.h
new file mode 100644
index 000000000..1d03a9381
--- /dev/null
+++ b/kicker/applets/launcher/configdlg.h
@@ -0,0 +1,55 @@
+/*****************************************************************
+
+Copyright (c) 2005 Fred Schaettgen <[email protected]>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#ifndef CONFIG_DLG_H
+#define CONFIG_DLG_H
+
+#include <kconfigdialog.h>
+
+class ConfigDlgBase;
+class Prefs;
+
+class ConfigDlg : public KConfigDialog
+{
+ Q_OBJECT
+
+public:
+ ConfigDlg(QWidget *parent, const char *name, Prefs *config, int autoSize,
+ KConfigDialog::DialogType dialogType, int dialogButtons);
+
+protected:
+ virtual bool hasChanged();
+
+protected slots:
+ virtual void updateSettings();
+ virtual void updateWidgets();
+ virtual void updateWidgetsDefault();
+
+private:
+ ConfigDlgBase *m_ui;
+ Prefs* m_settings;
+ int m_autoSize;
+ QString m_oldIconDimText;
+};
+
+#endif
diff --git a/kicker/applets/launcher/configdlgbase.ui b/kicker/applets/launcher/configdlgbase.ui
new file mode 100644
index 000000000..bfb1bc4e6
--- /dev/null
+++ b/kicker/applets/launcher/configdlgbase.ui
@@ -0,0 +1,273 @@
+<!DOCTYPE UI><UI version="3.3" stdsetdef="1">
+<class>ConfigDlgBase</class>
+<widget class="QWidget">
+ <property name="name">
+ <cstring>ConfigDlgBase</cstring>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>371</width>
+ <height>338</height>
+ </rect>
+ </property>
+ <vbox>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <property name="margin">
+ <number>0</number>
+ </property>
+ <widget class="QCheckBox">
+ <property name="name">
+ <cstring>kcfg_DragEnabled</cstring>
+ </property>
+ <property name="text">
+ <string>Allow drag and drop</string>
+ </property>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>groupBox2</cstring>
+ </property>
+ <property name="title">
+ <string>Layout</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QCheckBox" row="1" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_ConserveSpace</cstring>
+ </property>
+ <property name="text">
+ <string>Conserve space</string>
+ </property>
+ <property name="toolTip" stdset="0">
+ <string>Do not expand icons to the size of the panel</string>
+ </property>
+ </widget>
+ <widget class="QLabel" row="0" column="0">
+ <property name="name">
+ <cstring>textLabel1_2</cstring>
+ </property>
+ <property name="text">
+ <string>Icon size:</string>
+ </property>
+ </widget>
+ <widget class="QComboBox" row="0" column="1">
+ <property name="name">
+ <cstring>iconDim</cstring>
+ </property>
+ <property name="editable">
+ <bool>true</bool>
+ </property>
+ </widget>
+ <spacer row="0" column="2">
+ <property name="name">
+ <cstring>spacer4_2</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>332</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </grid>
+ </widget>
+ <widget class="QGroupBox">
+ <property name="name">
+ <cstring>autoAdjustGroup</cstring>
+ </property>
+ <property name="title">
+ <string>Most Popular Applications</string>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QLayoutWidget" row="3" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>layout1</cstring>
+ </property>
+ <grid>
+ <property name="name">
+ <cstring>unnamed</cstring>
+ </property>
+ <widget class="QSlider" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_HistoryHorizon</cstring>
+ </property>
+ <property name="maxValue">
+ <number>100</number>
+ </property>
+ <property name="lineStep">
+ <number>0</number>
+ </property>
+ <property name="value">
+ <number>10</number>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ </widget>
+ <spacer row="1" column="1">
+ <property name="name">
+ <cstring>spacer7</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>140</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel1</cstring>
+ </property>
+ <property name="text">
+ <string>Short Term</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="2">
+ <property name="name">
+ <cstring>textLabel2</cstring>
+ </property>
+ <property name="text">
+ <string>Long Term</string>
+ </property>
+ <property name="alignment">
+ <set>AlignCenter</set>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ <widget class="QLabel" row="2" column="0">
+ <property name="name">
+ <cstring>textLabel3</cstring>
+ </property>
+ <property name="text">
+ <string>Maximum number of applications:</string>
+ </property>
+ </widget>
+ <widget class="KIntSpinBox" row="1" column="1">
+ <property name="name">
+ <cstring>kcfg_AutoAdjustMinItems</cstring>
+ </property>
+ </widget>
+ <spacer row="2" column="2">
+ <property name="name">
+ <cstring>spacer4</cstring>
+ </property>
+ <property name="orientation">
+ <enum>Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>Expanding</enum>
+ </property>
+ <property name="sizeHint">
+ <size>
+ <width>50</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ <widget class="KIntSpinBox" row="2" column="1">
+ <property name="name">
+ <cstring>kcfg_AutoAdjustMaxItems</cstring>
+ </property>
+ </widget>
+ <widget class="QLabel" row="1" column="0">
+ <property name="name">
+ <cstring>textLabel3_2</cstring>
+ </property>
+ <property name="text">
+ <string>Minimum number of applications:</string>
+ </property>
+ </widget>
+ <widget class="QCheckBox" row="0" column="0" rowspan="1" colspan="3">
+ <property name="name">
+ <cstring>kcfg_AutoAdjustEnabled</cstring>
+ </property>
+ <property name="text">
+ <string>Add/remove applications based on their popularity</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </grid>
+ </widget>
+ </vbox>
+</widget>
+<customwidgets>
+</customwidgets>
+<connections>
+ <connection>
+ <sender>kcfg_AutoAdjustEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_AutoAdjustMinItems</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_AutoAdjustEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_AutoAdjustMaxItems</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_AutoAdjustEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>kcfg_HistoryHorizon</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_AutoAdjustEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel1</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_AutoAdjustEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel2</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_AutoAdjustEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel3</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+ <connection>
+ <sender>kcfg_AutoAdjustEnabled</sender>
+ <signal>toggled(bool)</signal>
+ <receiver>textLabel3_2</receiver>
+ <slot>setEnabled(bool)</slot>
+ </connection>
+</connections>
+<layoutdefaults spacing="6" margin="11"/>
+<includehints>
+ <includehint>knuminput.h</includehint>
+ <includehint>knuminput.h</includehint>
+</includehints>
+</UI>
diff --git a/kicker/applets/launcher/easyvector.h b/kicker/applets/launcher/easyvector.h
new file mode 100644
index 000000000..cad9a2c86
--- /dev/null
+++ b/kicker/applets/launcher/easyvector.h
@@ -0,0 +1,147 @@
+/* Copyright 2004, Daniel Woods Bullok <[email protected]>
+ distributed under the terms of the
+ GNU GENERAL PUBLIC LICENSE Version 2 -
+ See the file kdebase/COPYING for details
+*/
+
+#ifndef __easyvector_h__
+#define __easyvector_h__
+#include <vector>
+#include <algorithm>
+#include <assert.h>
+
+template < class VALUE >
+class __Valtype {
+public:
+ typedef const VALUE& CVALUE;
+};
+
+
+template < class VALUE >
+class __Valtype< VALUE* > {
+public:
+ typedef const VALUE* CVALUE;
+};
+
+
+template <class VALUE, bool CHECKINDEX=true>
+class EasyVector: public std::vector< VALUE > {
+public:
+ typedef int Index;
+ typedef std::vector< Index > Indices;
+ typedef typename __Valtype< VALUE >::CVALUE CVALUE;
+
+ static const Index NotFound=-2;
+ static const Index Append=-1;
+
+ template < class PTYPE, class PROP_FUNC >
+ Index findProperty(const PTYPE &property,
+ PROP_FUNC prop_func) const;
+ Index findValue(CVALUE value) const;
+
+ Index lastIndex() const {return this->size()-1;}
+
+ void eraseAt(Index index);
+
+ VALUE takeFrom(Index index);
+
+ void insertAt(Index index,const VALUE &value);
+ void insertAt(Index index,const EasyVector &values);
+
+ bool isValidIndex(Index index) const;
+ bool isValidInsertIndex(Index index) const;
+ virtual ~EasyVector(){};
+
+
+protected:
+ void _checkInsertIndex(Index index) const;
+ void _checkIndex(Index index) const;
+ Index _convertInsertIndex(Index index) const;
+};
+
+
+template < class VALUE, bool CHECKINDEX >
+template < class PTYPE, class PROP_FUNC >
+typename EasyVector< VALUE, CHECKINDEX >::Index
+ EasyVector< VALUE, CHECKINDEX >::findProperty(const PTYPE &property,
+ PROP_FUNC prop_func) const
+{ typename EasyVector< VALUE, CHECKINDEX >::const_iterator i;
+ for (i=this->begin();i!=this->end();++i) {
+ if (prop_func(*i)==property)
+ return i-this->begin();
+ }
+ return NotFound;
+}
+
+
+template < class VALUE, bool CHECKINDEX >
+typename EasyVector< VALUE, CHECKINDEX >::Index
+ EasyVector< VALUE, CHECKINDEX >::findValue(CVALUE value) const
+{ typename EasyVector< VALUE, CHECKINDEX >::const_iterator i;
+ i=std::find(this->begin(),this->end(),value);
+ if (i==this->end()) return NotFound;
+ return i-this->begin();
+}
+
+
+template < class VALUE, bool CHECKINDEX >
+void EasyVector< VALUE, CHECKINDEX >::eraseAt(Index index)
+{ _checkIndex(index);
+ erase(this->begin()+index);
+}
+
+
+template < class VALUE, bool CHECKINDEX >
+VALUE EasyVector< VALUE, CHECKINDEX >::takeFrom(Index index)
+{ _checkIndex(index);
+ VALUE result=(*this)[index];
+ eraseAt(index);
+ return result;
+}
+
+
+template < class VALUE, bool CHECKINDEX >
+void EasyVector< VALUE, CHECKINDEX >::insertAt(EasyVector< VALUE, CHECKINDEX >::Index index,const VALUE &value)
+{ index=_convertInsertIndex(index);
+ _checkInsertIndex(index);
+ if (index==int(this->size())) {
+ this->push_back(value);
+ return;
+ }
+ insert(this->begin()+index,value);
+}
+
+
+template < class VALUE, bool CHECKINDEX >
+void EasyVector< VALUE, CHECKINDEX >::insertAt(EasyVector< VALUE, CHECKINDEX >::Index index,const EasyVector< VALUE, CHECKINDEX > &v)
+{ index=_convertInsertIndex(index);
+ _checkInsertIndex(index);
+ insert(this->begin()+index,v.begin(),v.end());
+}
+
+
+template < class VALUE, bool CHECKINDEX >
+bool EasyVector< VALUE, CHECKINDEX >::isValidIndex(EasyVector< VALUE, CHECKINDEX >::Index index) const
+{ return(0<=index && index<int(this->size()));}
+
+template < class VALUE, bool CHECKINDEX >
+bool EasyVector< VALUE, CHECKINDEX >::isValidInsertIndex(EasyVector< VALUE, CHECKINDEX >::Index index) const
+{ return(index==Append)||(0<=index && index<=int(this->size()));}
+
+template < class VALUE, bool CHECKINDEX >
+inline typename EasyVector< VALUE, CHECKINDEX >::Index EasyVector< VALUE, CHECKINDEX >::_convertInsertIndex(Index index) const
+{ if (index==Append) return this->size();
+ return index;
+}
+
+template < class VALUE, bool CHECKINDEX >
+void EasyVector< VALUE, CHECKINDEX >::_checkInsertIndex(Index index) const
+{ if (CHECKINDEX) assert (isValidInsertIndex(index));}
+
+template < class VALUE, bool CHECKINDEX >
+void EasyVector< VALUE, CHECKINDEX >::_checkIndex(Index index) const
+{ if (CHECKINDEX) assert (isValidIndex(index));}
+
+
+#endif
+
diff --git a/kicker/applets/launcher/flowgridmanager.cpp b/kicker/applets/launcher/flowgridmanager.cpp
new file mode 100644
index 000000000..b5715097b
--- /dev/null
+++ b/kicker/applets/launcher/flowgridmanager.cpp
@@ -0,0 +1,316 @@
+/* Copyright 2004, Daniel Woods Bullok <[email protected]>
+ distributed under the terms of the
+ GNU GENERAL PUBLIC LICENSE Version 2 -
+ See the file kdebase/COPYING for details
+*/
+
+#include "flowgridmanager.h"
+#include <kdebug.h>
+#ifdef DEBUG
+ #define DEBUGSTR kdDebug()
+#else
+ #define DEBUGSTR kndDebug()
+#endif
+
+
+FlowGridManager::FlowGridManager(QSize p_item_size,
+ QSize p_space_size,
+ QSize p_border_size,
+ QSize p_frame_size,
+ Qt::Orientation orient,
+ int num_items,
+ Slack slack_x,Slack slack_y)
+{
+ _pItemSize=p_item_size;
+ _pSpaceSize=p_space_size;
+ _pBorderSize=p_border_size;
+ _pFrameSize=p_frame_size;
+ _orientation=orient;
+ _numItems=num_items;
+ _slackX=slack_x;
+ _slackY=slack_y;
+ _conserveSpace=false;
+
+ _dirty=true;
+ _valid=false;
+}
+
+// set members.
+// These all set the _dirty flag if the new value is different.
+void FlowGridManager::setNumItems(int num_items)
+{ if (_numItems==num_items)
+ return;
+ _numItems=num_items; _dirty=true;
+}
+void FlowGridManager::setItemSize(QSize p_item_size)
+{ if (_pItemSize==p_item_size)
+ return;
+ _pItemSize=p_item_size; _dirty=true;
+}
+
+void FlowGridManager::setSpaceSize(QSize p_space_size)
+{ if (_pSpaceSize==p_space_size)
+ return;
+ _pSpaceSize=p_space_size; _dirty=true;
+}
+
+void FlowGridManager::setBorderSize(QSize p_border_size)
+{ if (_pBorderSize==p_border_size)
+ return;
+ _pBorderSize=p_border_size; _dirty=true;
+}
+
+void FlowGridManager::setFrameSize(QSize p_frame_size)
+{ if (_pFrameSize==p_frame_size)
+ return;
+ _pFrameSize=p_frame_size;
+ if (_pFrameSize.width()<=0) {
+ _orientation=Qt::Vertical;
+ }
+ if (_pFrameSize.height()<=0) {
+ _orientation=Qt::Horizontal;
+ }
+ _dirty=true;
+}
+
+void FlowGridManager::setOrientation(Qt::Orientation orient)
+{ if (orient==_orientation)
+ return;
+ _orientation=orient; _dirty=true;
+}
+
+void FlowGridManager::setSlack(Slack slack_x, Slack slack_y)
+{ if (slack_x==_slackX && slack_y==_slackY) return;
+ _slackX=slack_x; _slackY=slack_y; _dirty=true;}
+
+
+void FlowGridManager::setConserveSpace(bool conserve)
+{ if (_conserveSpace==conserve)
+ return;
+ _conserveSpace=conserve; _dirty=true;
+}
+
+
+
+// get members
+QSize FlowGridManager::itemSize() const
+{ _checkReconfigure(); return _itemSize;}
+
+QSize FlowGridManager::spaceSize() const
+{ _checkReconfigure(); return _spaceSize;}
+
+QSize FlowGridManager::borderSize() const
+{ _checkReconfigure(); return _borderSize;}
+
+QSize FlowGridManager::gridDim() const
+{ _checkReconfigure(); return _gridDim;}
+
+QSize FlowGridManager::gridSpacing() const
+{ _checkReconfigure(); return _gridSpacing;}
+
+QSize FlowGridManager::frameSize() const
+{ _checkReconfigure(); return _frameSize;}
+
+QPoint FlowGridManager::origin() const
+{ _checkReconfigure(); return _origin;}
+
+Qt::Orientation FlowGridManager::orientation() const
+{ _checkReconfigure(); return _orientation;}
+
+/*Slack FlowGridManager::slackX() const
+{ return _slackY;}
+
+Slack FlowGridManager::slackY() const
+{ return _slackY;}
+*/
+
+bool FlowGridManager::conserveSpace() const
+{ return _conserveSpace; }
+
+
+bool FlowGridManager::isValid() const
+{ _checkReconfigure(); return _valid;}
+
+QPoint FlowGridManager::posAtCell(int x,int y) const
+{ _checkReconfigure();
+ return _origin+QPoint(_gridSpacing.width()*x,_gridSpacing.height()*y);
+}
+
+QPoint FlowGridManager::pos(int i) const
+{ return posAtCell(cell(i).x(),cell(i).y());
+}
+
+QPoint FlowGridManager::cell(int index) const
+{ _checkReconfigure();
+ //assert((index>=0) && (index<_gridDim.width()*_gridDim.height()));
+ int x=index % _gridDim.width(),
+ y=index / _gridDim.width();
+ return QPoint(x,y);
+}
+
+
+
+
+// return height if orientation is Horizontal
+// return width if orientation is Vertical
+int FlowGridManager::_getHH(QSize size) const
+{ if (_orientation==Qt::Horizontal)
+ return size.height();
+ return size.width();
+}
+
+// return height if orientation is Vertical
+// return width if orientation is Horizontal
+int FlowGridManager::_getWH(QSize size) const
+{ if (_orientation==Qt::Horizontal)
+ return size.width();
+ return size.height();
+}
+
+// swap horizontal and vertical if orientation is Vertical, otherwise return arg
+QSize FlowGridManager::_swapHV(QSize hv) const
+{ if (_orientation==Qt::Horizontal)
+ return hv;
+ QSize temp=hv;
+ temp.transpose();
+ return temp;
+}
+
+
+// return the amount of slack when:
+// nitems = # of items
+// length = total length of space where items will be placed
+// item, space, border = length of respective entities
+int FlowGridManager::_slack(int nitems,int length,int item,int space,int border) const
+{ return length-(2*border)-(nitems-1)*space-nitems*item;}
+
+
+void FlowGridManager::_clear() const
+{
+ _borderSize=QSize(0,0);
+ _spaceSize=QSize(0,0);
+ _itemSize=QSize(0,0);
+ _gridDim=QSize(0,0);
+ _gridSpacing=QSize(0,0);
+ _origin=QPoint(0,0);
+ _frameSize=QSize(0,0);
+
+ _dirty=false;
+ _valid=false;
+}
+
+
+int FlowGridManager::indexNearest(QPoint p) const
+{ if (!isValid()) return -1;
+ QPoint c=(p-_origin)-QPoint(_spaceSize.width(),_spaceSize.height())/2;
+ int x=c.x()/_gridSpacing.width(),
+ y=c.y()/_gridSpacing.height();
+ int i= x+y*_gridDim.width();
+ if (i>_numItems) return -1;
+ return i;
+}
+
+
+
+// Redistribute the boxes
+void FlowGridManager::_reconfigure() const
+{ if ((!_pFrameSize.isValid()) ||
+ (!_pItemSize.isValid()) ||
+ _numItems==0 ) {
+ _clear();
+ return;
+ }
+ int height=_getHH(_pFrameSize),
+ pItemHeight=_getHH(_pItemSize),
+ pSpaceHeight=_getHH(_pSpaceSize),
+ pBorderHeight=_getHH(_pBorderSize),
+ spanlen=(height-2*pBorderHeight+pSpaceHeight)/(pItemHeight+pSpaceHeight);
+ int slack,iSlack;
+
+ if (spanlen==0) {
+ _dirty=false;
+ _valid=false;
+ return;
+ }
+ // figure out the number of spans required for all items
+ int numspans=_numItems/spanlen;
+ if (numspans*spanlen<_numItems) {
+ numspans++;
+ }
+
+ slack=_slack(spanlen,height,pItemHeight,pSpaceHeight,pBorderHeight); // total slack
+ iSlack=slack/spanlen; // slack per item
+ // Items pick up extra slack
+ if (_slackX==ItemSlack) pItemHeight+=iSlack;
+ slack=_slack(spanlen,height,pItemHeight,pSpaceHeight,pBorderHeight);
+
+ // space picks up extra slack
+ if (spanlen>1) {
+ iSlack=slack/(spanlen+1);
+ pSpaceHeight+=iSlack;
+ }
+
+ slack=_slack(spanlen,height,pItemHeight,pSpaceHeight,pBorderHeight);
+ iSlack=slack/2;
+ pBorderHeight+=iSlack;
+ if (_conserveSpace) {
+ _itemSize=_swapHV(QSize(_getWH(_pItemSize),pItemHeight));
+ _spaceSize=_swapHV(QSize(_getWH(_pSpaceSize),pSpaceHeight));
+ _borderSize=_swapHV(QSize(_getWH(_pBorderSize),pBorderHeight));
+ }
+ else {
+ _itemSize=_swapHV(QSize(pItemHeight,pItemHeight));
+ _spaceSize=_swapHV(QSize(pSpaceHeight,pSpaceHeight));
+ _borderSize=_swapHV(QSize(pBorderHeight,pBorderHeight));
+ }
+ _gridDim=_swapHV(QSize(numspans,spanlen));
+
+ _gridSpacing=_itemSize+_spaceSize;
+ _origin=QPoint(_borderSize.width(),_borderSize.height());
+ _frameSize=2*_borderSize+QSize(_gridDim.width()*_gridSpacing.width()-_spaceSize.width(),
+ _gridDim.height()*_gridSpacing.height()-_spaceSize.height());
+
+ _dirty=false;
+ _valid=true;
+}
+
+
+void FlowGridManager::dump()
+{
+ DEBUGSTR<<endl<<flush;
+
+ DEBUGSTR<<"_pItemSize=("<<_pItemSize.width()<<","<<_pItemSize.height()<<")"<<endl<<flush;
+ DEBUGSTR<<"_pSpaceSize=("<<_pSpaceSize.width()<<","<<_pSpaceSize.height()<<")"<<endl<<flush;
+ DEBUGSTR<<"_pBorderSize=("<<_pBorderSize.width()<<","<<_pBorderSize.height()<<")"<<endl<<flush;
+ DEBUGSTR<<"_pFrameSize=("<<_pFrameSize.width()<<","<<_pFrameSize.height()<<")"<<endl<<flush;
+ DEBUGSTR<<"_borderSize=("<<_borderSize.width()<<","<<_borderSize.height()<<")"<<endl<<flush;
+ DEBUGSTR<<"_spaceSize=("<<_spaceSize.width()<<","<<_spaceSize.height()<<")"<<endl<<flush;
+ DEBUGSTR<<"_itemSize=("<<_itemSize.width()<<","<<_itemSize.height()<<")"<<endl<<flush;
+ DEBUGSTR<<"_gridDim=("<<_gridDim.width()<<","<<_gridDim.height()<<")"<<endl<<flush;
+ DEBUGSTR<<"_gridSpacing=("<<_gridSpacing.width()<<","<<_gridSpacing.height()<<")"<<endl<<flush;
+ DEBUGSTR<<"_origin=("<<_origin.x()<<","<<_origin.y()<<")"<<endl<<flush;
+ DEBUGSTR<<"_frameSize=("<<_frameSize.width()<<","<<_frameSize.height()<<")"<<endl<<flush;
+ DEBUGSTR<<"_conserveSpace="<<_conserveSpace<<endl<<flush;
+
+ DEBUGSTR<<"_orientation="<<_orientation<<endl<<flush;
+ DEBUGSTR<<"_numItems="<<_numItems<<endl<<flush;
+ DEBUGSTR<<"_slackX="<<_slackX<<endl<<flush;
+ DEBUGSTR<<"_slackY="<<_slackY<<endl<<flush;
+ DEBUGSTR<<"_dirty="<<_dirty<<endl<<flush;
+ DEBUGSTR<<"_valid="<<_valid<<endl<<flush;
+ DEBUGSTR<<endl<<flush;
+}
+
+
+
+bool operator== ( const FlowGridManager & csg1, const FlowGridManager & csg2 )
+{
+ return csg1.gridDim()==csg2.gridDim() &&
+ csg1.origin()==csg2.origin() &&
+ csg1.gridSpacing()==csg2.gridSpacing() &&
+ csg1.frameSize()==csg2.frameSize();
+}
+
+
+
+
diff --git a/kicker/applets/launcher/flowgridmanager.h b/kicker/applets/launcher/flowgridmanager.h
new file mode 100644
index 000000000..9d100c74f
--- /dev/null
+++ b/kicker/applets/launcher/flowgridmanager.h
@@ -0,0 +1,99 @@
+/* Copyright 2004, Daniel Woods Bullok <[email protected]>
+ distributed under the terms of the
+ GNU GENERAL PUBLIC LICENSE Version 2 -
+ See the file kdebase/COPYING for details
+*/
+
+#ifndef __const_space_grid_h__
+#define __const_space_grid_h__
+
+#include <qnamespace.h>
+#include <qpoint.h>
+#include <qsize.h>
+
+
+class FlowGridManager {
+// Determine if two FlowGridManager objs have the same layout. They may or
+// may not have the same input parameters, but the resulting layout is identical.
+ friend bool operator== ( const FlowGridManager & gp1, const FlowGridManager & gp2 );
+
+public:
+ typedef enum {
+ ItemSlack,SpaceSlack,BorderSlack,NoSlack
+ } Slack;
+
+ FlowGridManager(QSize p_item_size=QSize(0,0),
+ QSize p_space_size=QSize(0,0),
+ QSize p_border_size=QSize(0,0),
+ QSize frame_size=QSize(0,0),
+ Qt::Orientation orient=Qt::Horizontal,
+ int num_items=0,
+ Slack slack_x=ItemSlack,
+ Slack slack_y=ItemSlack);
+
+
+ void setNumItems(int num_items);
+ void setItemSize(QSize item_size);
+ void setSpaceSize(QSize space_size);
+ void setBorderSize(QSize border_size);
+ void setOrientation(Qt::Orientation orient);
+ void setFrameSize(QSize frame_size);
+ void setSlack(Slack slack_x, Slack slack_y);
+ void setConserveSpace(bool conserve);
+
+
+ QSize itemSize() const;
+ QSize spaceSize() const;
+ QSize borderSize() const;
+ QSize gridDim() const;
+ QSize gridSpacing() const;
+ QSize frameSize() const;
+ QPoint origin() const;
+ Qt::Orientation orientation() const;
+ bool conserveSpace() const;
+
+// Slack slackX() const;
+// Slack slackY() const;
+
+ QPoint posAtCell(int x,int y) const;
+ QPoint pos(int i) const;
+ QPoint cell(int index) const;
+ bool isValid() const;
+ int indexNearest(QPoint p) const;
+
+ void dump();
+protected:
+ int _getHH(QSize size) const;
+ int _getWH(QSize size) const;
+ QSize _swapHV(QSize hv) const;
+ inline void _checkReconfigure() const;
+ int _slack(int nitems,int length,int item,int space,int border) const;
+ void _reconfigure() const;
+ void _clear() const;
+
+protected:
+ // user-definable data
+ QSize _pItemSize,_pSpaceSize,_pBorderSize,_pFrameSize;
+ Slack _slackX, _slackY;
+ bool _conserveSpace;
+ Qt::Orientation _orientation;
+ int _numItems;
+
+ // results
+ mutable QSize _itemSize, _spaceSize, _borderSize, _gridDim, _gridSpacing, _frameSize;
+ mutable QPoint _origin;
+
+ // status
+ mutable bool _dirty, _valid;
+
+};
+
+
+// reconfigure the grid if necessary.
+inline void FlowGridManager::_checkReconfigure() const
+{ if (!_dirty) return;
+ _reconfigure();
+}
+
+#endif
+
diff --git a/kicker/applets/launcher/launcherapplet.kcfg b/kicker/applets/launcher/launcherapplet.kcfg
new file mode 100644
index 000000000..3433bf437
--- /dev/null
+++ b/kicker/applets/launcher/launcherapplet.kcfg
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<kcfg xmlns="http://www.kde.org/standards/kcfg/1.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://www.kde.org/standards/kcfg/1.0
+ http://www.kde.org/standards/kcfg/1.0/kcfg.xsd" >
+ <kcfgfile arg="true"/>
+ <group name="General">
+ <entry name="ConserveSpace" type="Bool">
+ <label>Conserve Space</label>
+ <default>true</default>
+ </entry>
+ <entry name="DragEnabled" type="Bool">
+ <label>Drag Enabled</label>
+ <default>true</default>
+ </entry>
+ <entry name="IconDim" type="Int">
+ <label>Icon Size</label>
+ <default>0</default>
+ </entry>
+ <entry name="IconDimChoices" type="IntList">
+ <label>Offered Icon Sizes</label>
+ <default>16,20,24,28,32,48,64</default>
+ </entry>
+ <entry name="Buttons" type="StringList">
+ <label>Buttons</label>
+ <default>kde-Home.desktop,kde-konsole.desktop,kde-KControl.desktop,kde-Help.desktop,kde-kwrite.desktop</default>
+ </entry>
+ <entry name="VolatileButtons" type="StringList">
+ <label>Volatile Buttons</label>
+ <whatsthis>Buttons that can be removed dynamically if they become unpopular</whatsthis>
+ <default></default>
+ </entry>
+ <entry name="ShowVolatileButtonIndicator" type="Bool">
+ <label>Show frame for volatile buttons</label>
+ <default>true</default>
+ </entry>
+ <entry name="AutoAdjustEnabled" type="Bool">
+ <label>Auto Adjust Enabled</label>
+ <default>false</default>
+ </entry>
+ <entry name="AutoAdjustMinItems" type="Int">
+ <label>Minimum Number of Items</label>
+ <min>0</min>
+ <default>3</default>
+ </entry>
+ <entry name="AutoAdjustMaxItems" type="Int">
+ <label>Maximum Number of Items</label>
+ <min>0</min>
+ <default>6</default>
+ </entry>
+ <entry name="HistoryHorizon" type="Int">
+ <label>History Weight</label>
+ <min>0</min>
+ <max>100</max>
+ <default>70</default>
+ </entry>
+ </group>
+ <group name="PopularityData">
+ <entry name="ServiceCacheSize" key="ServiceCacheSize" type="Int">
+ <label>Service Cache Size</label>
+ <whatsthis>Number of services to remember</whatsthis>
+ <default>500</default>
+ </entry>
+ <entry name="ServiceNames" key="ServiceNames" type="StringList">
+ <label>Service Names</label>
+ <whatsthis>Name of known services</whatsthis>
+ </entry>
+ <entry name="ServiceInspos" key="ServiceInspos" type="IntList">
+ <label>Service Insertion Positions</label>
+ <whatsthis>Position where services are inserted when they regain popularity</whatsthis>
+ </entry>
+ <entry name="ServiceHistories" key="ServiceHistories" type="StringList">
+ <label>Service History Data</label>
+ <whatsthis>History Data used to determine the popularity of a service</whatsthis>
+ </entry>
+ </group>
+</kcfg>
diff --git a/kicker/applets/launcher/popularity.cpp b/kicker/applets/launcher/popularity.cpp
new file mode 100644
index 000000000..3bfcdd872
--- /dev/null
+++ b/kicker/applets/launcher/popularity.cpp
@@ -0,0 +1,424 @@
+/*****************************************************************
+
+Copyright (c) 2005 Fred Schaettgen <[email protected]>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#include "popularity.h"
+#include "prefs.h"
+#include <assert.h>
+#include <algorithm>
+#include <iterator>
+#include <kconfig.h>
+#include <kdebug.h>
+#include <list>
+#include <set>
+#include <map>
+#include <cmath>
+#include <vector>
+
+using namespace std;
+
+class PopularityStatisticsImpl
+{
+public:
+ struct SingleFalloffHistory
+ {
+ public:
+ // fallof is a number between 0 and 1. The popularity of
+ // each service is multiplied with the falloff value
+ // every time a service is used. Only the used service
+ // gets also 1-falloff added to its popularity.
+ double falloff;
+ // popularity values for each service
+ map<QString, double> vals;
+ // accumulated popularity of the unknown programs
+ // started before the statistic started
+ double iniVal;
+ };
+
+ struct Popularity
+ {
+ QString service;
+ double popularity;
+ bool operator<(const Popularity& p) const
+ {
+ return popularity > p.popularity;
+ }
+ };
+
+ PopularityStatisticsImpl();
+ void normalizeHistory(SingleFalloffHistory& h);
+ void updateServiceRanks();
+
+ vector<SingleFalloffHistory> m_stats;
+ vector<Popularity> m_servicesByPopularity;
+ map<QString, int> m_serviceRanks;
+ double m_historyHorizon;
+};
+
+// ---- Public methods ----
+
+PopularityStatistics::PopularityStatistics() :
+ d(new PopularityStatisticsImpl())
+{
+}
+
+PopularityStatistics::~PopularityStatistics()
+{
+ delete d;
+}
+
+void PopularityStatistics::useService(const QString& service)
+{
+ vector<PopularityStatisticsImpl::SingleFalloffHistory>::iterator
+ it(d->m_stats.begin()), end(d->m_stats.end());
+ for (; it != end; ++it)
+ {
+ map<QString, double>::iterator valIt;
+ bool found(false);
+ for (valIt = it->vals.begin(); valIt != it->vals.end(); ++valIt)
+ {
+ valIt->second = valIt->second * it->falloff;
+ if (valIt->first == service)
+ {
+ found = true;
+ valIt->second += 1-it->falloff;
+ }
+ }
+ it->iniVal = it->iniVal * it->falloff;
+ if (found == false)
+ {
+ it->vals[service] = 1-it->falloff;
+ }
+ d->normalizeHistory(*it);
+ }
+ d->updateServiceRanks();
+}
+
+void PopularityStatistics::moveToTop(const QStringList& newTopServiceList)
+{
+ vector<PopularityStatisticsImpl::SingleFalloffHistory>::iterator
+ histIt(d->m_stats.begin()), histEnd(d->m_stats.end());
+ for (; histIt != histEnd; ++histIt)
+ {
+ set<QString> newTopServices;
+ for (uint n=0; n<newTopServiceList.size(); ++n)
+ newTopServices.insert(newTopServiceList[n]);
+
+ // Sort by popularity
+ vector<PopularityStatisticsImpl::Popularity> ranking;
+ map<QString, double>::iterator valIt;
+ for (valIt = histIt->vals.begin(); valIt != histIt->vals.end(); ++valIt)
+ {
+ PopularityStatisticsImpl::Popularity pop;
+ pop.service = valIt->first;
+ pop.popularity = valIt->second;
+ ranking.push_back(pop);
+ }
+ stable_sort(ranking.begin(), ranking.end());
+
+ // Get the new positions of each service in the ranking.
+ // We don't touch the popularity values in the ranking.
+ list<QString> topServiceList, bottomServiceList;
+ vector<PopularityStatisticsImpl::Popularity>:: iterator rankIt;
+ for (rankIt = ranking.begin(); rankIt != ranking.end(); ++rankIt)
+ {
+ if (newTopServices.find(rankIt->service) != newTopServices.end())
+ {
+ topServiceList.push_back(rankIt->service);
+ //kdDebug() << "top service: " << valIt->first << endl;
+ newTopServices.erase(rankIt->service);
+ }
+ else
+ {
+ //kdDebug() << "bottom service: " << valIt->first << endl;
+ bottomServiceList.push_back(rankIt->service);
+ }
+ }
+ // Append remaining new services to the topServices list
+ while (newTopServices.size() > 0)
+ {
+ topServiceList.push_back(*newTopServices.begin());
+ newTopServices.erase(newTopServices.begin());
+ }
+
+ list<QString> newServiceList;
+ copy(topServiceList.begin(), topServiceList.end(),
+ back_insert_iterator<list<QString> >(newServiceList));
+ copy(bottomServiceList.begin(), bottomServiceList.end(),
+ back_insert_iterator<list<QString> >(newServiceList));
+
+ // Merge the old list of service popularities
+ // with the new ordering of the services
+ histIt->vals.clear();
+ list<QString>::iterator servIt;
+ uint serviceIndex = 0;
+ //kdDebug() << endl;
+
+ for (servIt = newServiceList.begin(); servIt != newServiceList.end();
+ ++servIt)
+ {
+ if (serviceIndex < ranking.size())
+ {
+ histIt->vals[*servIt] = ranking[serviceIndex].popularity;
+ //kdDebug() << "->Re-Added service " <<
+ //ranking[serviceIndex].popularity
+ // << " " << *servIt << endl;
+ //kdDebug() << "...was replaced by " << *servIt << endl;
+ }
+ else
+ {
+ //kdDebug() << "Service " << *servIt << endl;
+ //kdDebug() << "...was set to popularity=0" << endl;
+ histIt->vals[*servIt] = 0.00001;
+ }
+ // Make sure that the topServices are actually bigger than
+ // the bottomServices and not just bigger or equal
+ // and also that no services have popularity==0
+ if (serviceIndex >= topServiceList.size())
+ {
+ histIt->vals[*servIt] *= histIt->falloff;
+ }
+ ++serviceIndex;
+ }
+ d->normalizeHistory(*histIt);
+ }
+ d->updateServiceRanks();
+}
+
+/*v
+Old version - moves everything else one position up
+and 'service' to the bottom
+void PopularityStatistics::moveToBottom(const QString& service)
+{
+ // Moves a service to the bottom of the ranking
+ // by moving everything else to the top
+ d->updateServiceRanks();
+ QStringList allButOneServices;
+ vector<PopularityStatisticsImpl::Popularity>::iterator
+ it(d->m_servicesByPopularity.begin()),
+ end(d->m_servicesByPopularity.end());
+ for (; it != end; ++it)
+ {
+ if (it->service != service)
+ allButOneServices << it->service;
+ }
+ moveToTop(allButOneServices);
+}*/
+
+void PopularityStatistics::moveToBottom(const QString& service)
+{
+ vector<PopularityStatisticsImpl::SingleFalloffHistory>::iterator
+ it(d->m_stats.begin()), end(d->m_stats.end());
+ for (; it != end; ++it)
+ {
+ it->iniVal += it->vals[service];
+ it->vals[service] = 0;
+ d->normalizeHistory(*it);
+ }
+ d->updateServiceRanks();
+}
+
+QString PopularityStatistics::serviceByRank(int n) const
+{
+ if (n >= 0 && n < int(d->m_servicesByPopularity.size()))
+ return d->m_servicesByPopularity[n].service;
+ else
+ return QString();
+}
+
+double PopularityStatistics::popularityByRank(int n) const
+{
+ if (n >= 0 && n < int(d->m_servicesByPopularity.size()))
+ return d->m_servicesByPopularity[n].popularity;
+ else
+ return 0.0;
+}
+
+int PopularityStatistics::rankByService(const QString service)
+{
+ if (d->m_serviceRanks.find(service) != d->m_serviceRanks.end())
+ {
+ return d->m_serviceRanks[service];
+ }
+ return -1;
+}
+
+void PopularityStatistics::writeConfig(Prefs* prefs) const
+{
+ QStringList serviceNames, serviceHistories;
+ int limit = prefs->serviceCacheSize();
+ //kdDebug() << "popularityData: writeConfig" << endl;
+ for (int n=0; n<int(d->m_servicesByPopularity.size()) && n<limit; ++n)
+ {
+ PopularityStatisticsImpl::Popularity pop = d->m_servicesByPopularity[n];
+ QStringList historyData;
+ for (int i=0; i<int(d->m_stats.size()); ++i)
+ {
+ historyData << QString::number(d->m_stats[i].vals[pop.service]);
+ }
+ serviceNames << pop.service;
+ serviceHistories << historyData.join("/");
+ //kdDebug() << "popularityData: writeConfig -- " << pop.service << endl;
+ }
+ prefs->setServiceNames(serviceNames);
+ prefs->setServiceHistories(serviceHistories);
+}
+
+void PopularityStatistics::readConfig(Prefs* prefs)
+{
+ int n = 0;
+ QStringList serviceNames = prefs->serviceNames();
+ QStringList histories = prefs->serviceHistories();
+ for (n = std::min(serviceNames.size(), histories.size())-1; n>=0; --n)
+ {
+ QString serviceName = serviceNames[n];
+ QStringList serviceHistory =
+ QStringList::split("/", histories[n]);
+ for (int i=min(serviceHistory.size(), d->m_stats.size())-1; i>=0; --i)
+ {
+ d->m_stats[i].vals[serviceName] = serviceHistory[i].toDouble();
+ }
+ }
+
+ for (int i=0; i<int(d->m_stats.size()); ++i)
+ {
+ map<QString, double>::iterator valIt;
+ double valSum = 0;
+ for (valIt = d->m_stats[i].vals.begin();
+ valIt != d->m_stats[i].vals.end(); ++valIt)
+ {
+ if (valIt->second < 0) valIt->second = 0;
+ valSum += valIt->second;
+ }
+ // Scale down values if their sum is bigger than 1
+ // because of rounding errors or a corrupted config file
+ if (valSum > 1)
+ {
+ for (valIt = d->m_stats[i].vals.begin();
+ valIt != d->m_stats[i].vals.end(); ++valIt)
+ {
+ valIt->second = valIt->second / valSum;
+ }
+ }
+ d->m_stats[i].iniVal = 1-valSum;
+ }
+ d->updateServiceRanks();
+}
+
+void PopularityStatistics::setHistoryHorizon(double h)
+{
+ d->m_historyHorizon = std::max(std::min(h, 1.0), 0.0);
+ d->updateServiceRanks();
+}
+
+double PopularityStatistics::historyHorizon()
+{
+ return d->m_historyHorizon;
+}
+
+
+// ---- Implementation methods ----
+
+PopularityStatisticsImpl::PopularityStatisticsImpl()
+{
+ const int rateBaseCount(8);
+
+ m_historyHorizon = 0.0;
+
+ for (int n=0; n<rateBaseCount; ++n)
+ {
+ SingleFalloffHistory h;
+ h.falloff = 1.0 - (0.5 / std::exp(double(n)*1.5));
+ m_stats.push_back(h);
+ }
+}
+
+void PopularityStatisticsImpl::normalizeHistory(SingleFalloffHistory& h)
+{
+ //kdDebug() << "Normalize history" << endl;
+ double sum = h.iniVal;
+ map<QString, double>::iterator it;
+ for (it = h.vals.begin(); it != h.vals.end(); ++it)
+ {
+ sum += it->second;
+ }
+ for (it = h.vals.begin(); it != h.vals.end(); ++it)
+ {
+ it->second = it->second / sum;
+ }
+ h.iniVal = h.iniVal / sum;
+}
+
+void PopularityStatisticsImpl::updateServiceRanks()
+{
+ // For each service calculate the average over the popularity
+ // for all falloff values and then sort by these averaged values
+
+ vector<SingleFalloffHistory>::iterator
+ it(m_stats.begin()), end(m_stats.end());
+ map<QString, double> serviceValSum, serviceValWeightSum;
+ int numStats = m_stats.size();
+ for (int statIndex = 0; it != end; ++it, ++statIndex)
+ {
+ // Put more weight on the short term history if m_historyHorizon==0
+ // and more on the the long term history for m_historyHorizon==1
+ double a = 2*(numStats-1)*m_historyHorizon - numStats + 0.5;
+ if (statIndex < a || statIndex > a + numStats)
+ {
+ continue;
+ }
+
+ map<QString, double>::iterator valIt;
+ /*double valSum = 0;
+ for (valIt = it->vals.begin(); valIt != it->vals.end(); ++valIt)
+ {
+ valSum += valIt->second;
+ }
+ if (valSum == 0) valSum = 1;*/
+ for (valIt = it->vals.begin(); valIt != it->vals.end(); ++valIt)
+ {
+ serviceValWeightSum[valIt->first] += 1;
+ serviceValSum[valIt->first] += valIt->second;
+ }
+ }
+
+ m_servicesByPopularity.clear();
+ map<QString, double>::iterator sIt;
+ for (sIt = serviceValWeightSum.begin();
+ sIt != serviceValWeightSum.end(); ++sIt)
+ {
+ Popularity p;
+ p.service = sIt->first;
+ assert(sIt->second > 0);
+ p.popularity = serviceValSum[sIt->first] / sIt->second;
+ m_servicesByPopularity.push_back(p);
+ }
+ stable_sort(m_servicesByPopularity.begin(), m_servicesByPopularity.end());
+ m_serviceRanks.clear();
+ for (uint n = 0; n < m_servicesByPopularity.size(); ++n)
+ {
+ m_serviceRanks[m_servicesByPopularity[n].service] = n;
+ /*kdDebug() << QString("Rank %1: %2 %3").arg(n)
+ .arg(m_servicesByPopularity[n].popularity)
+ .arg(m_servicesByPopularity[n].service) << endl;*/
+ }
+}
diff --git a/kicker/applets/launcher/popularity.h b/kicker/applets/launcher/popularity.h
new file mode 100644
index 000000000..b1dcb32d6
--- /dev/null
+++ b/kicker/applets/launcher/popularity.h
@@ -0,0 +1,127 @@
+/*****************************************************************
+
+Copyright (c) 2005 Fred Schaettgen <[email protected]>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#ifndef __popularity_h__
+#define __popularity_h__
+
+#include <qstring.h>
+#include <qstringlist.h>
+
+class PopularityStatisticsImpl;
+class Prefs;
+
+/**
+ * Tracks the usage of any kind of service to offer recommendations.
+ * A service is identified by a string. After calling @useService
+ * a few times, you can get a popularity ranking for the used
+ * services.
+ * The algorithm tries to take both short- and long-term usage
+ * into account at the same time.
+ * The popularity value can be interpreted as the probability
+ * that the given service will be the next one to be used.
+ * If some new services are suddenly used a few times, their ranking
+ * may grow higher than the ranking of services, which were used very
+ * frequently a while ago. But after this short term usage has
+ * stopped, its influence gets weaker more quickly then for the old,
+ * more frequently used services.
+ * During first time usage, the algorithm needs some time to stabilize,
+ * since there is simply no dependable long term usage data available,
+ * so the ranking is more likely to change at first.
+ * But in the long run, the behaviour of the algorithm is
+ * completely stable, unlike simple usage counting for instance.
+ */
+class PopularityStatistics
+{
+public:
+ PopularityStatistics();
+ virtual ~PopularityStatistics();
+
+ /**
+ * Touch a service. This will increase the usage
+ * counters for the given service and decrease the
+ * counters for all the others.
+ */
+ void useService(const QString& service);
+
+ /**
+ * Exchange all state variables of the most
+ * popular service with those from services,
+ * moving the given services to the top of the
+ * list. Apart from that the order stays the same
+ * as before. Order of items in the string list
+ * does *not* matter/
+ */
+ void moveToTop(const QStringList& services);
+
+ /**
+ * Sets all counters to zero for the given service
+ */
+ void moveToBottom(const QString& service);
+
+ /**
+ * Retrieve the name of a service by its position
+ * in the current popularity ranking
+ */
+ QString serviceByRank(int n) const;
+
+ /**
+ * Retrieve the popularity (0-1) of a service by
+ * its position in the current popularity ranking
+ */
+ double popularityByRank(int n) const;
+
+ /**
+ * Gets the rank of a given service.
+ * Returns -1 if the service is not in the ranking
+ */
+ int rankByService(const QString service);
+
+ /**
+ * Writes the configuration.
+ * A section must be set already for config.
+ */
+ void writeConfig(Prefs* prefs) const;
+
+ /**
+ * Reads the configuration.
+ * A section must be set already for config.
+ */
+ void readConfig(Prefs* prefs);
+
+ /**
+ * Modify the weighting of the history logs.
+ * 0 <= h <= 1. 1 means long term history
+ * 0 means short term history - in fact the popularity ranking
+ * becomes a recently-used list in that case.
+ */
+ void setHistoryHorizon(double h);
+ double historyHorizon();
+
+protected:
+ PopularityStatisticsImpl *d;
+
+private:
+ PopularityStatistics(const PopularityStatistics&) {}
+};
+
+#endif
diff --git a/kicker/applets/launcher/prefs.kcfgc b/kicker/applets/launcher/prefs.kcfgc
new file mode 100644
index 000000000..26a3f3d07
--- /dev/null
+++ b/kicker/applets/launcher/prefs.kcfgc
@@ -0,0 +1,6 @@
+# Code generation options for kconfig_compiler
+File=launcherapplet.kcfg
+ClassName=Prefs
+Singleton=false
+Mutators=AutoAdjustMaxItems,Buttons,VolatileButtons,AutoAdjustMaxItems,AutoAdjustMinItems,AutoAdjustEnabled,IconDim,DragEnabled,ConserveSpace,ServiceInspos,ServiceNames,ServiceHistories
+# MemberVariables=public
diff --git a/kicker/applets/launcher/quickaddappsmenu.cpp b/kicker/applets/launcher/quickaddappsmenu.cpp
new file mode 100644
index 000000000..74d00a6e4
--- /dev/null
+++ b/kicker/applets/launcher/quickaddappsmenu.cpp
@@ -0,0 +1,67 @@
+/*****************************************************************
+
+Copyright (c) 2000 Bill Nagel
+ based on paneladdappsmenu.cpp which is
+ Copyright (c) 1999-2000 the kicker authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#include <kstandarddirs.h>
+#include <kdesktopfile.h>
+#include <kglobalsettings.h>
+#include <ksycocaentry.h>
+#include <kservice.h>
+#include <kservicegroup.h>
+
+#include <kdebug.h>
+#include "quickaddappsmenu.h"
+
+QuickAddAppsMenu::QuickAddAppsMenu(const QString &label, const QString &relPath, QWidget *target, QWidget *parent, const char *name, const QString &sender)
+ : PanelServiceMenu(label, relPath, parent, name)
+{
+ _targetObject = target;
+ _sender = sender;
+ connect(this, SIGNAL(addAppBefore(QString,QString)),
+ target, SLOT(addAppBeforeManually(QString,QString)));
+}
+
+QuickAddAppsMenu::QuickAddAppsMenu(QWidget *target, QWidget *parent, const QString &sender, const char *name)
+ : PanelServiceMenu(QString::null, QString::null, parent, name)
+{
+ _targetObject = target;
+ _sender = sender;
+ connect(this, SIGNAL(addAppBefore(QString,QString)),
+ target, SLOT(addAppBeforeManually(QString,QString)));
+}
+
+void QuickAddAppsMenu::slotExec(int id)
+{
+ if (!entryMap_.contains(id)) return;
+ KSycocaEntry * e = entryMap_[id];
+ KService::Ptr service = static_cast<KService *>(e);
+ emit addAppBefore(locate("apps", service->desktopEntryPath()),_sender);
+}
+
+
+PanelServiceMenu *QuickAddAppsMenu::newSubMenu(const QString &label, const QString &relPath, QWidget *parent, const char *name, const QString &insertInlineHeader)
+{
+ return new QuickAddAppsMenu(label, relPath, _targetObject, parent, name, _sender);
+}
+#include "quickaddappsmenu.moc"
diff --git a/kicker/applets/launcher/quickaddappsmenu.h b/kicker/applets/launcher/quickaddappsmenu.h
new file mode 100644
index 000000000..88465049c
--- /dev/null
+++ b/kicker/applets/launcher/quickaddappsmenu.h
@@ -0,0 +1,51 @@
+/*****************************************************************
+
+Copyright (c) 2000 Bill Nagel
+ based on paneladdappsmenu.h which is
+ Copyright (c) 1999-2000 the kicker authors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+s
+******************************************************************/
+
+#ifndef __quickaddappsmenu_h__
+#define __quickaddappsmenu_h__
+
+#include "service_mnu.h"
+
+class QuickAddAppsMenu: public PanelServiceMenu {
+ Q_OBJECT
+public:
+ QuickAddAppsMenu(const QString &label, const QString &relPath, QWidget *target, QWidget *parent=0, const char *name=0, const QString &sender=QString(""));
+ QuickAddAppsMenu(QWidget *target, QWidget *parent=0, const QString &sender=QString(""), const char *name=0);
+signals:
+ void addAppBefore(QString,QString);
+protected slots:
+ virtual void slotExec(int id);
+protected:
+ virtual PanelServiceMenu *newSubMenu(const QString &label,
+ const QString &relPath,
+ QWidget *parent,
+ const char *name,
+ const QString & _inlineHeader=QString::null);
+private:
+ QWidget *_targetObject;
+ QString _sender;
+};
+
+#endif
diff --git a/kicker/applets/launcher/quickbutton.cpp b/kicker/applets/launcher/quickbutton.cpp
new file mode 100644
index 000000000..933088b04
--- /dev/null
+++ b/kicker/applets/launcher/quickbutton.cpp
@@ -0,0 +1,322 @@
+/*****************************************************************
+
+Copyright (c) 2000 Bill Nagel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#include "quickbutton.h"
+#include "quickaddappsmenu.h"
+
+#include <qpainter.h>
+#include <qdrawutil.h>
+#include <qpopupmenu.h>
+#include <qtooltip.h>
+
+#include <kactionclasses.h>
+#include <kickertip.h>
+#include <klocale.h>
+#include <kdesktopfile.h>
+#include <krun.h>
+#include <kiconeffect.h>
+#include <kglobalsettings.h>
+#include <kcursor.h>
+#include <kapplication.h>
+#include <kipc.h>
+#include <kiconloader.h>
+#include <kurldrag.h>
+#include <kstandarddirs.h>
+
+#include <math.h>
+#include <algorithm>
+
+#ifdef DEBUG
+ #define DEBUGSTR kdDebug()
+#else
+ #define DEBUGSTR kndDebug()
+#endif
+
+QuickURL::QuickURL(const QString &u)
+{ DEBUGSTR<<"QuickURL::QuickURL("<<u<<")"<<endl<<flush;
+ KService::Ptr _service=0;
+ _menuId = u;
+ if (_menuId.startsWith("file:") && _menuId.endsWith(".desktop")) {
+ // this ensures that desktop entries are referenced by desktop name instead of by file name
+ _menuId=KURL(_menuId).path();
+ }
+ if (_menuId.startsWith("/")) {
+ // Absolute path
+ _kurl.setPath(_menuId);
+
+ if (_menuId.endsWith(".desktop")) {
+ // Strip path
+ QString s = _menuId;
+ s = s.mid(s.findRev('/')+1);
+ s = s.left(s.length()-8);
+ _service = KService::serviceByStorageId(s);
+ if (!_service) {
+ _service = new KService(_menuId);
+ } else {
+ }
+ }
+ } else if (!KURL::isRelativeURL(_menuId)) {
+ // Full URL
+ _kurl = _menuId;
+ } else {
+ // menu-id
+ _service = KService::serviceByMenuId(_menuId);
+ }
+ DEBUGSTR << "QuickURL: _service='"<<_service<<" _kurl="<<_kurl<<" _menuId="<<_menuId<<endl<<flush;
+
+ if (_service) {
+ if (!_service->isValid()) {
+ DEBUGSTR << "QuickURL: _service is not valid"<<endl<<flush;
+ // _service is a KShared pointer, don't try to delete it!
+ _service = 0;
+ } else {
+ DEBUGSTR << "QuickURL: _service='"<<_service<<"' _service->desktopEntryPath()="<<_service->desktopEntryPath()<<endl<<flush;
+ if (_kurl.path().length() == 0)
+ {
+ _kurl.setPath(locate("apps", _service->desktopEntryPath()));
+ }
+ if (!_service->menuId().isEmpty())
+ _menuId = _service->menuId();
+
+ m_genericName = _service->genericName();
+ m_name = _service->name();
+ }
+ } else {
+ m_name = _kurl.prettyURL();
+ }
+ DEBUGSTR<<"QuickURL::QuickURL("<<u<<") END"<<endl<<flush;
+}
+
+void QuickURL::run() const
+{ kapp->propagateSessionManager(); // is this needed?
+ if (_service)
+ KRun::run(*(_service), KURL::List());
+ else
+ new KRun(_kurl, 0, _kurl.isLocalFile());
+}
+
+//similar to MimeType::pixmapForURL
+QPixmap QuickURL::pixmap( mode_t _mode, KIcon::Group _group,
+ int _force_size, int _state, QString *) const
+{ // Load icon
+ QPixmap pxmap = KMimeType::pixmapForURL(_kurl, _mode, _group, _force_size, _state);
+ // Resize to fit button
+ pxmap.convertFromImage(pxmap.convertToImage().smoothScale(_force_size,_force_size, QImage::ScaleMin));
+ return pxmap;
+}
+
+
+QuickButton::QuickButton(const QString &u, KAction* configAction,
+ QWidget *parent, const char *name) :
+ SimpleButton(parent, name),
+ m_flashCounter(0),
+ m_sticky(false)
+{
+ installEventFilter(KickerTip::the());
+ setMouseTracking(true);
+ _highlight = false;
+ _oldCursor = cursor();
+ _qurl=new QuickURL(u);
+
+ QToolTip::add(this, _qurl->name());
+ resize(int(DEFAULT_ICON_DIM),int(DEFAULT_ICON_DIM));
+ QBrush bgbrush(colorGroup().brush(QColorGroup::Background));
+
+ QuickAddAppsMenu *addAppsMenu = new QuickAddAppsMenu(
+ parent, this, _qurl->url());
+ _popup = new QPopupMenu(this);
+ _popup->insertItem(i18n("Add Application"), addAppsMenu);
+ configAction->plug(_popup);
+ _popup->insertSeparator();
+ _popup->insertItem(SmallIcon("remove"), i18n("Remove"),
+ this, SLOT(removeApp()));
+
+ m_stickyAction = new KToggleAction(i18n("Never Remove Automatically"),
+ KShortcut(), this);
+ connect(m_stickyAction, SIGNAL(toggled(bool)),
+ this, SLOT(slotStickyToggled(bool)));
+ m_stickyAction->plug(_popup, 2);
+ m_stickyId = _popup->idAt(2);
+
+ connect(this, SIGNAL(clicked()), SLOT(launch()));
+ connect(this, SIGNAL(removeApp(QuickButton *)), parent,
+ SLOT(removeAppManually(QuickButton *)));
+}
+
+QuickButton::~QuickButton()
+{
+ delete _qurl;
+}
+
+
+QString QuickButton::url() const
+{
+ return _qurl->url();
+}
+
+
+QString QuickButton::menuId() const
+{ return _qurl->menuId();}
+
+
+void QuickButton::loadIcon()
+{
+ // Set Icon Dimension from size
+ _iconDim=std::min(size().width(),size().height())-2*ICON_MARGIN;
+ // Load icons
+ _icon = _qurl->pixmap(0, KIcon::Panel, _iconDim, KIcon::DefaultState);
+ _iconh = _qurl->pixmap(0, KIcon::Panel, _iconDim, KIcon::ActiveState);
+ setPixmap(_icon);
+}
+
+void QuickButton::resizeEvent(QResizeEvent *e)
+{
+ loadIcon();
+ SimpleButton::resizeEvent(e);
+}
+
+void QuickButton::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() == RightButton)
+ _popup->popup(e->globalPos());
+ else if (e->button() == LeftButton) {
+ _dragPos = e->pos();
+ QButton::mousePressEvent(e);
+ }
+}
+
+void QuickButton::mouseMoveEvent(QMouseEvent *e)
+{
+ if ((e->state() & LeftButton) == 0) return;
+ QPoint p(e->pos() - _dragPos);
+ if (p.manhattanLength() <= KGlobalSettings::dndEventDelay())
+ return;
+ DEBUGSTR<<"dragstart"<<endl<<flush;
+ setDown(false);
+ if (_dragEnabled) {
+ KURL::List uris;
+ uris.append(_qurl->kurl());
+ DEBUGSTR<<"creating KURLDrag"<<endl<<flush;
+ KURLDrag *dd = new KURLDrag(uris,this);
+ dd->setPixmap(_icon); //PIX
+ DEBUGSTR<<"ready to drag"<<endl<<flush;
+ grabKeyboard();
+ dd->drag();
+ releaseKeyboard();
+ } else {
+ setCursor(Qt::ForbiddenCursor);
+ }
+}
+
+void QuickButton::slotIconChanged(int group)
+{
+ loadIcon();
+ SimpleButton::slotIconChanged(group);
+ update();
+}
+
+void QuickButton::launch()
+{
+ setDown(false);
+ update();
+ KIconEffect::visualActivate(this, rect());
+ _qurl->run();
+ emit executed(_qurl->menuId());
+}
+
+void QuickButton::setDragging(bool enable)
+{
+ setDown(enable);
+ _highlight=enable;
+ update();
+}
+
+void QuickButton::setEnableDrag(bool enable)
+{
+ _dragEnabled=enable;
+}
+
+void QuickButton::removeApp()
+{
+ emit removeApp(this);
+}
+
+void QuickButton::flash()
+{
+ m_flashCounter = 2000;
+ QTimer::singleShot(0, this, SLOT(slotFlash()));
+}
+
+void QuickButton::slotFlash()
+{
+ static const int timeout = 500/4;
+ if (m_flashCounter > 0)
+ {
+ m_flashCounter -= timeout;
+ if (m_flashCounter < 0) m_flashCounter = 0;
+ update();
+ QTimer::singleShot(timeout, this, SLOT(slotFlash()));
+ }
+}
+
+void QuickButton::slotStickyToggled(bool isSticky)
+{
+ m_sticky = isSticky;
+ emit stickyToggled(isSticky);
+}
+
+void QuickButton::setSticky(bool sticky)
+{
+ m_stickyAction->setChecked(sticky);
+ slotStickyToggled(sticky);
+}
+
+void QuickButton::updateKickerTip(KickerTip::Data &data)
+{
+ if (!_qurl)
+ {
+ return;
+ }
+ data.message = _qurl->name();
+ data.direction = m_popupDirection;
+ data.subtext = _qurl->genericName();
+ if (data.subtext == QString())
+ {
+ data.subtext = data.message;
+ }
+ data.icon = KMimeType::pixmapForURL(_qurl->kurl(), 0,
+ KIcon::Panel, KIcon::SizeHuge, KIcon::DefaultState);
+}
+
+void QuickButton::setPopupDirection(KPanelApplet::Direction d)
+{
+ m_popupDirection = d;
+}
+
+void QuickButton::setDynamicModeEnabled(bool enabled)
+{
+ _popup->setItemVisible(m_stickyId, enabled);
+}
+
+
+#include "quickbutton.moc"
diff --git a/kicker/applets/launcher/quickbutton.h b/kicker/applets/launcher/quickbutton.h
new file mode 100644
index 000000000..98eabec6e
--- /dev/null
+++ b/kicker/applets/launcher/quickbutton.h
@@ -0,0 +1,124 @@
+/*****************************************************************
+
+Copyright (c) 2000 Bill Nagel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#ifndef __quickbutton_h__
+#define __quickbutton_h__
+
+#include <qbutton.h>
+#include <qpoint.h>
+#include <qstring.h>
+#include <qpixmap.h>
+#include <qcursor.h>
+
+#include <kickertip.h>
+#include <kicontheme.h>
+#include <kmimetype.h>
+#include <kpanelapplet.h>
+#include <kservice.h>
+#include <kurl.h>
+
+#include "simplebutton.h"
+
+class QPopupMenu;
+class KAction;
+class KToggleAction;
+
+class QuickURL {
+public:
+ QuickURL(const QString &u);
+ KURL kurl() const {return _kurl;};
+ QString url() const {return _kurl.url();};
+ QString menuId() const {return _menuId;};
+ QString genericName() const { return m_genericName; }
+ QString name() const { return m_name; }
+ KService::Ptr service() const {return _service;};
+ void run() const;
+ QPixmap pixmap(mode_t _mode = 0, KIcon::Group _group = KIcon::Desktop,
+ int _force_size = 0, int _state = 0, QString * _path = 0L) const;
+
+private:
+ KURL _kurl;
+ QString _menuId;
+ QString m_genericName;
+ QString m_name;
+ KService::Ptr _service;
+};
+
+
+class QuickButton: public SimpleButton, public KickerTip::Client {
+ Q_OBJECT
+
+public:
+ enum { DEFAULT_ICON_DIM = 16 };
+ enum { ICON_MARGIN = 1 };
+ QuickButton(const QString &u, KAction* configAction,
+ QWidget *parent=0, const char *name=0);
+ ~QuickButton();
+ QString url() const;
+ QString menuId() const;
+ QPixmap icon() const{ return _icon;}
+ bool sticky() { return m_sticky; }
+ void setSticky(bool bSticky);
+ void setPopupDirection(KPanelApplet::Direction d);
+
+ void setDragging(bool drag);
+ void setEnableDrag(bool enable);
+ void setDynamicModeEnabled(bool enabled);
+ void flash();
+
+signals:
+ void removeApp(QuickButton *);
+ void executed(QString serviceStorageID);
+ void stickyToggled(bool isSticky);
+
+protected:
+ void mousePressEvent(QMouseEvent *e);
+ void mouseMoveEvent(QMouseEvent *e);
+ void resizeEvent(QResizeEvent *rsevent);
+ void loadIcon();
+ void updateKickerTip(KickerTip::Data &data);
+
+protected slots:
+ void slotIconChanged(int);
+ void launch();
+ void removeApp();
+ void slotFlash();
+ void slotStickyToggled(bool isSticky);
+
+private:
+ int m_flashCounter;
+ QuickURL *_qurl;
+ QPoint _dragPos;
+ QPopupMenu *_popup;
+ QPixmap _icon, _iconh;
+ QCursor _oldCursor;
+ bool _highlight, _changeCursorOverItem, _dragEnabled;
+ int _iconDim;
+ bool m_sticky;
+ KToggleAction *m_stickyAction;
+ int m_stickyId;
+ KPanelApplet::Direction m_popupDirection;
+};
+
+#endif
+
diff --git a/kicker/applets/launcher/quickbuttongroup.h b/kicker/applets/launcher/quickbuttongroup.h
new file mode 100644
index 000000000..1d373ae92
--- /dev/null
+++ b/kicker/applets/launcher/quickbuttongroup.h
@@ -0,0 +1,60 @@
+/* Copyright 2004, Daniel Woods Bullok <[email protected]>
+ distributed under the terms of the
+ GNU GENERAL PUBLIC LICENSE Version 2 -
+ See the file kdebase/COPYING for details
+*/
+
+#ifndef __quickbuttongroup_h__
+#define __quickbuttongroup_h__
+
+#include <qstring.h>
+#include <functional>
+#include "easyvector.h"
+#include "quickbutton.h"
+
+
+class QuickButtonGroup: virtual public EasyVector< QuickButton* > {
+public:
+ QuickButtonGroup(const EasyVector< QuickButton* > &kv):EasyVector< QuickButton* >(kv){};
+ QuickButtonGroup():EasyVector< QuickButton* >(){};
+ Index findDescriptor(const QString &desc);
+
+ void show();
+ void hide();
+ void setDragging(bool drag);
+ void setEnableDrag(bool enable);
+ void deleteContents();
+ void setUpdatesEnabled(bool enable);
+};
+
+QuickButtonGroup::Index QuickButtonGroup::findDescriptor(const QString &desc)
+{ return findProperty(desc, std::mem_fun(&QuickButton::url));}
+
+inline void QuickButtonGroup::setUpdatesEnabled(bool enable)
+{ for (QuickButtonGroup::iterator i=begin();i!=end();++i) {
+ (*i)->setUpdatesEnabled(enable);
+ if (enable) { (*i)->update();}
+ }
+}
+
+inline void QuickButtonGroup::show()
+{ std::for_each(begin(),end(),std::mem_fun(&QWidget::show));}
+
+inline void QuickButtonGroup::hide()
+{ std::for_each(begin(),end(),std::mem_fun(&QWidget::hide));}
+
+inline void QuickButtonGroup::setDragging(bool drag)
+{ std::for_each(begin(),end(),std::bind2nd(std::mem_fun(&QuickButton::setDragging),drag));}
+
+inline void QuickButtonGroup::setEnableDrag(bool enable)
+{ std::for_each(begin(),end(),std::bind2nd(std::mem_fun(&QuickButton::setEnableDrag),enable));}
+
+inline void QuickButtonGroup::deleteContents()
+{ for (QuickButtonGroup::iterator i=begin();i!=end();++i) {
+ delete (*i);
+ (*i)=0;
+ }
+}
+
+#endif
+
diff --git a/kicker/applets/launcher/quicklauncher.cpp b/kicker/applets/launcher/quicklauncher.cpp
new file mode 100644
index 000000000..abae9efe1
--- /dev/null
+++ b/kicker/applets/launcher/quicklauncher.cpp
@@ -0,0 +1,1093 @@
+/*****************************************************************
+
+Copyright (c) 2000 Bill Nagel
+Copyright (c) 2004 Dan Bullok <[email protected]>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#include <qpainter.h>
+#include <qpopupmenu.h>
+#include <qslider.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+
+#include <dcopclient.h>
+#include <kaction.h>
+#include <kapplication.h>
+#include <kaboutapplication.h>
+#include <kaboutdata.h>
+#include <kdialogbase.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <knuminput.h>
+#include <kconfig.h>
+#include <kstandarddirs.h>
+#include <kurldrag.h>
+#include <kdebug.h>
+
+
+#include <algorithm>
+#include <list>
+#include <math.h>
+#include <set>
+#include <assert.h>
+
+#include "configdlg.h"
+#include "popularity.h"
+#include "quicklauncher.h"
+#include "quickbutton.h"
+#include "quickaddappsmenu.h"
+#include "quickbuttongroup.h"
+
+typedef ButtonGroup::iterator ButtonIter;
+const ButtonGroup::Index NotFound=ButtonGroup::NotFound;
+const ButtonGroup::Index Append=ButtonGroup::Append;
+
+#ifdef DEBUG
+ #define DEBUGSTR kdDebug()
+#else
+ #define DEBUGSTR kndDebug()
+#endif
+
+extern "C"
+{
+ KDE_EXPORT KPanelApplet* init(QWidget *parent, const QString& configFile)
+ {
+ KGlobal::locale()->insertCatalogue("quicklauncher");
+ return new QuickLauncher(configFile, KPanelApplet::Normal,
+ KPanelApplet::Preferences,
+ parent, "quicklauncher");
+ }
+}
+
+QuickLauncher::QuickLauncher(const QString& configFile, Type type, int actions,
+ QWidget *parent, const char *name) :
+ KPanelApplet(configFile, type, actions, parent, name)
+{
+ DCOPObject::setObjId("QuickLauncherApplet");
+ DEBUGSTR << endl << endl << endl << "------------" << flush;
+ DEBUGSTR << "QuickLauncher::QuickLauncher(" << configFile << ",...)" <<
+ endl << flush;
+
+ m_settings = new Prefs(sharedConfig());
+ m_settings->readConfig();
+
+ m_needsSave = false;
+ m_needsRefresh = false;
+ m_refreshEnabled = false;
+
+ m_configDialog = 0;
+ m_popup = 0;
+ m_appletPopup = 0;
+ m_removeAppsMenu = 0;
+
+ m_dragAccepted = false;
+
+ m_buttons = new ButtonGroup;
+ m_manager = new FlowGridManager;
+ m_newButtons = 0;
+ m_oldButtons = 0;
+ m_dragButtons = 0;
+
+ m_configAction = new KAction(i18n("Configure Quicklauncher..."), "configure", KShortcut(),
+ this, SLOT(slotConfigure()), this);
+
+ m_saveTimer = new QTimer(this);
+ connect(m_saveTimer, SIGNAL(timeout()), this, SLOT(saveConfig()));
+
+ m_popularity = new PopularityStatistics();
+
+ setBackgroundOrigin(AncestorOrigin);
+
+ loadConfig();
+
+ buildPopupMenu();
+ m_minPanelDim = std::max(16, m_settings->iconDimChoices()[1]);
+ refreshContents();
+ setRefreshEnabled(true);
+
+ setAcceptDrops(true);
+ //QToolTip::add(this, i18n("Drop applications here"));
+ DEBUGSTR << " QuickLauncher::QuickLauncher(" << configFile <<
+ ",...) END" << endl << flush;
+
+ DCOPClient *dcopClient = KApplication::dcopClient();
+ dcopClient->connectDCOPSignal(0, "appLauncher",
+ "serviceStartedByStorageId(QString,QString)",
+ "QuickLauncherApplet",
+ "serviceStartedByStorageId(QString,QString)",
+ false);
+ kdDebug() << "Quicklauncher registered DCOP signal" << endl;
+}
+
+
+//TODO:? Drag/drop more than one item at a time
+
+QuickLauncher::~QuickLauncher()
+{
+ KGlobal::locale()->removeCatalogue("quicklauncher");
+ setCustomMenu(0);
+ delete m_popup;
+ delete m_appletPopup;
+ delete m_removeAppsMenu;
+ delete m_popularity;
+ clearTempButtons();
+ if (m_buttons)
+ {
+ m_buttons->deleteContents();
+ delete m_buttons;
+ }
+}
+
+// Builds, connects _popup menu
+void QuickLauncher::buildPopupMenu()
+{
+ QuickAddAppsMenu *addAppsMenu = new QuickAddAppsMenu(this, this);
+ m_popup = new QPopupMenu(this);
+ m_popup->insertItem(i18n("Add Application"), addAppsMenu);
+ m_configAction->plug(m_popup);
+
+ m_appletPopup = new QPopupMenu(this);
+ m_appletPopup->insertItem(i18n("Add Application"), addAppsMenu);
+ m_removeAppsMenu = new QPopupMenu(this);
+ connect(m_removeAppsMenu, SIGNAL(aboutToShow()),
+ SLOT(fillRemoveAppsMenu()));
+ connect(m_removeAppsMenu, SIGNAL(activated(int)),
+ SLOT(removeAppManually(int)));
+ m_appletPopup->insertItem(i18n("Remove Application"), m_removeAppsMenu);
+
+ m_appletPopup->insertSeparator();
+ m_appletPopup->setCheckable( true );
+ m_appletPopup->insertItem(i18n("About"), this, SLOT(about()));
+ setCustomMenu(m_appletPopup);
+}
+
+
+// Fill the remove apps menu
+void QuickLauncher::fillRemoveAppsMenu()
+{
+ m_removeAppsMenu->clear();
+ ButtonIter iter(m_buttons->begin());
+ int i = 0;
+ while (iter != m_buttons->end())
+ {
+ QString text = QToolTip::textFor(*iter);
+ if (text.isEmpty())
+ {
+ text = (*iter)->url();
+ if (text.isEmpty())
+ {
+ text = i18n("Unknown");
+ }
+ }
+ m_removeAppsMenu->insertItem((*iter)->icon(), text, i);
+ ++iter;
+ ++i;
+ }
+}
+
+void QuickLauncher::slotSettingsDialogChanged()
+{
+ // Update conserve space setting
+ setConserveSpace(m_settings->conserveSpace());
+ m_popularity->setHistoryHorizon(m_settings->historyHorizon()/100.0);
+ slotAdjustToCurrentPopularity();
+ kdDebug() << "Icon size: " << m_settings->iconDim() << endl;
+ refreshContents();
+
+ saveConfig();
+}
+
+void QuickLauncher::action(Action a)
+{
+ if (a == KPanelApplet::Preferences)
+ {
+ slotConfigure();
+ }
+ else
+ {
+ KPanelApplet::action(a);
+ }
+}
+
+void QuickLauncher::slotConfigure()
+{
+ if (!m_configDialog)
+ {
+ m_configDialog = new ConfigDlg(this, "configdialog",
+ m_settings, SIZE_AUTO, KDialogBase::Plain, KDialogBase::Ok |
+ KDialogBase::Cancel | KDialogBase::Apply | KDialogBase::Default);
+ connect(m_configDialog, SIGNAL(settingsChanged()),
+ this, SLOT(slotSettingsDialogChanged()));
+ }
+
+ m_configDialog->show();
+}
+
+
+int QuickLauncher::findApp(QuickButton *button)
+{
+ if (m_buttons->empty())
+ {
+ return NotFound;
+ }
+ int pos = m_buttons->findValue(button);
+ return pos;
+}
+
+
+int QuickLauncher::findApp(QString url)
+{
+ if (m_buttons->empty())
+ {
+ return NotFound;
+ }
+ int pos=m_buttons->findDescriptor(url);
+ return pos;
+}
+
+void QuickLauncher::removeAppManually(int index)
+{
+ removeApp(index, true);
+}
+
+void QuickLauncher::removeApp(int index, bool manuallyRemoved)
+{
+ if (m_buttons->empty())
+ {
+ return;
+ }
+ if (!m_buttons->isValidIndex(index))
+ {
+ kdWarning() << " removeApp (" << index <<
+ ") *******WARNING****** index=" << index << "is out of bounds." <<
+ endl << flush;
+ return;
+ }
+ DEBUGSTR << "Removing button. index=" << index << " url='" <<
+ (*m_buttons)[index]->url() << "'" << endl << flush;
+
+ QString removeAppUrl = (*m_buttons)[index]->url();
+ QString removeAppMenuId = (*m_buttons)[index]->menuId();
+
+ delete (*m_buttons)[index];
+ m_buttons->eraseAt(index);
+ refreshContents();
+
+ if (int(m_buttons->size()) < m_settings->autoAdjustMinItems() && manuallyRemoved)
+ {
+ m_settings->setAutoAdjustMinItems(m_buttons->size());
+ }
+
+ if (manuallyRemoved)
+ {
+ m_popularity->moveToBottom(removeAppMenuId);
+ slotAdjustToCurrentPopularity();
+ }
+
+ saveConfig();
+}
+
+
+void QuickLauncher::removeApp(QString url, bool manuallyRemoved)
+{
+ int index = findApp(url);
+ if (index == NotFound)
+ {
+ kdDebug() << "removeApp: Not found: " << url << endl;
+ return;
+ }
+ removeApp(index, manuallyRemoved);
+}
+
+
+void QuickLauncher::removeAppManually(QuickButton *button)
+{
+ int index = findApp(button);
+ if (index == NotFound)
+ {
+ return;
+ }
+ removeApp(index, true);
+}
+
+
+int QuickLauncher::widthForHeight(int h) const
+{
+ FlowGridManager temp_manager = *m_manager;
+ temp_manager.setFrameSize(QSize(h,h));
+ temp_manager.setOrientation(Qt::Horizontal); // ??? probably not necessary
+ if (temp_manager.isValid())
+ {
+ return temp_manager.frameSize().width();
+ }
+ return m_minPanelDim;
+}
+
+
+int QuickLauncher::heightForWidth(int w) const
+{
+ FlowGridManager temp_manager=*m_manager;
+ temp_manager.setFrameSize(QSize(w,w));
+ temp_manager.setOrientation(Qt::Vertical); // ??? probably not necessary
+ if (temp_manager.isValid())
+ {
+ return temp_manager.frameSize().height();
+ }
+ return m_minPanelDim;
+}
+
+
+int QuickLauncher::dimension() const
+{
+ if (orientation()==Qt::Vertical)
+ {
+ return size().width();
+ }
+ return size().height();
+}
+
+void QuickLauncher::addApp(QString url, bool manuallyAdded)
+{
+ assert(m_buttons);
+ QString newButtonId = QuickURL(url).menuId();
+ if (m_appOrdering.find(newButtonId) == m_appOrdering.end())
+ {
+ m_appOrdering[newButtonId] = m_appOrdering.size();
+ }
+ uint appPos;
+ for (appPos = 0; appPos < m_buttons->size(); ++appPos)
+ {
+ QString buttonId = (*m_buttons)[appPos]->menuId();
+ if (m_appOrdering[buttonId] >= m_appOrdering[newButtonId])
+ {
+ break;
+ }
+ }
+ addApp(url, appPos, manuallyAdded);
+}
+
+QuickButton* QuickLauncher::createButton(QString url)
+{
+ QuickButton* newButton=new QuickButton(url, m_configAction, this);
+ connect(newButton, SIGNAL(executed(QString)),
+ this, SLOT(slotOwnServiceExecuted(QString)));
+ connect(newButton, SIGNAL(stickyToggled(bool)),
+ this, SLOT(slotStickyToggled()));
+ newButton->setPopupDirection(popupDirection());
+ return newButton;
+}
+
+void QuickLauncher::addApp(QString url, int index, bool manuallyAdded)
+{
+ DEBUGSTR << endl <<"About to add: url='" << url <<
+ "' index=" << index << endl << flush;
+ QuickButton *newButton;
+ if (!m_buttons->isValidInsertIndex(index))
+ {
+ kdWarning() << " *******WARNING****** index=" << index <<
+ "is out of bounds." << endl << flush;
+ index = m_buttons->lastIndex();
+ }
+ int old = findApp(QuickURL(url).url());
+ if (old != NotFound)
+ {
+ if (index == old)
+ {
+ return;
+ }
+ if (index > old)
+ {
+ index--;
+ }
+ newButton = (*m_buttons)[old];
+ m_buttons->eraseAt(old);
+ }
+ else
+ {
+ newButton = createButton(url);
+ }
+ m_buttons->insertAt(index, newButton);
+ DEBUGSTR << "Added: url='"<<url<<"' index="<<index<<endl<<endl<<flush;
+ refreshContents();
+
+ if (manuallyAdded)
+ {
+ newButton->setSticky(true);
+ if (int(m_buttons->size()) > m_settings->autoAdjustMaxItems())
+ {
+ m_settings->setAutoAdjustMaxItems(m_buttons->size());
+ }
+ }
+
+ updateInsertionPosToStatusQuo();
+ saveConfig();
+}
+
+void QuickLauncher::updateInsertionPosToStatusQuo()
+{
+ // Update the app ordering map, so that next time,
+ // addApp(url,manAdded) (without index) will insert the
+ // item at the same position again.
+ std::list<QString> appList;
+ std::set<int> posList;
+ //kdDebug() << "Rearranging application order. Before:" << endl;
+ for (uint n = 0; n < m_buttons->size(); ++n)
+ {
+ QString buttonId = (*m_buttons)[n]->menuId();
+ appList.push_back(buttonId);
+ if (m_appOrdering.find(buttonId) == m_appOrdering.end())
+ {
+ m_appOrdering[buttonId] = m_appOrdering.size();
+ }
+ posList.insert(m_appOrdering[buttonId]);
+ //kdDebug() << m_appOrdering[buttonId] << " = " << buttonId << endl;
+ }
+ //kdDebug() << "After:" << endl;
+ while (posList.size() > 0)
+ {
+ assert(appList.size() > 0);
+ m_appOrdering[*appList.begin()] = *posList.begin();
+ kdDebug() << *posList.begin() << " = " << *appList.begin() << endl;
+ posList.erase(posList.begin());
+ appList.pop_front();
+ }
+ //kdDebug() << "Done." << endl;
+}
+
+void QuickLauncher::addAppBeforeManually(QString url, QString sender)
+{
+ if (sender.isNull())
+ {
+ addApp(url, Append, true);
+ }
+ int pos = findApp(sender);
+ if (pos < 0)
+ {
+ pos = Append;
+ }
+ DEBUGSTR << "QuickLauncher::addAppBefore(" << url <<
+ "," << sender << "): pos=" << pos << endl << flush;
+ addApp(url, pos, true);
+}
+
+
+void QuickLauncher::about()
+{
+ KAboutData about("quicklauncher", I18N_NOOP("Quick Launcher"), "2.0",
+ I18N_NOOP("A simple application launcher"),
+ KAboutData::License_GPL_V2,
+ "(C) 2000 Bill Nagel\n(C) 2004 Dan Bullok\n(C) 2005 Fred Schaettgen");
+ KAboutApplication a(&about, this);
+ a.exec();
+}
+
+
+void QuickLauncher::mousePressEvent(QMouseEvent *e)
+{
+ if (e->button() == RightButton)
+ {
+ m_popup->popup(e->globalPos());
+ }
+}
+
+void QuickLauncher::resizeEvent(QResizeEvent*)
+{
+ refreshContents();
+}
+
+void QuickLauncher::dragEnterEvent(QDragEnterEvent *e)
+{
+ DEBUGSTR << "QuickLauncher::dragEnterEvent(pos=" << e->pos() <<
+ " type=" << e->type() << ")" << endl << flush;
+ m_dragAccepted=false;
+ KURL::List kurlList;
+ if (!isDragEnabled() || !KURLDrag::decode(e, kurlList))
+ {
+ e->accept(false);
+ return;
+ }
+
+ if (kurlList.size()<=0)
+ {
+ e->accept(false);
+ return;
+ }
+ m_dragButtons=new ButtonGroup;
+ m_oldButtons=new ButtonGroup(*m_buttons);
+
+ QString url;
+ KURL::List::ConstIterator it = kurlList.begin();
+ for ( ; it != kurlList.end(); ++it )
+ {
+ url = QuickURL((*it).url()).url();
+ kdDebug() << " Drag Object='"<<url<<"' " << (*it).url() << endl;
+ int pos = m_buttons->findDescriptor(url);
+ if (pos != NotFound)
+ {
+ // if it's already in m_buttons, take it out
+ m_dragButtons->push_back(m_buttons->takeFrom(pos));
+ }
+ else
+ {
+ // otherwise, create a new one
+ QuickButton* button = createButton(url);
+ button->setSticky(true);
+ m_dragButtons->push_back(button);
+ }
+ }
+ if (m_dragButtons->size() > 0)
+ {
+ //make sure we can drag at least one button.
+ m_dragAccepted=true;
+ m_newButtons=new ButtonGroup(*m_buttons);
+ m_dropPos=NotFound;
+ e->accept(true);
+ return;
+ }
+ e->accept(false);
+ clearTempButtons();
+}
+
+
+void QuickLauncher::dragMoveEvent(QDragMoveEvent *e)
+{
+ if (!m_dragAccepted)
+ {
+ kdWarning() << "QuickLauncher::dragMoveEvent: Drag is not accepted." <<
+ m_dragAccepted << endl << flush;
+ e->accept(false);
+ return;
+ }
+
+ e->accept(true);
+ int pos=m_manager->indexNearest(e->pos());
+ if (pos == m_dropPos)
+ {
+ return;// Already been inserted here, no need to update
+ }
+
+ if (m_newButtons->isValidInsertIndex(pos))
+ {
+ mergeButtons(pos);
+ m_dropPos=pos;
+ }
+ refreshContents();
+}
+
+
+void QuickLauncher::dragLeaveEvent(QDragLeaveEvent *e)
+{
+ DEBUGSTR << "QuickLauncher::dragLeaveEvent(type=" <<
+ e->type() << ")" << endl << flush;
+ if (!m_dragAccepted)
+ {
+ return;
+ }
+
+ // No drop. Return to starting state.
+ std::swap(m_buttons,m_oldButtons);
+ clearTempButtons();
+
+ refreshContents();
+ saveConfig();
+}
+
+
+void QuickLauncher::dropEvent(QDropEvent *e)
+{
+ DEBUGSTR << "QuickLauncher::dropEvent(pos=" << e->pos() <<
+ " type=" << e->type() << ")" << endl << flush;
+ if (!m_dragAccepted)
+ {
+ e->accept(false);
+ return;
+ }
+
+ if (e->source() == 0)
+ {
+ for (uint n=0; n<m_dragButtons->size(); ++n)
+ {
+ (*m_dragButtons)[n]->setSticky(true);
+ }
+ }
+
+ clearTempButtons();
+ refreshContents();
+ saveConfig();
+ updateInsertionPosToStatusQuo();
+}
+
+// insert dragbuttons at index in m_newButtons. Put result in m_buttons
+void QuickLauncher::mergeButtons(int index)
+{
+ if (!m_newButtons->isValidInsertIndex(index))
+ {
+ index=m_newButtons->size();
+ }
+
+ m_buttons->clear();
+ (*m_buttons) = (*m_newButtons);
+ m_buttons->insertAt(index, *m_dragButtons);
+ refreshContents();
+}
+
+void QuickLauncher::clearTempButtons()
+{
+ std::set<QuickButton*> allButtons;
+ //put all the m_buttons in a set (removes duplicates automatically
+ if (m_newButtons)
+ {
+ allButtons.insert(m_newButtons->begin(),m_newButtons->end());
+ }
+ if (m_oldButtons)
+ {
+ allButtons.insert(m_oldButtons->begin(),m_oldButtons->end());
+ }
+ if (m_dragButtons)
+ {
+ allButtons.insert(m_dragButtons->begin(),m_dragButtons->end());
+ }
+
+ //delete temp ButtonGroups
+ delete m_newButtons; m_newButtons=0;
+ delete m_oldButtons; m_oldButtons=0;
+ delete m_dragButtons; m_dragButtons=0;
+
+ //if an element allButtons is NOT in m_buttons (the ones we keep), delete it
+ std::set<QuickButton *>::iterator iter = allButtons.begin();
+ while (iter != allButtons.end())
+ {
+ if (findApp(*iter) == NotFound)
+ {
+ delete *iter;
+ }
+ ++iter;
+ }
+ m_dragAccepted = false;
+ m_dropPos = NotFound;
+}
+
+void QuickLauncher::refreshContents()
+{
+ int idim, d(dimension());
+ // determine button size
+ if (m_settings->iconDim() == SIZE_AUTO)
+ {
+ if (d < 18)
+ {
+ idim = std::min(16,d);
+ }
+ else if (d < 64)
+ {
+ idim = 16;
+ }
+ else if (d < 80)
+ {
+ idim = 20;
+ }
+ else if (d < 122)
+ {
+ idim = 24;
+ }
+ else
+ {
+ idim = 28;
+ }
+ }
+ else
+ {
+ idim = std::min(m_settings->iconDim(), d - std::max((d/8)-1, 0) * 2);
+ }
+ m_space = std::max((idim/8)-1, 0);
+ m_border = m_space;
+ m_buttonSize = QSize(idim, idim);
+ m_manager->setOrientation(orientation());
+ m_manager->setNumItems(m_buttons->size());
+ m_manager->setFrameSize(size());
+ m_manager->setItemSize(m_buttonSize);
+ m_manager->setSpaceSize(QSize(m_space, m_space));
+ m_manager->setBorderSize(QSize(m_border, m_border));
+ if (!m_refreshEnabled)
+ {
+ m_needsRefresh=true;
+ return;
+ }
+ if (!m_manager->isValid())
+ {
+ kdDebug()<<endl<<"******WARNING****** Layout is invalid."<<
+ endl << flush;
+ m_manager->dump();
+ return;
+ }
+
+ unsigned index;
+ QPoint pos;
+ setUpdatesEnabled(false);
+ m_buttons->setUpdatesEnabled(false);
+ for (index = 0; index < m_buttons->size(); index++)
+ {
+ pos = m_manager->pos(index);
+ QuickButton *button = (*m_buttons)[index];
+ button->resize(m_manager->itemSize());
+ button->move(pos.x(), pos.y());
+ button->setDragging(false);
+ button->setEnableDrag(isDragEnabled());
+ button->setDynamicModeEnabled(m_settings->autoAdjustEnabled());
+ }
+ if (m_newButtons)
+ {
+ m_newButtons->setDragging(false);
+ }
+ if (m_dragButtons)
+ {
+ m_dragButtons->setDragging(true);
+ }
+ m_buttons->show();
+ setUpdatesEnabled(true);
+ update();
+ m_buttons->setUpdatesEnabled(true);
+ updateGeometry();
+ emit updateLayout();
+ updateStickyHighlightLayer();
+}
+
+
+void QuickLauncher::setDragEnabled(bool enable)
+{
+ m_settings->setDragEnabled(enable);
+}
+
+void QuickLauncher::setConserveSpace(bool conserve_space)
+{
+ m_manager->setConserveSpace(conserve_space);
+ if (conserve_space)
+ {
+ m_manager->setSlack(FlowGridManager::SpaceSlack,
+ FlowGridManager::SpaceSlack);
+ }
+ else
+ {
+ m_manager->setSlack(FlowGridManager::ItemSlack,
+ FlowGridManager::ItemSlack);
+ }
+ refreshContents();
+}
+
+class SortByPopularity {
+public:
+ bool operator()(const QuickLauncher::PopularityInfo& a,
+ const QuickLauncher::PopularityInfo& b)
+ {
+ return a.popularity < b.popularity;
+ }
+};
+
+void QuickLauncher::loadConfig()
+{
+ DEBUGSTR << "QuickLauncher::loadConfig()" << endl << flush;
+ //KConfig *c = config();
+ //c->setGroup("General");
+ setConserveSpace(m_settings->conserveSpace());
+ setDragEnabled(m_settings->dragEnabled());
+ /*DEBUGSTR << " IconDim="<<m_iconDim << endl << flush;
+ DEBUGSTR << " ConserveSpace=" << (m_manager->conserveSpace()) <<
+ endl << flush;
+ DEBUGSTR << " DragEnabled=" << isDragEnabled() << endl << flush;*/
+ QStringList volatileButtons = m_settings->volatileButtons();
+ QStringList urls = m_settings->buttons();
+ kdDebug() << "GetButtons " << urls.join("/") << endl;
+ QStringList::Iterator iter(urls.begin());
+ int n = 0;
+ while (iter != urls.end()) {
+ QString url = *iter;
+ addApp(url, n, false);
+ ++iter;
+ ++n;
+ }
+
+ // Restore sticky state
+ for (n=0; n<int(m_buttons->size()); ++n)
+ {
+ QuickButton* button = (*m_buttons)[n];
+ if (volatileButtons.contains(button->menuId()) == false)
+ {
+ button->setSticky(true);
+ }
+ button->setDynamicModeEnabled(m_settings->autoAdjustEnabled());
+ }
+
+ m_popularity->readConfig(m_settings);
+ m_popularity->setHistoryHorizon(m_settings->historyHorizon()/100.0);
+
+ QStringList serviceNames = m_settings->serviceNames();
+ QValueList<int> insPos = m_settings->serviceInspos();
+ for (int n=std::min(serviceNames.size(),insPos.size())-1; n>=0; --n)
+ {
+ m_appOrdering[serviceNames[n]] = insPos[n];
+ }
+}
+
+void QuickLauncher::saveConfig()
+{
+ if (!m_refreshEnabled)
+ {
+ m_needsSave=true;
+ return;
+ }
+ QStringList urls, volatileUrls;
+ ButtonIter iter = m_buttons->begin();
+ while (iter != m_buttons->end()) {
+ if ((*iter)->sticky() == false)
+ {
+ volatileUrls.append((*iter)->menuId());
+ }
+ urls.append((*iter)->menuId());
+ ++iter;
+ }
+ m_settings->setButtons(urls);
+ kdDebug() << "SetButtons " << urls.join("/") << endl;
+ m_settings->setVolatileButtons(volatileUrls);
+ m_settings->setConserveSpace(m_manager->conserveSpace());
+ m_settings->setDragEnabled(isDragEnabled());
+
+ m_popularity->writeConfig(m_settings);
+
+ // m_popularity must have written the current service list by now
+ QStringList serviceNames = m_settings->serviceNames();
+ QValueList<int> insertionPositions;
+ for (int n=0; n<int(serviceNames.size()); ++n)
+ {
+ if (m_appOrdering.find(serviceNames[n]) != m_appOrdering.end())
+ {
+ insertionPositions.push_back(m_appOrdering[serviceNames[n]]);
+ }
+ }
+ m_settings->setServiceInspos(insertionPositions);
+
+ m_settings->writeConfig();
+}
+
+
+void QuickLauncher::setRefreshEnabled(bool enable)
+{
+ m_refreshEnabled=enable;
+ if (m_refreshEnabled)
+ {
+ if (m_needsSave) {
+ saveConfig();
+ }
+ if (m_needsRefresh) {
+ refreshContents();
+ }
+ }
+}
+
+void QuickLauncher::serviceStartedByStorageId(QString /*starter*/, QString storageId)
+{
+ KService::Ptr service = KService::serviceByStorageId(storageId);
+ if (service->icon() == QString::null)
+ {
+ kdDebug() << storageId << " has no icon. Makes no sense to add it.";
+ return;
+ }
+ QuickURL url = QuickURL(locate("apps", service->desktopEntryPath()));
+ QString desktopMenuId(url.menuId());
+ kdDebug() << "storageId=" << storageId << " desktopURL=" << desktopMenuId << endl;
+ // A service was started somwhere else. If the quicklauncher contains
+ // this service too, we flash the icon
+ QuickButton *startedButton = 0;
+ std::set<QString> buttonIdSet;
+ for (uint n = 0; n < m_buttons->size(); ++n)
+ {
+ QuickButton *button = (*m_buttons)[n];
+ QString buttonMenuId = button->menuId();
+ buttonIdSet.insert(buttonMenuId);
+ if (desktopMenuId == buttonMenuId)
+ {
+ kdDebug() << "QuickLauncher: I know that one: " << storageId << endl;
+ button->flash();
+ startedButton = button;
+ }
+ }
+
+ // Update popularity info.
+ // We do this even if autoadjust is disabled
+ // so there are sane values to start with if it's turned on.
+ m_popularity->useService(desktopMenuId);
+
+ if (m_settings->autoAdjustEnabled())
+ {
+ QTimer::singleShot(0, this, SLOT(slotAdjustToCurrentPopularity()));
+ }
+}
+
+void QuickLauncher::slotAdjustToCurrentPopularity()
+{
+ // TODO: Shrink immediately if buttons->size() > maxItems
+ kdDebug() << "Starting popularity update" << endl;
+ PopularityStatistics* stats = m_popularity;
+ int minItems = m_settings->autoAdjustMinItems();
+ int maxItems = m_settings->autoAdjustMaxItems();
+
+ static const double hysteresisFactor = 0.90;
+ double minAddPopularity = 0;
+ for (int n = 0; n < maxItems; ++n)
+ {
+ // All items with a popularity not less than 0.75 of the average
+ // of the first maxItems apps are included in the list
+ double belowAvgAllowed = 0.75;
+ minAddPopularity += (belowAvgAllowed * stats->popularityByRank(n)) / maxItems;
+ }
+ double minDelPopularity = minAddPopularity * hysteresisFactor;
+ std::map<QString, QuickButton*> removeableApps;
+ std::set<QString> existingApps;
+ int numApps = m_buttons->size();
+ for (int n = 0; n < int(m_buttons->size()); ++n)
+ {
+ QuickButton *button = (*m_buttons)[n];
+ if (((stats->popularityByRank(stats->rankByService(button->menuId())) <
+ minDelPopularity) || m_settings->autoAdjustEnabled()==false) &&
+ (button->sticky() == false))
+ {
+ removeableApps[button->menuId()] = button;
+ --numApps;
+ }
+ existingApps.insert(button->menuId());
+ }
+ for (int n = 0;
+ (numApps < minItems && stats->popularityByRank(n) > 0) ||
+ (numApps < maxItems && stats->popularityByRank(n) > minAddPopularity);
+ ++n)
+ {
+ QString app = m_popularity->serviceByRank(n);
+ if (existingApps.find(app) == existingApps.end())
+ {
+ addApp(QuickURL(m_popularity->serviceByRank(n)).url(), false);
+ kdDebug() << "Adding app " << app << endl;
+ ++numApps;
+ }
+ else if (removeableApps.find(app) != removeableApps.end())
+ {
+ removeableApps.erase(app);
+ ++numApps;
+ }
+ }
+ while (removeableApps.size() > 0)
+ {
+ removeApp(findApp(removeableApps.begin()->second), false);
+ kdDebug() << "Removing app " << removeableApps.begin()->first << endl;
+ removeableApps.erase(removeableApps.begin()->first);
+ }
+ kdDebug() << "done popularity update" << endl;
+ m_settings->setAutoAdjustMinItems(minItems);
+ m_settings->setAutoAdjustMaxItems(maxItems);
+
+ // TODO: Think of something better than that:
+ m_saveTimer->start(10000,true);
+}
+
+void QuickLauncher::slotOwnServiceExecuted(QString serviceMenuId)
+{
+ m_popularity->useService(serviceMenuId);
+ if (m_settings->autoAdjustEnabled())
+ {
+ QTimer::singleShot(0, this, SLOT(slotAdjustToCurrentPopularity()));
+ }
+}
+
+void QuickLauncher::updateStickyHighlightLayer()
+{
+ // Creates a transparent image which is used
+ // to highlight those buttons which will never
+ // be removed automatically from the launcher
+ QPixmap areaPix(width(), height());
+ QPainter areaPixPainter(&areaPix);
+ areaPixPainter.fillRect(0, 0, width(), height(), QColor(255, 255, 255));
+ QSize itemSize = m_manager->itemSize();
+ QSize spaceSize = m_manager->spaceSize();
+ for (uint n=0; n<m_buttons->size(); ++n)
+ {
+ QPoint pos = m_manager->pos(n);
+ if ((*m_buttons)[n]->sticky() == false)
+ {
+ areaPixPainter.fillRect(pos.x()-(spaceSize.width()+1)/2,
+ pos.y()-(spaceSize.height()+1)/2,
+ itemSize.width()+spaceSize.width()+1,
+ itemSize.height()+spaceSize.height()+1,
+ QColor(0, 0, 0));
+ }
+ }
+ QImage areaLayer = areaPix.convertToImage();
+ m_stickyHighlightLayer = QImage(width(), height(), 32);
+ m_stickyHighlightLayer.setAlphaBuffer(true);
+ int pix, tlPix, brPix, w(width()), h(height());
+ QRgb transparent(qRgba(0, 0, 0, 0));
+ for (int y = h-1; y >= 0; --y)
+ {
+ for (int x = w-1; x >= 0; --x)
+ {
+ pix = qRed(areaLayer.pixel(x, y));
+ if (pix == 0)
+ {
+ tlPix = (y>0 && x>0) ? qRed(areaLayer.pixel(x-1,y-1)) : 255;
+ brPix = (y<h-1 && x<w-1) ? qRed(areaLayer.pixel(x+1,y+1)) : 255;
+ int c = tlPix-brPix < 0 ? 255 : 0;
+ int alpha = abs(tlPix-brPix)/2;
+ m_stickyHighlightLayer.setPixel(x, y, qRgba(c, c, c, alpha));
+ }
+ else
+ {
+ m_stickyHighlightLayer.setPixel(x, y, transparent);
+ }
+ }
+ }
+ repaint();
+}
+
+void QuickLauncher::paintEvent(QPaintEvent* e)
+{
+ KPanelApplet::paintEvent(e);
+
+ if (m_settings->autoAdjustEnabled() &&
+ m_settings->showVolatileButtonIndicator())
+ {
+ QPainter p(this);
+ p.drawImage(0, 0, m_stickyHighlightLayer);
+ }
+}
+
+void QuickLauncher::slotStickyToggled()
+{
+ updateStickyHighlightLayer();
+ saveConfig();
+}
+
+void QuickLauncher::positionChange(Position)
+{
+ for (int n=0; n<int(m_buttons->size()); ++n)
+ {
+ (*m_buttons)[n]->setPopupDirection(popupDirection());
+ }
+}
+
+
+#include "quicklauncher.moc"
diff --git a/kicker/applets/launcher/quicklauncher.desktop b/kicker/applets/launcher/quicklauncher.desktop
new file mode 100644
index 000000000..0e80149aa
--- /dev/null
+++ b/kicker/applets/launcher/quicklauncher.desktop
@@ -0,0 +1,140 @@
+[Desktop Entry]
+Type=Plugin
+Name=Quick Launcher
+Name[af]=Vinnige Lanseerder
+Name[ar]=الإنطلاق السريع
+Name[az]=Sür'ətli Başladıcı
+Name[be]=Хуткі запускальнік
+Name[bg]=Бързо стартиране
+Name[bn]=কুইক লঞ্চার
+Name[br]=Loc'her prim
+Name[bs]=Brzo pokretanje
+Name[ca]=Engegador ràpid
+Name[cs]=Rychlé spouštění aplikací
+Name[csb]=Chùtczé zrëszenié
+Name[cy]=Cychwynydd Cyflym
+Name[da]=Hurtigstarter
+Name[de]=Schnellstarter
+Name[el]=Γρήγορη φόρτωση
+Name[eo]=Rapidlanĉilo
+Name[es]=Lanzador rápido
+Name[et]=Kiirkäivitaja
+Name[eu]=Abiarazle bizkorra
+Name[fa]=راه‌انداز سریع
+Name[fi]=Sovellusten pikakäynnistin
+Name[fr]=Lanceur d'applications
+Name[fy]=Snel útfierder
+Name[ga]=Tosaitheoir Tapa
+Name[gl]=Lanzador Rápido
+Name[he]=הפעלה מהירה
+Name[hi]=द्रुत लांचर
+Name[hr]=Brzo pokretanje
+Name[hu]=Gyorsindító
+Name[id]=Launcher Cepat
+Name[is]=Flýtiræsir
+Name[it]=Esecuzione rapida
+Name[ja]=クイックランチャー
+Name[ka]=სწრაფი დაწყება
+Name[kk]=Жедел жегуші
+Name[km]=អ្នក​ចាប់ផ្ដើម​រហ័ស
+Name[lo]=ຮງກທຳງານດ່ວນ
+Name[lt]=Greitasis paleidimas
+Name[lv]=Ātrais Palaidējs
+Name[mk]=Брз стартувач
+Name[mn]=Түргэн ажилуулагч
+Name[ms]=Pelancar Pantas
+Name[mt]=Ħaddem Malajr
+Name[nb]=Hurtigstarter
+Name[nds]=Fixstarter
+Name[ne]=द्रुत सुरुआत
+Name[nl]=Snelstarter
+Name[nn]=Snøggstartar
+Name[nso]=Ngwadisoleswa ya Kapela
+Name[oc]=Engegador rapid
+Name[pa]=ਚੁਸਤ ਸ਼ੁਰੂਆਤੀ
+Name[pl]=Szybkie uruchamianie
+Name[pt]=Execução de Aplicações
+Name[pt_BR]=Lançador rápido
+Name[ro]=Executor rapid
+Name[ru]=Быстрый запуск
+Name[rw]=Mutangiza Yihuta
+Name[se]=Jođánisálggaheaddji
+Name[sk]=Rýchly spúšťač
+Name[sl]=Hitri zaganjalnik
+Name[sr]=Брзи покретач
+Name[sr@Latn]=Brzi pokretač
+Name[sv]=Snabbstartare
+Name[ta]=உடனடியாக திரையில் தெரிதல்
+Name[te]=త్వరగా మొదలుపెట్టెది
+Name[tg]=Сар додани тез
+Name[th]=เรียกทำงานด่วน
+Name[tr]=Hızlı Başlatıcı
+Name[tt]=Tiz Cibärgeç
+Name[uk]=Швидкий запуск
+Name[uz]=Tez ishga tushirgich
+Name[uz@cyrillic]=Тез ишга туширгич
+Name[ven]=Tavhanya
+Name[vi]=Khởi động nhanh
+Name[wa]=Enondeu al vole di programes
+Name[zh_CN]=快速启动
+Name[zh_TW]=快速起動
+Name[zu]=Umqalisi osheshayo
+Comment=Directly access your frequently used applications
+Comment[af]=Kry direkte toegang tot die programme wat jy gereeld gebruik
+Comment[ar]=للوصول المباشر إلى تطبيقاتك الأكثر إستعمالاً
+Comment[be]=Наўпрост запускае праграму
+Comment[bg]=Бърз достъп до често използваните програми
+Comment[bn]=আপনার সবচেয়ে ঘনঘন ব্যবহৃত অ্যাপলিকেশনগুলি সরাসরি চালু করুন
+Comment[bs]=Direktno pristupite vašim često korištenim programima
+Comment[ca]=Accedeix directament a les aplicacions més usades
+Comment[cs]=Přímý přístup k nejčastěji používaným aplikacím
+Comment[csb]=Prosti przistãp do nôczãstczi brëkòwónëch programów
+Comment[da]=Direkte adgang til programmer du ofte bruger
+Comment[de]=Schneller Zugriff auf häufig verwendete Programme
+Comment[el]=Απευθείας πρόσβαση στις συχνά χρησιμοποιούμενες εφαρμογές σας
+Comment[eo]=Rekte atingi viajn preferatajn aplikaĵojn
+Comment[es]=Acceso directo a las aplicaciones usadas más frecuentemente
+Comment[et]=Ligipääs sagedamini kasutatud rakendustele
+Comment[eu]=Sarbide zuzena zure ohiko aplikazioei
+Comment[fa]=دستیابی مستقیم به کاربردهای مکرر استفاده‌شدۀ شما
+Comment[fi]=Siirry suoraan useimmin käyttämiisi sovelluksiin
+Comment[fr]=Accès direct aux applications les plus utilisées
+Comment[fy]=Direkte tagong ta jo faak brûkte programma's
+Comment[gl]=Aceda directamenta ás aplicacións que use mais amiudo
+Comment[he]=גישה מהירה ליישומים שאתה משתמש בהם הכי הרבה
+Comment[hr]=Izravni pristup najčešće upotrebljavanim aplikacijama
+Comment[hu]=A gyakran használt alkalmazások közvetlen elérése
+Comment[is]=Beinn aðgangur að mest notuðu forritunum þínum
+Comment[it]=Accesso diretto alle applicazioni usate più frequentemente
+Comment[ja]=よく用いるアプリケーションに直接アクセス
+Comment[kk]=Жиі пайдаланатын қолданбаларды тез жегу
+Comment[km]=ដំណើរការ​កម្មវិធី​ដែល​បាន​ប្រើ​ជា​រឿយៗ​របស់អ្នក​ដោយ​ផ្ទាល់
+Comment[lt]=Tiesiogiai pasiekite dažniausiai naudojamas programas
+Comment[mk]=Пристапете директно на вашите често користени апликации
+Comment[nb]=Få direkte tilgang til ofte brukte programmer
+Comment[nds]=Direktemang Dien meist bruukte Programmen opropen
+Comment[ne]=बारम्बार प्रयोग भएका अनुप्रयोगमा तपाईँको प्रत्यक्ष पहुँच
+Comment[nl]=Directe toegang tot uw veelgebruikte programma's
+Comment[nn]=Direkte tilgang til program du brukar ofte
+Comment[pa]=ਅਕਸਰ ਵਰਤੇ ਜਾਂਦੇ ਕਾਰਜਾਂ ਲਈ ਸਿੱਧੀ ਪਹੁੰਚ
+Comment[pl]=Bezpośredni dostęp do najczęściej używanych programów
+Comment[pt]=Aceder directamente às aplicações usadas com mais frequência por si
+Comment[pt_BR]=Acesso direito à seus aplicativos mais freqüentemente usados
+Comment[ro]=Accesează direct aplicațiile folosite frecvent
+Comment[ru]=Быстрый вызов часто используемых приложений
+Comment[sk]=Priamo zprístupní najčastejšie používané programy.
+Comment[sl]=Neposreden dostop do vaših najbolj uporabljanih programov
+Comment[sr]=Директно приступите својим често коришћеним програмима
+Comment[sr@Latn]=Direktno pristupite svojim često korišćenim programima
+Comment[sv]=Direkt åtkomst av program du ofta använder
+Comment[th]=เรียกใช้งานแอพพลิเคชั่นที่คุณใช้บ่อยๆ ได้โดยตรง
+Comment[tr]=Sıkça kullanılan programlara erişim sağlar
+Comment[uk]=Безпосередній доступ до програм, які часто вживаються
+Comment[uz]=Eng koʻp ishlatilgan dasturlarga qisqa yoʻl
+Comment[uz@cyrillic]=Энг кўп ишлатилган дастурларга қисқа йўл
+Comment[vi]=Chạy ngay các trình bạn thường xuyên dùng
+Comment[wa]=Accès direk ås programes sovint eployîs
+Comment[zh_CN]=直接访问您最经常使用的应用程序
+Comment[zh_TW]=直接存取您最常使用的應用程式
+Icon=launch
+X-KDE-Library=launcher_panelapplet
diff --git a/kicker/applets/launcher/quicklauncher.h b/kicker/applets/launcher/quicklauncher.h
new file mode 100644
index 000000000..c82d39661
--- /dev/null
+++ b/kicker/applets/launcher/quicklauncher.h
@@ -0,0 +1,138 @@
+/*****************************************************************
+
+Copyright (c) 2000 Bill Nagel
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
+AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+******************************************************************/
+
+#ifndef __quicklauncher_h__
+#define __quicklauncher_h__
+
+#include <dcopobject.h>
+#include <qimage.h>
+#include <qstring.h>
+#include <qvaluevector.h>
+#include <kpanelapplet.h>
+#include <map>
+
+#include "flowgridmanager.h"
+#include "prefs.h"
+#include "quickbutton.h"
+
+class ConfigDlg;
+class QPopupMenu;
+class QuickButtonGroup;
+class PopularityStatistics;
+class KAction;
+
+typedef QuickButtonGroup ButtonGroup;
+
+class QuickLauncher: public KPanelApplet, public DCOPObject
+{
+ Q_OBJECT
+ K_DCOP
+
+k_dcop:
+ void serviceStartedByStorageId(QString starter, QString storageId);
+
+public:
+ enum {DEFAULT_ICON_DIM=QuickButton::DEFAULT_ICON_DIM};
+ enum {SIZE_AUTO=0};
+
+ struct PopularityInfo {
+ float popularity;
+ };
+
+ QuickLauncher(const QString& configFile, Type t = Normal, int actions = 0,
+ QWidget *parent = 0, const char *name = 0);
+ ~QuickLauncher();
+ int widthForHeight(int height) const;
+ int heightForWidth(int width) const;
+ void addApp(QString url, int index, bool manuallyAdded);
+ virtual void action(Action a);
+
+public slots:
+ void addApp(QString url, bool manuallyAdded);
+ void addAppBeforeManually(QString url, QString sender);
+ void removeAppManually(QuickButton *button);
+ void removeApp(QString url, bool manuallyRemoved);
+ void removeApp(int index, bool manuallyRemoved);
+ void removeAppManually(int index);
+ void saveConfig();
+ void about();
+
+protected:
+ int findApp(QString url);
+ int findApp(QuickButton *button);
+
+ void mousePressEvent(QMouseEvent *e);
+ void resizeEvent(QResizeEvent*);
+ void dragEnterEvent(QDragEnterEvent *e);
+ void dragLeaveEvent(QDragLeaveEvent *e);
+ void dragMoveEvent(QDragMoveEvent *e);
+ void dropEvent(QDropEvent *e);
+ void refreshContents();
+ void setRefreshEnabled(bool enable);
+ void setConserveSpace(bool conserve_space);
+ void setDragEnabled(bool conserve_space);
+
+ bool conserveSpace() const { return m_manager->conserveSpace(); }
+ bool isDragEnabled() const { return m_settings->dragEnabled(); }
+
+ void buildPopupMenu();
+ void loadConfig();
+
+ void mergeButtons(int index);
+ void clearTempButtons();
+ int dimension() const;
+
+protected slots:
+ void slotConfigure();
+ void slotSettingsDialogChanged();
+ void fillRemoveAppsMenu();
+ void slotOwnServiceExecuted(QString serviceMenuId);
+ void slotAdjustToCurrentPopularity();
+ void slotStickyToggled();
+
+protected:
+ void updateInsertionPosToStatusQuo();
+ void updateStickyHighlightLayer();
+ QuickButton* createButton(QString url);
+ virtual void paintEvent(QPaintEvent* e);
+ virtual void positionChange(Position);
+
+ QPopupMenu *m_popup;
+ QPopupMenu *m_appletPopup;
+ QPopupMenu *m_removeAppsMenu;
+ QuickButtonGroup *m_buttons, *m_newButtons, *m_oldButtons, *m_dragButtons;
+ int m_space, m_border;
+ QSize m_buttonSize;
+ FlowGridManager *m_manager;
+ int m_dropLen, m_dropPos, m_minPanelDim;
+ bool m_dragAccepted, m_refreshEnabled, m_needsSave, m_needsRefresh;
+ std::map<QString, int> m_appOrdering;
+ Prefs* m_settings;
+ KAction *m_configAction;
+ ConfigDlg *m_configDialog;
+ PopularityStatistics* m_popularity;
+ QImage m_stickyHighlightLayer;
+ QTimer *m_saveTimer;
+};
+
+#endif