diff options
Diffstat (limited to 'kicker/applets/launcher')
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 |