diff options
Diffstat (limited to 'src')
34 files changed, 5957 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am new file mode 100644 index 0000000..75007d3 --- /dev/null +++ b/src/Makefile.am @@ -0,0 +1,22 @@ +INCLUDES= $(all_includes) +METASOURCES = AUTO + +kde_module_LTLIBRARIES = tastymenu_panelapplet.la + +tastymenu_panelapplet_la_SOURCES = tastymenu.cpp menu.ui menuhandler.cpp \ + dmctl.cpp dmctl.h tastylistview.cpp misc.cpp misc.h appearance.ui prefs.kcfgc \ + behaviour.ui tastytooltip.cpp tastytooltipwidget.ui tastybutton.cpp +tastymenu_panelapplet_la_LDFLAGS = -version-info 0:0:0 -module $(KDE_PLUGIN) $(all_libraries) +tastymenu_panelapplet_la_LIBADD = $(LIB_KDEUI) $(LIB_KIO) + +tastymenu_DATA = tastymenu.desktop +tastymenudir = $(kde_datadir)/kicker/applets +kde_kcfg_DATA = tastymenu.kcfg + +EXTRA_DIST = pics + +messages: rc.cpp + $(EXTRACTRC) `find . -name \*.ui -o -name \*.rc` > rc.cpp + $(XGETTEXT) *.cpp -o $(podir)/tastymenu.pot +noinst_HEADERS = menuhandler.h tastylistview.h tastytooltip.h buttons.h \ + tastybutton.h diff --git a/src/appearance.ui b/src/appearance.ui new file mode 100644 index 0000000..6e8c409 --- /dev/null +++ b/src/appearance.ui @@ -0,0 +1,608 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>Appearance</class> +<widget class="QWidget"> + <property name="name"> + <cstring>Appearance</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>520</width> + <height>465</height> + </rect> + </property> + <property name="caption"> + <string>Form1</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>Icons</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Applications list</string> + </property> + <property name="alignment"> + <set>AlignBottom</set> + </property> + </widget> + <widget class="QCheckBox" row="12" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_ShowExpander</cstring> + </property> + <property name="text"> + <string>Show tree e&xpanders</string> + </property> + <property name="accel"> + <string>Alt+X</string> + </property> + </widget> + <widget class="QCheckBox" row="13" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_AlwaysCollapsed</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>Alwa&ys collapsed categories</string> + </property> + <property name="accel"> + <string>Alt+Y</string> + </property> + </widget> + <widget class="QLabel" row="10" column="1"> + <property name="name"> + <cstring>textLabel2</cstring> + </property> + <property name="text"> + <string>Hei&ght:</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_MenuHeight</cstring> + </property> + </widget> + <widget class="QLabel" row="5" column="1"> + <property name="name"> + <cstring>textLabel1_4</cstring> + </property> + <property name="text"> + <string>Button label:</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + <spacer row="1" column="0"> + <property name="name"> + <cstring>spacer6</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>79</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QLineEdit" row="7" column="2"> + <property name="name"> + <cstring>kcfg_ToolTipTitle</cstring> + </property> + <property name="whatsThis" stdset="0"> + <string>What text should be displayed in the Kicker button tooltip instead of "Tasty Menu". +Empty means default.</string> + </property> + </widget> + <widget class="QLabel" row="9" column="1"> + <property name="name"> + <cstring>textLabel1_2</cstring> + </property> + <property name="text"> + <string>Wi&dth:</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_MenuWidth</cstring> + </property> + </widget> + <widget class="QLabel" row="7" column="1"> + <property name="name"> + <cstring>textLabel5</cstring> + </property> + <property name="text"> + <string>Tootip title:</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + <widget class="QLabel" row="11" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>ApplicationsList</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Applications list</string> + </property> + <property name="alignment"> + <set>AlignBottom</set> + </property> + </widget> + <widget class="QLabel" row="9" column="3"> + <property name="name"> + <cstring>textLabel3</cstring> + </property> + <property name="text"> + <string>of the screen width</string> + </property> + </widget> + <widget class="QLabel" row="4" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>MenuButton</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Menu button</string> + </property> + <property name="alignment"> + <set>AlignBottom</set> + </property> + </widget> + <widget class="QLabel" row="10" column="3"> + <property name="name"> + <cstring>textLabel4</cstring> + </property> + <property name="text"> + <string>of the screen height</string> + </property> + </widget> + <widget class="QButtonGroup" row="6" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_MenuButtonIconType</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + <property name="title"> + <string></string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>kcfg_Icon</cstring> + </property> + <property name="text"> + <string>&Icon:</string> + </property> + <property name="accel"> + <string>Alt+I</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="KIconButton"> + <property name="name"> + <cstring>MenuButtonIcon</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>What icon should be displayed in the kicker button instead of the KMenu one. +Empty means default.</string> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>kcfg_MenuButtonIcon</cstring> + </property> + <property name="enabled"> + <bool>false</bool> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>kcfg_IconNone</cstring> + </property> + <property name="text"> + <string>N&one</string> + </property> + <property name="accel"> + <string>Alt+O</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLabel" row="8" column="0" rowspan="1" colspan="4"> + <property name="name"> + <cstring>menuSize</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Menu size</string> + </property> + <property name="alignment"> + <set>AlignBottom</set> + </property> + </widget> + <widget class="QLabel" row="2" column="1"> + <property name="name"> + <cstring>textLabel1_3</cstring> + </property> + <property name="text"> + <string>&Action icons size:</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_ActionIconSize</cstring> + </property> + </widget> + <widget class="QCheckBox" row="3" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_DisplaySubText</cstring> + </property> + <property name="text"> + <string>&Display applications descriptions</string> + </property> + <property name="accel"> + <string>Alt+D</string> + </property> + </widget> + <widget class="QButtonGroup" row="5" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>kcfg_MenuButtonLabelType</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>5</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + <property name="title"> + <string></string> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>kcfg_MenuButtonText</cstring> + </property> + <property name="text"> + <string>&Text:</string> + </property> + <property name="accel"> + <string>Alt+T</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QLineEdit"> + <property name="name"> + <cstring>kcfg_MenuButtonLabel</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="whatsThis" stdset="0"> + <string>What label instead of "Menu" should be shown for the Kicker button. +Empty means default.</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>kcfg_MenuButtonNone</cstring> + </property> + <property name="text"> + <string>&None</string> + </property> + <property name="accel"> + <string>Alt+N</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QSpinBox" row="9" column="2"> + <property name="name"> + <cstring>kcfg_MenuWidth</cstring> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="maxValue"> + <number>90</number> + </property> + <property name="minValue"> + <number>10</number> + </property> + <property name="value"> + <number>70</number> + </property> + </widget> + <widget class="QSpinBox" row="10" column="2"> + <property name="name"> + <cstring>kcfg_MenuHeight</cstring> + </property> + <property name="suffix"> + <string>%</string> + </property> + <property name="maxValue"> + <number>90</number> + </property> + <property name="minValue"> + <number>10</number> + </property> + <property name="value"> + <number>70</number> + </property> + </widget> + <widget class="QLabel" row="6" column="1"> + <property name="name"> + <cstring>textLabel2_2</cstring> + </property> + <property name="text"> + <string>Button icon:</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + </widget> + <spacer row="9" column="4" rowspan="2" colspan="1"> + <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>78</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QLayoutWidget" row="1" column="2" rowspan="1" colspan="2"> + <property name="name"> + <cstring>layout1</cstring> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="2"> + <property name="name"> + <cstring>textLabel1_5_3</cstring> + </property> + <property name="text"> + <string>third column:</string> + </property> + </widget> + <widget class="QLabel" row="0" column="1"> + <property name="name"> + <cstring>textLabel1_5_2</cstring> + </property> + <property name="text"> + <string>second column:</string> + </property> + </widget> + <widget class="QSpinBox" row="1" column="0"> + <property name="name"> + <cstring>kcfg_IconSize1</cstring> + </property> + <property name="suffix"> + <string> pixels</string> + </property> + <property name="maxValue"> + <number>48</number> + </property> + <property name="minValue"> + <number>16</number> + </property> + <property name="value"> + <number>22</number> + </property> + </widget> + <widget class="QSpinBox" row="1" column="2"> + <property name="name"> + <cstring>kcfg_IconSize3</cstring> + </property> + <property name="suffix"> + <string> pixels</string> + </property> + <property name="maxValue"> + <number>48</number> + </property> + <property name="minValue"> + <number>16</number> + </property> + <property name="value"> + <number>22</number> + </property> + </widget> + <widget class="QSpinBox" row="1" column="1"> + <property name="name"> + <cstring>kcfg_IconSize2</cstring> + </property> + <property name="suffix"> + <string> pixels</string> + </property> + <property name="maxValue"> + <number>48</number> + </property> + <property name="minValue"> + <number>16</number> + </property> + <property name="value"> + <number>22</number> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>textLabel1_5</cstring> + </property> + <property name="text"> + <string>first column:</string> + </property> + </widget> + </grid> + </widget> + <widget class="QLabel" row="1" column="1"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>Icons sizes:</string> + </property> + <property name="alignment"> + <set>AlignVCenter|AlignRight</set> + </property> + <property name="buddy" stdset="0"> + <cstring>kcfg_IconSize</cstring> + </property> + </widget> + <widget class="QSpinBox" row="2" column="2"> + <property name="name"> + <cstring>kcfg_ActionIconSize</cstring> + </property> + <property name="suffix"> + <string> pixels</string> + </property> + <property name="maxValue"> + <number>48</number> + </property> + <property name="minValue"> + <number>16</number> + </property> + </widget> + </grid> +</widget> +<customwidgets> +</customwidgets> +<connections> + <connection> + <sender>kcfg_MenuButtonText</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_IconNone</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_Icon</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_MenuButtonNone</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_MenuButtonText</sender> + <signal>toggled(bool)</signal> + <receiver>kcfg_MenuButtonLabel</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>kcfg_Icon</sender> + <signal>toggled(bool)</signal> + <receiver>MenuButtonIcon</receiver> + <slot>setEnabled(bool)</slot> + </connection> + <connection> + <sender>MenuButtonIcon</sender> + <signal>iconChanged(QString)</signal> + <receiver>Appearance</receiver> + <slot>iconChanged(QString)</slot> + </connection> +</connections> +<includes> + <include location="local" impldecl="in implementation">appearance.ui.h</include> +</includes> +<slots> + <slot>kcfg_ShowExpander_toggled( bool status )</slot> + <slot>iconChanged( QString icon )</slot> +</slots> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>kicondialog.h</includehint> +</includehints> +</UI> diff --git a/src/appearance.ui.h b/src/appearance.ui.h new file mode 100644 index 0000000..938c41a --- /dev/null +++ b/src/appearance.ui.h @@ -0,0 +1,23 @@ +/**************************************************************************** +** ui.h extension file, included from the uic-generated form implementation. +** +** If you want to add, delete, or rename functions or slots, use +** Qt Designer to update this file, preserving your code. +** +** You should not define a constructor or destructor in this file. +** Instead, write your code in functions called init() and destroy(). +** These will automatically be called by the form's constructor and +** destructor. +*****************************************************************************/ + + +void Appearance::kcfg_ShowExpander_toggled( bool status ) +{ + kcfg_AlwaysCollapsed->setEnabled( status ); +} + + +void Appearance::iconChanged( QString icon ) +{ + kcfg_MenuButtonIcon->setText(icon); +} diff --git a/src/behaviour.ui b/src/behaviour.ui new file mode 100644 index 0000000..b65cd3c --- /dev/null +++ b/src/behaviour.ui @@ -0,0 +1,234 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>Behaviour</class> +<widget class="QWidget"> + <property name="name"> + <cstring>Behaviour</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>516</width> + <height>400</height> + </rect> + </property> + <property name="caption"> + <string>Form1</string> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel" row="0" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>GeneralBehaviour</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>General behaviour</string> + </property> + <property name="alignment"> + <set>AlignBottom</set> + </property> + </widget> + <spacer row="1" column="2"> + <property name="name"> + <cstring>spacer8</cstring> + </property> + <property name="orientation"> + <enum>Horizontal</enum> + </property> + <property name="sizeType"> + <enum>Expanding</enum> + </property> + <property name="sizeHint"> + <size> + <width>31</width> + <height>21</height> + </size> + </property> + </spacer> + <widget class="QCheckBox" row="1" column="1"> + <property name="name"> + <cstring>kcfg_OverrideAltF1</cstring> + </property> + <property name="text"> + <string>&Override alt+F1 keyboard shortcut (requires kicker restart)</string> + </property> + <property name="accel"> + <string>Alt+O</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Pops up Tasty Menu instead of KMenu when the shortcut sequence Alt+F1 is pressed</string> + </property> + </widget> + <spacer row="1" column="0"> + <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>81</width> + <height>20</height> + </size> + </property> + </spacer> + <widget class="QCheckBox" row="2" column="1"> + <property name="name"> + <cstring>kcfg_ShowSaveSession</cstring> + </property> + <property name="text"> + <string>Show save session functionalit&y</string> + </property> + <property name="accel"> + <string>Alt+Y</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Under the menu that pops up when the button with user name is pressed a new item "Save current session" is added. +It saves the current session informations like open applications and windows position. In order to use it you must set "Restore manually saved session" in the Session Manager Kcontrol module.</string> + </property> + </widget> + <widget class="QCheckBox" row="4" column="1"> + <property name="name"> + <cstring>kcfg_HideOneChild</cstring> + </property> + <property name="text"> + <string>&Hide groups with only one application</string> + </property> + <property name="accel"> + <string>Alt+H</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + <widget class="QCheckBox" row="5" column="1"> + <property name="name"> + <cstring>kcfg_Alphabetical</cstring> + </property> + <property name="text"> + <string>Show items in alpha&betical order</string> + </property> + <property name="accel"> + <string>Alt+B</string> + </property> + </widget> + <widget class="QLabel" row="6" column="0" rowspan="1" colspan="2"> + <property name="name"> + <cstring>SearchFieldBehaviour</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>Search field behaviour</string> + </property> + <property name="alignment"> + <set>AlignBottom</set> + </property> + </widget> + <widget class="QButtonGroup" row="7" column="1"> + <property name="name"> + <cstring>buttonGroup1</cstring> + </property> + <property name="frameShape"> + <enum>NoFrame</enum> + </property> + <property name="title"> + <string></string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="QRadioButton"> + <property name="name"> + <cstring>simpleSearch</cstring> + </property> + <property name="text"> + <string>Si&mple searches</string> + </property> + <property name="accel"> + <string>Alt+M</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Filters the applications list</string> + </property> + <property name="whatsThis" stdset="0"> + <string>It displays in the first column all the applications whose name or description matches with the query you entered</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>kcfg_KerryIntegration</cstring> + </property> + <property name="text"> + <string>&Kerry Beagle integration</string> + </property> + <property name="accel"> + <string>Alt+K</string> + </property> + <property name="toolTip" stdset="0"> + <string>Perform queries with Kerry Beagle</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Perform queries with Kerry Beagle instead of using the first column. +It needs Kerry Beagle up and running.</string> + </property> + </widget> + <widget class="QRadioButton"> + <property name="name"> + <cstring>kcfg_StrigiIntegration</cstring> + </property> + <property name="text"> + <string>Stri&gi Integration</string> + </property> + <property name="accel"> + <string>Alt+G</string> + </property> + <property name="toolTip" stdset="0"> + <string>Perform queries with Strigi</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Perform queries with Strigi instead of using the first column. +It needs the Strigi Daemon up and running</string> + </property> + </widget> + </vbox> + </widget> + <widget class="QCheckBox" row="3" column="1"> + <property name="name"> + <cstring>kcfg_NewAppsNotification</cstring> + </property> + <property name="text"> + <string>&Notify recently installed applications (requires kicker restart)</string> + </property> + <property name="accel"> + <string>Alt+N</string> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </grid> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> diff --git a/src/create_buttons.sh b/src/create_buttons.sh new file mode 100644 index 0000000..7f787b2 --- /dev/null +++ b/src/create_buttons.sh @@ -0,0 +1 @@ +uic -o buttons.h -embed buttons pics/attach.png pics/detach.png diff --git a/src/dmctl.cpp b/src/dmctl.cpp new file mode 100644 index 0000000..9f24a52 --- /dev/null +++ b/src/dmctl.cpp @@ -0,0 +1,434 @@ +/* + Copyright (C) 2004 Oswald Buddenhagen <ossi@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the Lesser GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#include "dmctl.h" + +#ifdef Q_WS_X11 + +#include <klocale.h> +#include <dcopclient.h> + +#include <qregexp.h> + +#include <X11/Xauth.h> +#include <X11/Xlib.h> + +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/un.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> + +static enum { Dunno, NoDM, NewKDM, OldKDM, GDM } DMType = Dunno; +static const char *ctl, *dpy; + +DM::DM() : fd( -1 ) +{ + const char *ptr; + struct sockaddr_un sa; + + if (DMType == Dunno) { + if (!(dpy = ::getenv( "DISPLAY" ))) + DMType = NoDM; + else if ((ctl = ::getenv( "DM_CONTROL" ))) + DMType = NewKDM; + else if ((ctl = ::getenv( "XDM_MANAGED" )) && ctl[0] == '/') + DMType = OldKDM; + else if (::getenv( "GDMSESSION" )) + DMType = GDM; + else + DMType = NoDM; + } + switch (DMType) { + default: + return; + case NewKDM: + case GDM: + if ((fd = ::socket( PF_UNIX, SOCK_STREAM, 0 )) < 0) + return; + sa.sun_family = AF_UNIX; + if (DMType == GDM) + strcpy( sa.sun_path, "/tmp/.gdm_socket" ); + else { + if ((ptr = strchr( dpy, ':' ))) + ptr = strchr( ptr, '.' ); + snprintf( sa.sun_path, sizeof(sa.sun_path), + "%s/dmctl-%.*s/socket", + ctl, ptr ? ptr - dpy : 512, dpy ); + } + if (::connect( fd, (struct sockaddr *)&sa, sizeof(sa) )) { + ::close( fd ); + fd = -1; + } + if (DMType == GDM) + GDMAuthenticate(); + break; + case OldKDM: + { + QString tf( ctl ); + tf.truncate( tf.find( ',' ) ); + fd = ::open( tf.latin1(), O_WRONLY ); + } + break; + } +} + +DM::~DM() +{ + if (fd >= 0) + close( fd ); +} + +bool +DM::exec( const char *cmd ) +{ + QCString buf; + + return exec( cmd, buf ); +} + +/** + * Execute a KDM/GDM remote control command. + * @param cmd the command to execute. FIXME: undocumented yet. + * @param buf the result buffer. + * @return result: + * @li If true, the command was successfully executed. + * @p ret might contain addional results. + * @li If false and @p ret is empty, a communication error occurred + * (most probably KDM is not running). + * @li If false and @p ret is non-empty, it contains the error message + * from KDM. + */ +bool +DM::exec( const char *cmd, QCString &buf ) +{ + bool ret = false; + int tl; + unsigned len = 0; + + if (fd < 0) + goto busted; + + if (::write( fd, cmd, (tl = strlen( cmd )) ) != tl) { + bust: + ::close( fd ); + fd = -1; + busted: + buf.resize( 0 ); + return false; + } + if (DMType == OldKDM) { + buf.resize( 0 ); + return true; + } + for (;;) { + if (buf.size() < 128) + buf.resize( 128 ); + else if (buf.size() < len * 2) + buf.resize( len * 2 ); + if ((tl = ::read( fd, buf.data() + len, buf.size() - len)) <= 0) { + if (tl < 0 && errno == EINTR) + continue; + goto bust; + } + len += tl; + if (buf[len - 1] == '\n') { + buf[len - 1] = 0; + if (len > 2 && (buf[0] == 'o' || buf[0] == 'O') && + (buf[1] == 'k' || buf[1] == 'K') && buf[2] <= 32) + ret = true; + break; + } + } + return ret; +} + +bool +DM::canShutdown() +{ + if (DMType == OldKDM) + return strstr( ctl, ",maysd" ) != 0; + + QCString re; + + if (DMType == GDM) + return exec( "QUERY_LOGOUT_ACTION\n", re ) && re.find("HALT") >= 0; + + return exec( "caps\n", re ) && re.find( "\tshutdown" ) >= 0; +} + +void +DM::shutdown( KApplication::ShutdownType shutdownType, + KApplication::ShutdownMode shutdownMode, /* NOT Default */ + const QString &bootOption ) +{ + if (shutdownType == KApplication::ShutdownTypeNone) + return; + + bool cap_ask; + if (DMType == NewKDM) { + QCString re; + cap_ask = exec( "caps\n", re ) && re.find( "\tshutdown ask" ) >= 0; + } else { + if (!bootOption.isEmpty()) + return; + cap_ask = false; + } + if (!cap_ask && shutdownMode == KApplication::ShutdownModeInteractive) + shutdownMode = KApplication::ShutdownModeForceNow; + + QCString cmd; + if (DMType == GDM) { + cmd.append( shutdownMode == KApplication::ShutdownModeForceNow ? + "SET_LOGOUT_ACTION " : "SET_SAFE_LOGOUT_ACTION " ); + cmd.append( shutdownType == KApplication::ShutdownTypeReboot ? + "REBOOT\n" : "HALT\n" ); + } else { + cmd.append( "shutdown\t" ); + cmd.append( shutdownType == KApplication::ShutdownTypeReboot ? + "reboot\t" : "halt\t" ); + if (!bootOption.isNull()) + cmd.append( "=" ).append( bootOption.local8Bit() ).append( "\t" ); + cmd.append( shutdownMode == KApplication::ShutdownModeInteractive ? + "ask\n" : + shutdownMode == KApplication::ShutdownModeForceNow ? + "forcenow\n" : + shutdownMode == KApplication::ShutdownModeTryNow ? + "trynow\n" : "schedule\n" ); + } + exec( cmd.data() ); +} + +bool +DM::bootOptions( QStringList &opts, int &defopt, int ¤t ) +{ + if (DMType != NewKDM) + return false; + + QCString re; + if (!exec( "listbootoptions\n", re )) + return false; + + opts = QStringList::split( '\t', QString::fromLocal8Bit( re.data() ) ); + if (opts.size() < 4) + return false; + + bool ok; + defopt = opts[2].toInt( &ok ); + if (!ok) + return false; + current = opts[3].toInt( &ok ); + if (!ok) + return false; + + opts = QStringList::split( ' ', opts[1] ); + for (QStringList::Iterator it = opts.begin(); it != opts.end(); ++it) + (*it).replace( "\\s", " " ); + + return true; +} + +void +DM::setLock( bool on ) +{ + if (DMType != GDM) + exec( on ? "lock\n" : "unlock\n" ); +} + +bool +DM::isSwitchable() +{ + if (DMType == OldKDM) + return dpy[0] == ':'; + + if (DMType == GDM) + return exec( "QUERY_VT\n" ); + + QCString re; + + return exec( "caps\n", re ) && re.find( "\tlocal" ) >= 0; +} + +int +DM::numReserve() +{ + if (DMType == GDM) + return 1; /* Bleh */ + + if (DMType == OldKDM) + return strstr( ctl, ",rsvd" ) ? 1 : -1; + + QCString re; + int p; + + if (!(exec( "caps\n", re ) && (p = re.find( "\treserve " )) >= 0)) + return -1; + return atoi( re.data() + p + 9 ); +} + +void +DM::startReserve() +{ + if (DMType == GDM) + exec("FLEXI_XSERVER\n"); + else + exec("reserve\n"); +} + +bool +DM::localSessions( SessList &list ) +{ + if (DMType == OldKDM) + return false; + + QCString re; + + if (DMType == GDM) { + if (!exec( "CONSOLE_SERVERS\n", re )) + return false; + QStringList sess = QStringList::split( QChar(';'), re.data() + 3 ); + for (QStringList::ConstIterator it = sess.begin(); it != sess.end(); ++it) { + QStringList ts = QStringList::split( QChar(','), *it, true ); + SessEnt se; + se.display = ts[0]; + se.user = ts[1]; + se.vt = ts[2].toInt(); + se.session = "<unknown>"; + se.self = ts[0] == ::getenv( "DISPLAY" ); /* Bleh */ + se.tty = false; + list.append( se ); + } + } else { + if (!exec( "list\talllocal\n", re )) + return false; + QStringList sess = QStringList::split( QChar('\t'), re.data() + 3 ); + for (QStringList::ConstIterator it = sess.begin(); it != sess.end(); ++it) { + QStringList ts = QStringList::split( QChar(','), *it, true ); + SessEnt se; + se.display = ts[0]; + if (ts[1][0] == '@') + se.from = ts[1].mid( 1 ), se.vt = 0; + else + se.vt = ts[1].mid( 2 ).toInt(); + se.user = ts[2]; + se.session = ts[3]; + se.self = (ts[4].find( '*' ) >= 0); + se.tty = (ts[4].find( 't' ) >= 0); + list.append( se ); + } + } + return true; +} + +void +DM::sess2Str2( const SessEnt &se, QString &user, QString &loc ) +{ + if (se.tty) { + user = i18n("user: ...", "%1: TTY login").arg( se.user ); + loc = se.vt ? QString("vt%1").arg( se.vt ) : se.display ; + } else { + user = + se.user.isEmpty() ? + se.session.isEmpty() ? + i18n("Unused") : + se.session == "<remote>" ? + i18n("X login on remote host") : + i18n("... host", "X login on %1").arg( se.session ) : + se.session == "<unknown>" ? + se.user : + i18n("user: session type", "%1: %2") + .arg( se.user ).arg( se.session ); + loc = + se.vt ? + QString("%1, vt%2").arg( se.display ).arg( se.vt ) : + se.display; + } +} + +QString +DM::sess2Str( const SessEnt &se ) +{ + QString user, loc; + + sess2Str2( se, user, loc ); + return i18n("session (location)", "%1 (%2)").arg( user ).arg( loc ); +} + +bool +DM::switchVT( int vt ) +{ + if (DMType == GDM) + return exec( QString("SET_VT %1\n").arg(vt).latin1() ); + + return exec( QString("activate\tvt%1\n").arg(vt).latin1() ); +} + +void +DM::lockSwitchVT( int vt ) +{ + if (switchVT( vt )) + kapp->dcopClient()->send( "kdesktop", "KScreensaverIface", "lock()", "" ); +} + +void +DM::GDMAuthenticate() +{ + FILE *fp; + const char *dpy, *dnum, *dne; + int dnl; + Xauth *xau; + + dpy = DisplayString( QPaintDevice::x11AppDisplay() ); + if (!dpy) { + dpy = ::getenv( "DISPLAY" ); + if (!dpy) + return; + } + dnum = strchr( dpy, ':' ) + 1; + dne = strchr( dpy, '.' ); + dnl = dne ? dne - dnum : strlen( dnum ); + + /* XXX should do locking */ + if (!(fp = fopen( XauFileName(), "r" ))) + return; + + while ((xau = XauReadAuth( fp ))) { + if (xau->family == FamilyLocal && + xau->number_length == dnl && !memcmp( xau->number, dnum, dnl ) && + xau->data_length == 16 && + xau->name_length == 18 && !memcmp( xau->name, "MIT-MAGIC-COOKIE-1", 18 )) + { + QString cmd( "AUTH_LOCAL " ); + for (int i = 0; i < 16; i++) + cmd += QString::number( (uchar)xau->data[i], 16 ).rightJustify( 2, '0'); + cmd += "\n"; + if (exec( cmd.latin1() )) { + XauDisposeAuth( xau ); + break; + } + } + XauDisposeAuth( xau ); + } + + fclose (fp); +} + +#endif // Q_WS_X11 diff --git a/src/dmctl.h b/src/dmctl.h new file mode 100644 index 0000000..aadc89b --- /dev/null +++ b/src/dmctl.h @@ -0,0 +1,93 @@ +/* + Copyright (C) 2004,2005 Oswald Buddenhagen <ossi@kde.org> + Copyright (C) 2005 Stephan Kulow <coolo@kde.org> + + This program is free software; you can redistribute it and/or + modify it under the terms of the Lesser GNU General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the Lesser GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, USA. +*/ + +#ifndef DMCTL_H +#define DMCTL_H + +#include <kapplication.h> + +struct SessEnt { + QString display, from, user, session; + int vt; + bool self:1, tty:1; +}; + +typedef QValueList<SessEnt> SessList; + +class DM { + +#ifdef Q_WS_X11 + +public: + DM(); + ~DM(); + + bool canShutdown(); + void shutdown( KApplication::ShutdownType shutdownType, + KApplication::ShutdownMode shutdownMode, + const QString &bootOption = QString::null ); + + void setLock( bool on ); + + bool isSwitchable(); + int numReserve(); + void startReserve(); + bool localSessions( SessList &list ); + bool switchVT( int vt ); + void lockSwitchVT( int vt ); + + bool bootOptions( QStringList &opts, int &dflt, int &curr ); + + static QString sess2Str( const SessEnt &se ); + static void sess2Str2( const SessEnt &se, QString &user, QString &loc ); + +private: + int fd; + + bool exec( const char *cmd, QCString &ret ); + bool exec( const char *cmd ); + + void GDMAuthenticate(); + +#else // Q_WS_X11 + +public: + DM() {} + + bool canShutdown() { return false; } + void shutdown( KApplication::ShutdownType shutdownType, + KApplication::ShutdownMode shutdownMode, + const QString &bootOption = QString::null ) {} + + void setLock( bool ) {} + + bool isSwitchable() { return false; } + int numReserve() { return -1; } + void startReserve() {} + bool localSessions( SessList &list ) { return false; } + void switchVT( int vt ) {} + + bool bootOptions( QStringList &opts, int &dflt, int &curr ); + +#endif // Q_WS_X11 + +}; // class DM + +#endif // DMCTL_H diff --git a/src/menu.ui b/src/menu.ui new file mode 100644 index 0000000..f666fd2 --- /dev/null +++ b/src/menu.ui @@ -0,0 +1,379 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>Menu</class> +<widget class="QWidget"> + <property name="name"> + <cstring>Menu</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>538</width> + <height>428</height> + </rect> + </property> + <property name="caption"> + <string>Tasty Menu</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QFrame"> + <property name="name"> + <cstring>frame9</cstring> + </property> + <property name="frameShape"> + <enum>PopupPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QFrame" row="0" column="0" rowspan="2" colspan="1"> + <property name="name"> + <cstring>leftFrame</cstring> + </property> + <property name="maximumSize"> + <size> + <width>200</width> + <height>32767</height> + </size> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Plain</enum> + </property> + <property name="lineWidth"> + <number>0</number> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="midLineWidth"> + <number>0</number> + </property> + <grid> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <widget class="TastyListView" row="2" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>dynamicList</cstring> + </property> + </widget> + <widget class="QPushButton" row="3" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>clearRecentButton</cstring> + </property> + <property name="text"> + <string>&Clear list</string> + </property> + <property name="accel"> + <string>Alt+C</string> + </property> + </widget> + <widget class="QLabel" row="0" column="0"> + <property name="name"> + <cstring>searchLabel</cstring> + </property> + <property name="text"> + <string>Se&arch:</string> + </property> + <property name="textFormat"> + <enum>AutoText</enum> + </property> + <property name="buddy" stdset="0"> + <cstring>searchLine</cstring> + </property> + </widget> + <widget class="QComboBox" row="1" column="1" rowspan="1" colspan="2"> + <item> + <property name="text"> + <string>Favourite applications</string> + </property> + </item> + <item> + <property name="text"> + <string>Most used applications</string> + </property> + </item> + <item> + <property name="text"> + <string>Recent applications</string> + </property> + </item> + <item> + <property name="text"> + <string>Recent documents</string> + </property> + </item> + <property name="name"> + <cstring>menuModes</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="toolTip" stdset="0"> + <string>Use this dropdown menu to change the behaviour of the list below</string> + </property> + </widget> + <widget class="KListViewSearchLine" row="0" column="1"> + <property name="name"> + <cstring>searchLine</cstring> + </property> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + <widget class="QToolButton" row="0" column="2"> + <property name="name"> + <cstring>clearButton</cstring> + </property> + <property name="text"> + <string>...</string> + </property> + </widget> + <widget class="QLabel" row="1" column="0"> + <property name="name"> + <cstring>showLabel</cstring> + </property> + <property name="text"> + <string>&Show:</string> + </property> + <property name="buddy" stdset="0"> + <cstring>menuModes</cstring> + </property> + </widget> + </grid> + </widget> + <widget class="TastyListView" row="1" column="1"> + <property name="name"> + <cstring>rootList</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </widget> + <widget class="TastyListView" row="1" column="2"> + <property name="name"> + <cstring>childList</cstring> + </property> + <property name="minimumSize"> + <size> + <width>0</width> + <height>0</height> + </size> + </property> + </widget> + <widget class="QFrame" row="0" column="1" rowspan="1" colspan="2"> + <property name="name"> + <cstring>allAppsFrame</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>7</hsizetype> + <vsizetype>5</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Sunken</enum> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>2</number> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>textLabel1</cstring> + </property> + <property name="font"> + <font> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string>All Applications</string> + </property> + <property name="alignment"> + <set>AlignCenter</set> + </property> + </widget> + <widget class="QToolButton"> + <property name="name"> + <cstring>detachButton</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="text"> + <string>...</string> + </property> + <property name="autoRaise"> + <bool>true</bool> + </property> + <property name="toolTip" stdset="0"> + <string>Make this menu a normal window</string> + </property> + </widget> + </hbox> + </widget> + <widget class="QLayoutWidget" row="2" column="0" rowspan="1" colspan="3"> + <property name="name"> + <cstring>layout6</cstring> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="KPushButton"> + <property name="name"> + <cstring>runButton</cstring> + </property> + <property name="text"> + <string>Ru&n program...</string> + </property> + <property name="accel"> + <string>Alt+N</string> + </property> + <property name="toolTip" stdset="0"> + <string>If you know exatly how a program is named you can directly enter the application name</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>switchButton</cstring> + </property> + <property name="text"> + <string></string> + </property> + <property name="accel"> + <string></string> + </property> + <property name="toolTip" stdset="0"> + <string>Start a new session, switch the active user or edit your user profile</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>lockButton</cstring> + </property> + <property name="text"> + <string>L&ock Session</string> + </property> + <property name="accel"> + <string>Alt+O</string> + </property> + <property name="whatsThis" stdset="0"> + <string>Lock the pc with a password if you are going away for a while</string> + </property> + </widget> + <widget class="KPushButton"> + <property name="name"> + <cstring>logoutButton</cstring> + </property> + <property name="text"> + <string>Lo&g Out...</string> + </property> + <property name="accel"> + <string>Alt+G</string> + </property> + <property name="toolTip" stdset="0"> + <string>Logout, reboot or shoutdown the computer</string> + </property> + </widget> + </hbox> + </widget> + </grid> + </widget> + </vbox> +</widget> +<customwidgets> + <customwidget> + <class>TastyListView</class> + <header location="local">tastylistview.h</header> + <sizehint> + <width>-1</width> + <height>-1</height> + </sizehint> + <container>0</container> + <sizepolicy> + <hordata>7</hordata> + <verdata>7</verdata> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + <pixmap>image0</pixmap> + </customwidget> +</customwidgets> +<images> + <image name="image0"> + <data format="XBM.GZ" length="79">789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f</data> + </image> +</images> +<tabstops> + <tabstop>searchLine</tabstop> + <tabstop>menuModes</tabstop> + <tabstop>dynamicList</tabstop> + <tabstop>rootList</tabstop> + <tabstop>childList</tabstop> + <tabstop>runButton</tabstop> + <tabstop>switchButton</tabstop> + <tabstop>lockButton</tabstop> + <tabstop>logoutButton</tabstop> +</tabstops> +<layoutdefaults spacing="6" margin="11"/> +<includehints> + <includehint>tastylistview.h</includehint> + <includehint>klistviewsearchline.h</includehint> + <includehint>tastylistview.h</includehint> + <includehint>tastylistview.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> + <includehint>kpushbutton.h</includehint> +</includehints> +</UI> diff --git a/src/menuhandler.cpp b/src/menuhandler.cpp new file mode 100644 index 0000000..06ca863 --- /dev/null +++ b/src/menuhandler.cpp @@ -0,0 +1,1712 @@ + +/*************************************************************************** + * Copyright (C) 2006-2007 by Marco Martin * + * notmart@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public * + * License as published by the Free Software Foundation; * + * either version 2 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include <klocale.h> +#include <kmessagebox.h> +#include <kuser.h> +#include <kstandarddirs.h> +#include <kmimetype.h> +#include <qfocusdata.h> +#include <qstyle.h> +#include <qfile.h> +#include <qcursor.h> +#include <kpopupmenu.h> +#include <kdebug.h> + + +#include "menuhandler.h" +#include "buttons.h" + +MenuHandler::MenuHandler( QWidget *parent, Prefs *prefs, char *name, WFlags fl) + :QFrame(parent, name, fl ), searchMode(false) +{ + + prefSkel = prefs; + + //make a KServiceGroup to iterate the menu entries + KServiceGroup::Ptr service = KServiceGroup::root(); + + //Kicker config + QString kickerConfPath = locate("config", "kickerrc"); + kickerConf = new KConfig(kickerConfPath); + + kickerConfWatch = new KDirWatch( this ); + kickerConfWatch->addFile(kickerConfPath); + connect( kickerConfWatch, SIGNAL(dirty(const QString&)), this, + SLOT(slotModKickerConf()) ); + connect( kickerConfWatch, SIGNAL(dirty(const QString&)), this, + SIGNAL(kickerConfChanged()) ); + + if( _newAppsNotification = prefSkel->newAppsNotification() ) + { + //init the old and new installed list + oldInstalledList = prefSkel->oldInstalledApps(); + loadNewInstalledApps(); + initOldInstalledApps(service); + //save the creation time + prefSkel->setOldInstalledAppsAge(time(0)); + initNewInstalledApps(service); + if( newInstalledList.count() > 0 ) + emit(newApplications(newInstalledList.count())); + prefSkel->setOldInstalledApps(oldInstalledList); + prefSkel->setNewInstalledApps( newInstalledList ); + prefSkel->setNewInstalledAppsTimeStamps( newInstalledTimeStamps ); + } + + + //Main widget initialization + menu = new Menu(this, "tastyMenu"); + + MenuHandlerLayout = new QVBoxLayout( this, 0, 0, "MenuHandlerLayout"); + MenuHandlerLayout->addWidget(menu); + + readConfig(); + setupColumns(); + + //Searchline... + iconLoader = KGlobal::iconLoader(); + QPixmap icon; + if( QApplication::reverseLayout() ) + icon = iconLoader->loadIcon("locationbar_erase", KIcon::Small); + else + icon = iconLoader->loadIcon("clear_left", KIcon::Small); + menu->clearButton->setIconSet(icon); + + connect(menu->clearButton, SIGNAL(clicked()), menu->searchLine, SLOT (clear()) ); + + menu->detachButton->setIconSet(QPixmap(uic_findImage("detach.png"))); + connect(menu->detachButton, SIGNAL(clicked()), this, SLOT (switchWindowMode()) ); + + menu->searchLine->setContextMenuEnabled(false); + + //event filters for keyboard navigation + menu->clearButton->installEventFilter(this); + menu->searchLine->installEventFilter(this); + menu->menuModes->installEventFilter(this); + menu->runButton->installEventFilter(this); + menu->switchButton->installEventFilter(this); + menu->lockButton->installEventFilter(this); + menu->logoutButton->installEventFilter(this); + + //action buttons + icon = iconLoader->loadIcon("exit", KIcon::Toolbar); + menu->logoutButton->setIconSet(icon); + + icon = iconLoader->loadIcon("lock", KIcon::Toolbar); + menu->lockButton->setIconSet(icon); + + icon = iconLoader->loadIcon("run", KIcon::Toolbar); + menu->runButton->setIconSet(icon); + + icon = iconLoader->loadIcon("locationbar_erase", KIcon::Toolbar); + menu->clearRecentButton->setIconSet(icon); + + setCaption("Tasty Menu"); + setIcon(iconLoader->loadIcon("kmenu", KIcon::Panel)); + + //user icon and login + KUser *user = new KUser (); + QString loginName (user->loginName ()); + QImage + userImage (locate + ("data", "/home/" + loginName + "/.face.icon")); + if( !userImage.isNull() ) + { + userImage = userImage.smoothScale (KIcon::SizeSmallMedium, KIcon::SizeSmallMedium); + menu->switchButton->setIconSet(QPixmap(userImage)); + } + menu->switchButton->setText(loginName); + sessionsMenu = new QPopupMenu(); + menu->switchButton->setPopup(sessionsMenu); + + + initializeRecentlyUsed( ); + + populateList( service, menu->rootList, NULL, false ); + + //left/middle mouse button + connect (menu->dynamicList, + SIGNAL (activated(QListViewItem *, const QPoint & , int )), this, + SLOT (dynListClicked(QListViewItem *, const QPoint &, int))); + connect (menu->rootList, + SIGNAL (activated(QListViewItem *, const QPoint & , int )), this, + SLOT (rootListClicked(QListViewItem *, const QPoint &, int))); + connect (menu->childList, + SIGNAL (activated(QListViewItem *, const QPoint & , int )), this, + SLOT (childListClicked(QListViewItem *, const QPoint &, int))); + + //right mouse button + connect (menu->dynamicList, + SIGNAL (contextMenuRequested(QListViewItem *, const QPoint & , int )), this, + SLOT (slotContextMenu(QListViewItem *, const QPoint &, int))); + connect (menu->rootList, + SIGNAL (contextMenuRequested(QListViewItem *, const QPoint & , int )), this, + SLOT (slotContextMenu(QListViewItem *, const QPoint &, int))); + connect (menu->childList, + SIGNAL (contextMenuRequested(QListViewItem *, const QPoint & , int )), this, + SLOT (slotContextMenu(QListViewItem *, const QPoint &, int))); + + //don't open categories on mouseover on childlist + //menu->childList->setEasyOpen( true ); + + connect (menu->clearRecentButton, SIGNAL (clicked()), this, SLOT (clearDynList())); + connect (menu->logoutButton, SIGNAL (clicked()), this, SLOT (doLogout())); + connect (menu->lockButton, SIGNAL (clicked()), this, SLOT (doLock())); + connect (menu->runButton, SIGNAL (clicked()), this, SLOT (runDialog())); + + connect( sessionsMenu, SIGNAL(aboutToShow()), SLOT(slotPopulateSessions()) ); + connect( sessionsMenu, SIGNAL(activated(int)), SLOT(slotSessionActivated(int)) ); + + + connect( menu->menuModes, SIGNAL(activated(int)), SLOT(menuModeChanged(int)) ); + menuModeChanged(_menuMode); +} + + +MenuHandler::~MenuHandler() +{ +} + +void MenuHandler::loadNewInstalledApps() +{ + //Notification for newly installed apps + xdgMenuLister = new KDirLister( ); + KStandardDirs *standardDir=new KStandardDirs(); + QStringList appDirs = standardDir->findDirs("xdgdata-apps", "."); + firstListing = 0; + for(QStringList::Iterator it = appDirs.begin(); it != appDirs.end(); ++it) + { + xdgMenuLister->openURL(*it, true); + /*HACK: links to kde programs are often installed in a kde subdirectory + of one of these, so i duplicate all the entries with entry+"kde/"*/ + QString appDirWithKde = (*it)+"kde/"; + if( QFile::exists(appDirWithKde) ) + { + xdgMenuLister->openURL(appDirWithKde, true); + firstListing++; + } + } + + firstListing += appDirs.count(); + connect( xdgMenuLister, SIGNAL(newItems(const KFileItemList &)), this, + SLOT(slotApplicationsAdded(const KFileItemList &)) ); + connect( xdgMenuLister, SIGNAL(deleteItem( KFileItem *)), this, + SLOT(slotApplicationRemoved()) ); + + xdgMenuWatch = new KDirWatch(this); + xdgMenuWatch->addFile(locateLocal("xdgconf-menu", "applications-kmenuedit.menu")); + //connect with slotApplicationRemoved() because we need to wait a while... + connect( xdgMenuWatch, SIGNAL(dirty(const QString&)), this, + SLOT(slotApplicationRemoved()) ); + + newInstalledList = prefSkel->newInstalledApps(); + newInstalledTimeStamps = prefSkel->newInstalledAppsTimeStamps(); + + //expire old apps + for(uint i=0; i<newInstalledTimeStamps.count(); i++) + { + //127800 seconds = two days + if(time(0) - (uint)newInstalledTimeStamps[i] > 127800 ) + { + newInstalledTimeStamps.remove(newInstalledTimeStamps.at(i)); + newInstalledList.remove(newInstalledList.at(i)); + } + } +} + +void MenuHandler::setupColumns() +{ + menu->dynamicList->header()->hide(); + menu->dynamicList->setResizeMode(QListView::AllColumns); + menu->dynamicList->addColumn("name"); + menu->dynamicList->setShowToolTips(true); + menu->dynamicList->setSortColumn(-1); + menu->dynamicList->header()->setResizeEnabled(false); + menu->dynamicList->setHScrollBarMode(QScrollView::AlwaysOff); + menu->dynamicList->setActionIconSize( _actionIconSize ); + menu->dynamicList->setRootIsDecorated( _showExpander ); + //manage drag'n drop + menu->dynamicList->setAcceptDrops(true); + menu->dynamicList->setDragEnabled(true); + connect( menu->dynamicList, SIGNAL(moved()), + this, SLOT(dynListElemMoved()) ); + + menu->rootList->header()->hide(); + menu->rootList->setResizeMode(QListView::AllColumns); + menu->rootList->addColumn("name"); + menu->rootList->setSortColumn(-1); + menu->rootList->header()->setResizeEnabled(false); + menu->rootList->setHScrollBarMode(QScrollView::AlwaysOff); + menu->rootList->setHighLightGroups( false ); + menu->rootList->setActionIconSize( _actionIconSize ); + menu->rootList->setDragEnabled(true); + + menu->childList->header()->hide(); + menu->childList->setResizeMode(QListView::AllColumns); + menu->childList->addColumn("name"); + menu->childList->setSortColumn(_alphabetical?0:-1); + menu->childList->header()->setResizeEnabled(false); + menu->childList->setHScrollBarMode(QScrollView::AlwaysOff); + menu->childList->setActionIconSize( _actionIconSize ); + menu->childList->setRootIsDecorated( _showExpander ); + menu->childList->setDragEnabled(true); +} + + +void MenuHandler::dynListElemMoved( ) +{ + //very stupid: iterate the entire list and rewrite the favouriteList + favouriteList.clear(); + QListViewItemIterator it( menu->dynamicList ); + + while ( it.current() ) + { + TastyListViewItem *li = dynamic_cast<TastyListViewItem *>(it.current()); + + if( !li ) + return; + + favouriteList.append( li->getDeskopEntryPath() ); + it++; + } + + prefSkel->setFavouriteApps(favouriteList); + prefSkel->writeConfig(); +} + + +bool MenuHandler::eventFilter( QObject *o, QEvent * e ) +{ + if ( e->type() == QEvent::KeyPress ) + { + QKeyEvent *keyEvent = (QKeyEvent *)e; + QFocusData *fData = focusData(); + fData->home(); + + switch( keyEvent->key() ) + { + + case Qt::Key_Up: + if( dynamic_cast<QComboBox *>(o) ) + return false; + fData->prev()->setFocus(); + break; + + case Qt::Key_Down: + { + if( dynamic_cast<QComboBox *>(o) ) + return false; + + //this is a workaround in order to set the focus on the + //right widget when the combobox is disabled + QWidget *nextWidget = fData->next(); + if( nextWidget->isEnabled() ) + nextWidget->setFocus(); + else + fData->next()->setFocus(); + } + break; + + case Qt::Key_Right: + if( dynamic_cast<KListViewSearchLine *>(o) ) + return false; + fData->next()->setFocus(); + break; + + case Qt::Key_Left: + if( dynamic_cast<KListViewSearchLine *>(o) ) + return false; + fData->prev()->setFocus(); + break; + + case Qt::Key_Enter: + case Qt::Key_Return: + { + //only filter enter on search line + if( o != menu->searchLine ) + return false; + + QListViewItem *listItem = NULL; + //execute the only list item when there is exactly one + QListViewItemIterator it( menu->dynamicList, + QListViewItemIterator::Visible); + //listItem = it.current(); + int count = 0; + //ensure the first level have only one result + while ( it.current() ) + { + if( it.current()->childCount() == 0 ) + { + count++; + listItem = it.current(); + } + kdDebug() << "current: " << count << " " << it.current()->text(0); + if( count > 1 ) + return false; + it++; + } + + if( listItem ) + { + dynListClicked(listItem, QPoint(0,0), 0); + menu->searchLine->clear(); + } + + break; + } + + default: + return false; + break; + } + return true; + } + //for some reasons clicking with the right mouse button on the searchline everything crashes... +/* else if( e->type() == QEvent::MouseButtonPress && o == menu->searchLine ) + {return true; + QMouseEvent *mouseEvent = (QMouseEvent *)e; + + if( mouseEvent->button() != QMouseEvent::RightButton ) + return true; + + if( menu->searchLine->text().length() < 4 ) + return true; + else + return false; + }*/ + else + return false; +} + + +void MenuHandler::readConfig() +{ + _menuMode = prefSkel->menuMode(); + if( _menuMode < 0 ) + _menuMode = 0; + menu->menuModes->setCurrentItem(_menuMode); + + _currentCategory = prefSkel->currentCategory(); + + kickerConf->setGroup("menus"); + _numRecentEntries = kickerConf->readNumEntry("NumVisibleEntries", 5); + + _hideOneChild = prefSkel->hideOneChild(); + _alphabetical = prefSkel->alphabetical(); + + favouriteList = prefSkel->favouriteApps(); + if( favouriteList.isEmpty() ) + { + favouriteList.append(locate("xdgdata-apps","kde/konqbrowser.desktop")); + favouriteList.append(locate("xdgdata-apps","kde/KMail.desktop")); + favouriteList.append(locate("xdgdata-apps","kde/Help.desktop")); + } + + _showExpander = prefSkel->showExpander(); + _alwaysCollapsed = prefSkel->alwaysCollapsed(); + + _displaySubText = prefSkel->displaySubText(); + + _iconSize1 = prefSkel->iconSize1(); + if( _iconSize1 < 16 || _iconSize1 > 64 ) + _iconSize1 = 22; + _iconSize2 = prefSkel->iconSize2(); + if( _iconSize2 < 16 || _iconSize2 > 64 ) + _iconSize2 = 22; + _iconSize3 = prefSkel->iconSize3(); + if( _iconSize3 < 16 || _iconSize3 > 64 ) + _iconSize3 = 22; + + _actionIconSize = prefSkel->actionIconSize(); + if( _actionIconSize > _iconSize1 ) + _actionIconSize = _iconSize1; + + //menu size + _menuWidth = 100.0/prefSkel->menuWidth(); + _menuHeight = 100.0/prefSkel->menuHeight(); + QRect r = QDesktopWidget().screenGeometry(this); + int w = (int)(r.width()/_menuWidth); + int h = (int)(r.height()/_menuHeight); + resize(w,h); + + _kerryIntegration = prefSkel->kerryIntegration(); + _strigiIntegration = prefSkel->strigiIntegration(); + + _isNormalWindow = prefSkel->isNormalWindow(); + if( _isNormalWindow ) + { + menu->detachButton->setIconSet(QPixmap(uic_findImage("attach.png"))); + QToolTip::add( menu->detachButton, tr2i18n( "Make this window a popup menu" ) ); + } + else + { + menu->detachButton->setIconSet(QPixmap(uic_findImage("detach.png"))); + QToolTip::add( menu->detachButton, tr2i18n( "Make this menu a normal window" ) ); + } + + // disconnect(menu->searchLine, 0, 0, 0); + if( !_kerryIntegration && !_strigiIntegration ) + { + //menu->searchLine->setListView((KListView *)(menu->dynamicList)); + disconnect(menu->searchLine, SIGNAL(returnPressed( const QString &)), + this, SLOT (kerrySearch( const QString &)) ); + disconnect(menu->searchLine, SIGNAL(returnPressed( const QString &)), + this, SLOT (strigiSearch( const QString &)) ); + connect(menu->searchLine, SIGNAL(textChanged( const QString &)), + this, SLOT (initializeSearch( const QString &)) ); + } + else if( _kerryIntegration ) + { + menu->searchLine->setListView(NULL); + menu->searchLine->setEnabled(true); + disconnect(menu->searchLine, SIGNAL(textChanged( const QString &)), + this, SLOT (initializeSearch( const QString &)) ); + disconnect(menu->searchLine, SIGNAL(returnPressed( const QString &)), + this, SLOT (strigiSearch( const QString &)) ); + connect(menu->searchLine, SIGNAL(returnPressed( const QString &)), + this, SLOT (kerrySearch( const QString &)) ); + menu->searchLine->setContextMenuEnabled(false); + } + else //strigi + { + menu->searchLine->setListView(NULL); + menu->searchLine->setEnabled(true); + disconnect(menu->searchLine, SIGNAL(textChanged( const QString &)), + this, SLOT (initializeSearch( const QString &)) ); + disconnect(menu->searchLine, SIGNAL(returnPressed( const QString &)), + this, SLOT (kerrySearch( const QString &)) ); + connect(menu->searchLine, SIGNAL(returnPressed( const QString &)), + this, SLOT (strigiSearch( const QString &)) ); + menu->searchLine->setContextMenuEnabled(false); + } +} + +void MenuHandler::updateConfig() +{ + readConfig(); + + menu->dynamicList->setActionIconSize( _actionIconSize ); + menu->rootList->setActionIconSize( _actionIconSize ); + menu->childList->setActionIconSize( _actionIconSize ); + + menu->dynamicList->setRootIsDecorated( _showExpander ); + menu->childList->setRootIsDecorated( _showExpander ); + + menuModeChanged( _menuMode ); + KServiceGroup::Ptr service = KServiceGroup::root(); + menu->rootList->clear(); + populateList( service, menu->rootList, NULL, false ); +} + + +void MenuHandler::mousePressEvent( QMouseEvent *e) +{ + if(static_cast<QWidget *>(parent())->hasMouse()) + { + close(); + } + else if(!_isNormalWindow && !(rect().contains(e->pos())) ) + { + hide(); + QTimer::singleShot(200, this,SLOT(close())); + } +} + +void MenuHandler::closeEvent ( QCloseEvent *e) +{ + e=e; + + if( _isNormalWindow ) + { + prefSkel->setNormalWindowWidth(width()); + prefSkel->setNormalWindowHeight(height()); + prefSkel->setNormalWindowX(x()); + prefSkel->setNormalWindowY(y()); + prefSkel->writeConfig(); + } + + //HACK: I wait a little bit to permit closing the menu + // when user clicks on the menu button again + QTimer::singleShot(50, this, SLOT(hide())); + emit(hidden()); +} + +void MenuHandler::popup(QPoint pos) +{ + if(isVisible()) + { + close(); + return; + } + + menu->searchLine->setFocus(); + + int w; + int h; + if( !_isNormalWindow ) + { + QRect r = QDesktopWidget().screenGeometry(this); + w = (int)(r.width()/_menuWidth); + h = (int)(r.height()/_menuHeight); + } + else + { + w = prefSkel->normalWindowWidth(); + h = prefSkel->normalWindowHeight(); + } + + //the only way to make things proportioned + menu->leftFrame->setMaximumWidth( (int)((w-24)/3) ); + menu->allAppsFrame->setMaximumHeight(menu->clearButton->height()); + + if( !_isNormalWindow ) + move(pos); + else + move(prefSkel->normalWindowX(), prefSkel->normalWindowY()); + + resize(w,h); + + show(); +} + +void MenuHandler::initOldInstalledApps(KServiceGroup::Ptr group) +{ + if( !group || !group->isValid() ) + return; + + //expires at 639000 seconds = 10 days + if( !prefSkel->oldInstalledApps().empty() || + (time(0) - (uint)prefSkel->oldInstalledAppsAge() < 639000 ) + ) + return; + + KServiceGroup::List list = group->entries(true, true, true, false); + for (KServiceGroup::List::ConstIterator it = list.begin (); + it != list.end (); it++) + { + KSycocaEntry *p = (*it); + if( p->isType( KST_KServiceGroup ) ) + { + KServiceGroup *g = static_cast < KServiceGroup * > ( p ); + if( g ->childCount() > 0 ) + initOldInstalledApps(g); + } + else + { + KService *s = static_cast < KService * >(p); + + oldInstalledList.append(s->desktopEntryPath()); + } + } +} + +void MenuHandler::initNewInstalledApps(KServiceGroup::Ptr group) +{ + if( !group || !group->isValid() ) + return; + if( oldInstalledList.empty() ) + return; + + KServiceGroup::List list = group->entries(true, true, true, false); + for (KServiceGroup::List::ConstIterator it = list.begin (); + it != list.end (); it++) + { + KSycocaEntry *p = (*it); + if( p->isType( KST_KServiceGroup ) ) + { + KServiceGroup *g = static_cast < KServiceGroup * > ( p ); + if( g ->childCount()>0 ) + initNewInstalledApps(g); + } + else + { + KService *s = static_cast < KService * > ( p ); + + + QString path(s->desktopEntryPath()); + if( oldInstalledList.findIndex(path) == -1 && + newInstalledList.findIndex(path) == -1 ) + { + newInstalledList.append(path); + newInstalledTimeStamps.append(time(0)); + oldInstalledList.append(path); + } + } + } +} + + +bool MenuHandler::searchNewItems(KServiceGroup::Ptr group) +{ + if( !group || !group->isValid() ) + return false; + + if( newInstalledList.count() <= 0 ) + return false; + + KServiceGroup::List list = group->entries(true, true, true, false); + for (KServiceGroup::List::ConstIterator it = list.begin (); + it != list.end (); it++) + { + KSycocaEntry *p = (*it); + if( p->isType( KST_KServiceGroup ) ) + { + KServiceGroup *g = static_cast < KServiceGroup * > ( p ); + if( g ->childCount()<=0 ) + continue; + + if(searchNewItems(g)) + return true; + } + else + { + KService *s = static_cast < KService * >(p); + + + /*kdDebug() << group->relPath() << ": "<< s->desktopEntryPath() + <<(( newInstalledList.findIndex(s->desktopEntryPath()) != -1 ) + ?" present":" not present")<< endl;*/ + if( newInstalledList.findIndex(s->desktopEntryPath()) != -1 ) + return true; + } + } + + return false; +} + +/*KServiceGroup::List MenuHandler::getServiceGroupList( KServiceGroup *serviceGroup ) +{ + QString key(serviceGroup->directoryEntryPath()); + + if( sListMap.contains(key) ) + return sListMap[key]; + else + { + KServiceGroup::List list = serviceGroup->entries(true, true, true, false); + sListMap[key] = list; + return list; + } +}*/ + +void MenuHandler::populateList( KServiceGroup *serviceGroup, TastyListView *listView, + TastyListViewItem *listItemFather, + bool recursive, const QString & query ) +{ + if( !serviceGroup || !serviceGroup->isValid() ) + return; + serviceGroup->setShowEmptyMenu(false); + //KServiceGroup::List list = serviceGroup->entries(true, true, true, false); + + KServiceGroup::List list = serviceGroup->entries(true, true, true, false); + + TastyListViewItem *prevListItem = NULL; + + for (KServiceGroup::List::ConstIterator it = list.begin (); + it != list.end (); it++) + { + KSycocaEntry *p = (*it); + + int iconSize; + if( listView && listView == menu->rootList + || listItemFather && listItemFather->listView() == menu->rootList ) + iconSize = _iconSize2; + else if( listView && listView == menu->childList + || listItemFather && listItemFather->listView() == menu->childList) + iconSize = _iconSize3; + else + iconSize = _iconSize1; + + if( p->isType( KST_KServiceGroup ) ) + { + //KServiceGroup::Ptr g (static_cast < KServiceGroup * >(p)); + KServiceGroup *g = static_cast < KServiceGroup * > ( p ); + if( g ->childCount()<=0 || (g->name().at(0) == '.') ) + continue; + + g->setShowEmptyMenu(false); + + int numChilds = g->childCount(); + //FIXME: useless? + if(numChilds == 0) + continue; + + QPixmap iconPix = iconLoader->loadIcon(g->icon(), KIcon::Toolbar,iconSize); + + if ( iconPix.height () != iconSize) + { + QImage img = iconPix.convertToImage(); + if( !img.isNull() ) + { + img = img.smoothScale ( iconSize, iconSize); + iconPix = QPixmap (img); + } + } + + TastyListViewItem *listItem; + if( listView ) + listItem = new TastyListViewItem( listView, prevListItem, + !g->caption().isEmpty()?g->caption() + :g->name().section('/', -2)); + else + listItem = new TastyListViewItem( listItemFather, prevListItem, + !g->caption().isEmpty()?g->caption() + :g->name().section('/', -2)); + + listItem->setPath( g->relPath() ); + listItem->setType( TastyListViewItem::ServiceGroup ); + //OpenGrup only on menu->rootList + if( listView == menu->rootList ) + listItem->setActionType( TastyListViewItem::OpenGroup ); + else + listItem->setActionType( TastyListViewItem::Collapse ); + + prevListItem = listItem; + + listItem->setPixmap(0, iconPix); + + //avoiding to display empty categories or with only one child (very common in SUSE) + if( (!_hideOneChild && numChilds > 0) || numChilds > 1 ) + { + if( listView ) + { + listView->insertItem( listItem ); + if( _currentCategory == listItem->text(0) && listView == menu->rootList ) + { + listView->setCurrentItem(listItem); + listView->setOpenItem(static_cast<QListViewItem *>(listItem)); + rootListClicked(listItem, QPoint(0,0), 0); + } + if(searchNewItems(g)) + listItem->setHighLight(true); + } + else if(listItemFather) + listItemFather->insertItem( listItem ); + + if( recursive ) + populateList( g, NULL, listItem, true, query); + + //the left column is always open + if( listView == menu->dynamicList || + (listItemFather && listItemFather->listView()==menu->dynamicList)) + listItem->setOpen( true ); + else + listItem->setOpen( !_alwaysCollapsed ); + + if( listItem->getActionType() == TastyListViewItem::Expand && + listItem->isOpen() ) + listItem->setActionType( TastyListViewItem::Collapse ); + else if( listItem->getActionType() == TastyListViewItem::Collapse && + !listItem->isOpen() ) + listItem->setActionType( TastyListViewItem::Expand ); + } + else if( recursive ) + { + if( listView ) + populateList( g, listView, NULL, true, query); + + else if(listItemFather) + populateList( g, NULL, listItemFather, true, query); + + delete listItem; + } + + } + else //The entry is a Service + { + KService *s = static_cast < KService * >(p); + + if( s->name().at(0) == '.' ) + continue; + + const bool isSeparator = s->name() == "separator"; + + + //Name and comment + QString itemName = s->name(); + QString subText= QString(); + if( !isSeparator ) + { + if( s->comment() != NULL && !s->comment().isEmpty() ) + subText = s->comment(); + else if( s->genericName() && !s->genericName().isEmpty() ) + subText = s->genericName(); + } + + //Have we done a query? (this improves query speed) + if( query != NULL + && !itemName.contains(query, false) + && !subText.contains(query, false) ) + continue; + + TastyListViewItem *listItem; + + if( isSeparator ) + { + if( _alphabetical ) + continue; + + if(listView) + { + listItem = new TastyListViewItem( listView, prevListItem, "separator" ); + listView->insertItem( listItem ); + } + else if(listItemFather) + { + listItem = new TastyListViewItem( listItemFather, prevListItem, "separator" ); + listItemFather->insertItem( listItem ); + } + else + return; + + prevListItem = listItem; + listItem->setSelectable(false); + continue; + } + + if( listView ) + listItem = new TastyListViewItem( listView, prevListItem, itemName ); + else + listItem = new TastyListViewItem( listItemFather, prevListItem, itemName ); + + listItem->setDisplaySubText(_displaySubText); + listItem->setPath( serviceGroup->relPath() ); + listItem->setDeskopEntryPath( s->desktopEntryPath() ); + listItem->setMenuId( s->menuId() ); + listItem->setType( TastyListViewItem::Service ); + listItem->setActionType( TastyListViewItem::AddBookMark ); + + listItem->setText(0,itemName); + listItem->setSubText(subText); + + prevListItem = listItem; + + QPixmap iconPix = s->pixmap( KIcon::Toolbar, iconSize ); + if ( iconPix.height () != iconSize) + { + QImage img = iconPix.convertToImage(); + if( !img.isNull() ) + { + img = img.smoothScale ( iconSize, iconSize); + iconPix = QPixmap (img); + } + } + listItem->setPixmap(0, iconPix); + + if( listView ) + { + listView->insertItem( listItem ); + } + else if(listItemFather) + { + listItemFather->insertItem( listItem ); + } + if( newInstalledList.findIndex(s->desktopEntryPath()) != -1 ) + listItem->setHighLight(true); + } + + } + +} + + +void MenuHandler::initializeRecentlyUsed( ) +{ + + recentlyUsedMap.clear(); + moreUsedList.clear(); + + kickerConf->reparseConfiguration(); + kickerConf->setGroup("menus"); + QStringList recentUsedList = QStringList::split(',',kickerConf->readEntry("RecentAppsStat")); + + for (QStringList::ConstIterator it = recentUsedList.begin(); + it != recentUsedList.end(); ++it ) + { + QString item = (*it); + + QString desktopPath = item.section(' ',2,2); + if(desktopPath.isEmpty() || !QFile::exists(desktopPath)) + continue; + + recentlyUsedMap[-item.section(' ',1,1).toULong()] = desktopPath; + moreUsedList.append(desktopPath); + + } + +} + +void MenuHandler::setupDynList( MenuMode mode ) +{ + /*( mode == RecentDocuments + || mode == MoreUsed + || mode == RecentlyUsed )*/ + if( mode != Favourites ) + menu->clearRecentButton->show(); + else + menu->clearRecentButton->hide(); + + currentMenuMode = mode; + menu->dynamicList->setAcceptDrops( mode == Favourites ); +} + +void MenuHandler::fillRecentDocuments( ) +{ + menu->dynamicList->clear(); + setupDynList( RecentDocuments ); + + QStringList recentDocsList = KRecentDocument::recentDocuments(); + + TastyListViewItem *listItem = NULL; + + if( recentDocsList.isEmpty() ) + return; + + for (QStringList::Iterator it = recentDocsList.begin(); + it != recentDocsList.end(); ++it ) + { + KDesktopFile *f= new KDesktopFile(*it, true /* read only */); + + if( !f ) + continue; + + listItem = new TastyListViewItem( menu->dynamicList, listItem, f->readName() ); + listItem->setMultiLinesEnabled(false); + + listItem->setDeskopEntryPath(*it); + listItem->setType( TastyListViewItem::DesktopFile ); + + QPixmap iconPix = iconLoader->loadIcon(f->readIcon(), KIcon::Panel, _iconSize1); + if ( iconPix.height () > _iconSize1) + { + QImage img = iconPix.convertToImage(); + if( !img.isNull() ) + { + img = img.smoothScale ( _iconSize1, _iconSize1); + iconPix = QPixmap (img); + } + } + listItem->setPixmap(0, iconPix); + + menu->dynamicList->insertItem( listItem ); + } + +} + + +void MenuHandler::fillMoreUsed( ) +{ + menu->dynamicList->clear(); + setupDynList( MoreUsed ); + + int iteration = 0; + + TastyListViewItem *listItem = NULL; + + for (QStringList::Iterator it = moreUsedList.begin(); + it != moreUsedList.end(); ++it ) + { + //FIXME: yeah, I know, this is U G L Y :-) + if( iteration++ >= _numRecentEntries ) + break; + + KService::Ptr s = KService::serviceByDesktopPath(*it); + if( !s ) + continue; + + listItem = new TastyListViewItem( menu->dynamicList, listItem, s->name()); + listItem->setSubText(!(s->comment().isEmpty())?s->comment():s->genericName() ); + + listItem->setDeskopEntryPath( s->desktopEntryPath() ); + listItem->setType( TastyListViewItem::Service ); + listItem->setActionType( TastyListViewItem::AddBookMark ); + listItem->setDisplaySubText(_displaySubText); + + + QPixmap iconPix = s->pixmap( KIcon::Toolbar, _iconSize1 ); + if( !iconPix.isNull() ) + { + if ( iconPix.height () != _iconSize1) + { + QImage img = iconPix.convertToImage(); + if( !img.isNull() ) + { + img = img.smoothScale ( _iconSize1, _iconSize1); + iconPix = QPixmap (img); + } + } + listItem->setPixmap(0, iconPix); + } + + menu->dynamicList->insertItem( listItem ); + } + +} + +//FIXME: avoid the ugly code duplication +void MenuHandler::fillRecentlyUsed( ) +{ + menu->dynamicList->clear(); + setupDynList( RecentlyUsed ); + + int iteration = 0; + TastyListViewItem *listItem = NULL; + + for (RecentlyUsedMap::Iterator it = recentlyUsedMap.begin(); + it != recentlyUsedMap.end(); ++it ) + { + //FIXME: yeah, I know, this is U G L Y :-) + if( iteration++ >= _numRecentEntries ) + break; + + KService::Ptr s = KService::serviceByDesktopPath(it.data()); + if( !s ) + continue; + + listItem = new TastyListViewItem( menu->dynamicList, listItem, s->name()); + listItem->setSubText(!(s->comment().isEmpty())?s->comment():s->genericName() ); + + listItem->setDeskopEntryPath( s->desktopEntryPath() ); + listItem->setType( TastyListViewItem::Service ); + listItem->setActionType( TastyListViewItem::AddBookMark ); + listItem->setDisplaySubText(_displaySubText); + + + QPixmap iconPix = s->pixmap( KIcon::Toolbar, _iconSize1 ); + if( !iconPix.isNull() ) + { + if ( iconPix.height () != _iconSize1) + { + QImage img = iconPix.convertToImage(); + if( !img.isNull() ) + { + img = img.smoothScale ( _iconSize1, _iconSize1); + iconPix = QPixmap (img); + } + } + listItem->setPixmap(0, iconPix); + } + + menu->dynamicList->insertItem( listItem ); + } + +} + + +//FIXME: avoid the ugly code duplication +void MenuHandler::fillFavourites( ) +{ + menu->dynamicList->clear(); + setupDynList( Favourites ); + + TastyListViewItem *listItem = NULL; + + for (QStringList::Iterator it = favouriteList.begin(); + it != favouriteList.end(); ++it ) + { + + KService::Ptr s = KService::serviceByDesktopPath(*it); + if( !s ) + continue; + listItem = new TastyListViewItem( menu->dynamicList, listItem, s->name()); + listItem->setSubText(!(s->comment().isEmpty())?s->comment():s->genericName() ); + + listItem->setDeskopEntryPath( s->desktopEntryPath() ); + listItem->setType( TastyListViewItem::Service ); + listItem->setActionType( TastyListViewItem::RemoveBookMark ); + listItem->setDisplaySubText(_displaySubText); + + + QPixmap iconPix = s->pixmap( KIcon::Toolbar, _iconSize1 ); + if ( iconPix.height () > _iconSize1) + { + QImage img = iconPix.convertToImage(); + if( !img.isNull() ) + { + img = img.smoothScale ( _iconSize1, _iconSize1); + iconPix = QPixmap (img); + } + } + listItem->setPixmap(0, iconPix); + + menu->dynamicList->insertItem( listItem ); + } + +} + + + +void MenuHandler::slotModKickerConf() +{ + kickerConf->reparseConfiguration(); + initializeRecentlyUsed( ); + if( currentMenuMode == MoreUsed ) + fillMoreUsed(); + else if( currentMenuMode == RecentlyUsed ) + fillRecentlyUsed(); +} + + +void MenuHandler::slotApplicationsAdded(const KFileItemList & newItems) +{ + + if( firstListing > 0 ) + { + firstListing--; + return; + } + + //avoiding to build a mammoth list... + if( newItems.count() > 15 ) + return; + + for(KFileItemListIterator it(newItems); it.current(); ++it) + { + KFileItem *item = (*it); + QString path(item->url().path()); + + kdDebug() << "new item: " << item->name() << endl; + //ignore items already here + if( oldInstalledList.findIndex(path) == -1 ) + { + newInstalledList.append(path); + newInstalledTimeStamps.append( time(0) ); + oldInstalledList.append(path); + } + } + + prefSkel->setNewInstalledApps( newInstalledList ); + prefSkel->setNewInstalledAppsTimeStamps( newInstalledTimeStamps ); + prefSkel->setOldInstalledApps( oldInstalledList ); + + emit(newApplications(newInstalledList.count())); + + //It's necessary to wait some seconds, otherwise apps won't be listed + //I do it two times for being sure, because I still haven't the clear idea + //where the right files to watch are... + QTimer::singleShot(15000, this, SLOT(slotUpdateApplications())); +} + + +void MenuHandler::slotApplicationRemoved() +{ + QTimer::singleShot(15000, this, SLOT(slotUpdateApplications())); + slotUpdateApplications(); +} + +void MenuHandler::slotUpdateApplications() +{ + KRun::runCommand ("kbuildsycoca"); + prefSkel->writeConfig(); + menu->rootList->clear(); + KServiceGroup::Ptr service = KServiceGroup::root(); + + populateList( service, menu->rootList, NULL, false ); +} + +void MenuHandler::listClicked( TastyListViewItem * listItem, const QPoint & coord ) +{ + if( !listItem ) + return; + + //exit if the user clicked on an empty area + if( coord.y() != 0 && + (listItem->itemPos( )+ listItem->height()) < coord.y() ) + return; + + int x = coord.x(); + + QString servicePath = listItem->getDeskopEntryPath(); + + switch( listItem->getType() ) + { + case TastyListViewItem::Service: + { + //These paranoid checks seems to be needed + if( !listItem->listView() ) + return; + TastyListView *lv = dynamic_cast<TastyListView *>(listItem->listView()); + if( !lv ) + return; + if( x >= (lv->visibleWidth() - lv->getActionIconSpace()) ) + { + switch( listItem->getActionType() ) + { + case TastyListViewItem::AddBookMark: + favouriteList.remove(servicePath); + favouriteList.append(servicePath); + prefSkel->setFavouriteApps(favouriteList); + prefSkel->writeConfig(); + if( menu->menuModes->currentItem() == 0 ) + fillFavourites( ); + return; + + case TastyListViewItem::RemoveBookMark: + favouriteList.remove(servicePath); + prefSkel->setFavouriteApps(favouriteList); + prefSkel->writeConfig(); + if( menu->menuModes->currentItem() == 0 ) + fillFavourites( ); + return; + + default: + break; + } + } + + //if the item was highlighted get it back to normal and remove it from recently installed + listItem->setHighLight(false); + int index=newInstalledList.findIndex(servicePath); + if( index != -1 ) + { + newInstalledList.remove(newInstalledList.at(index)); + newInstalledTimeStamps.remove(newInstalledTimeStamps.at(index)); + prefSkel->setNewInstalledApps( newInstalledList ); + prefSkel->setNewInstalledAppsTimeStamps( newInstalledTimeStamps ); + emit(newApplications(newInstalledList.count())); + //remove the highlight from the rootList, at the moment very costly + slotUpdateApplications(); + } + + int started = KApplication::startServiceByDesktopPath( servicePath ); + if( !started ) + { + DCOPRef kickerKMenuIface ("kicker", "KMenu"); + + kickerKMenuIface.call("slotServiceStartedByStorageId(QString,QString)", "tastymenu", servicePath); + if( (currentMenuMode == MoreUsed + || currentMenuMode == RecentlyUsed) + && !searchMode) + slotModKickerConf(); + if( !_isNormalWindow ) + close(); + } + break; + } + + case TastyListViewItem::ServiceGroup: + { + //open, collapse or expand? + switch( listItem->getActionType() ) + { + case TastyListViewItem::Expand: + //avoiding setOpen when the user clicks on the three handle + if( !listItem->xOnDecoration(x) ) + listItem->setOpen(true); + if(listItem->isOpen()) + listItem->setActionType( TastyListViewItem::Collapse ); + break; + case TastyListViewItem::Collapse: + if( !listItem->xOnDecoration(x) ) + listItem->setOpen(false); + if(!listItem->isOpen()) + listItem->setActionType( TastyListViewItem::Expand ); + break; + case TastyListViewItem::OpenGroup: + default: + { + KServiceGroup::Ptr serviceGroup = KServiceGroup::group( listItem->getPath() ); + if( serviceGroup ) + { + menu->childList->clear(); + populateList( serviceGroup, menu->childList, NULL, true ); + } + break; + } + } + break; + } + + case TastyListViewItem::DesktopFile: + { + KDEDesktopMimeType::run(servicePath, true); + if( !_isNormalWindow ) + close(); + break; + } + + default: + return; + } + +} + +void MenuHandler::dynListClicked( QListViewItem * listItem, const QPoint & coord, int c ) +{ + c=c; + if( !listItem ) + return; + + TastyListViewItem *tastyListItem = dynamic_cast<TastyListViewItem *>(listItem); + if( !tastyListItem ) + return; + + listClicked( static_cast<TastyListViewItem *>(listItem), coord ); +} + +void MenuHandler::childListClicked( QListViewItem * listItem, const QPoint & coord, int c ) +{ + c=c; + if( !listItem ) + return; + + TastyListViewItem *tastyListItem = dynamic_cast<TastyListViewItem *>(listItem); + if( !tastyListItem ) + return; + + /*if( _showExpander && (tastyListItem->getType() == TastyListViewItem::ServiceGroup) ) + { + //FIXME: get the REAL size of the expander + if( coord.x() > 16 ) + listItem->setOpen( !listItem->isOpen() ); + } + else*/ + listClicked( static_cast<TastyListViewItem *>(listItem), coord ); +} + +void MenuHandler::rootListClicked( QListViewItem * listItem, const QPoint & coord, int c ) +{ + c=c; + if( !listItem ) + return; + + TastyListViewItem *tastyListItem = dynamic_cast<TastyListViewItem *>(listItem); + if( !tastyListItem ) + return; + + //don't reload when the current category is clicked (except for the first run) + if( menu->childList->childCount() > 0 && + prefSkel->currentCategory( ) == tastyListItem->text(0) ) + return; + + if( tastyListItem->getType() == TastyListViewItem::ServiceGroup ) + { + prefSkel->setCurrentCategory( tastyListItem->text(0) ); + prefSkel->writeConfig(); + } + + listClicked( tastyListItem, coord ); + if( _alphabetical ) + menu->childList->setSorting(0); +} + +void MenuHandler::slotContextMenu(QListViewItem *listItem, const QPoint &coord, int c) +{ + + if( !listItem ) + return; + TastyListViewItem *tastyListItem = dynamic_cast<TastyListViewItem *>(listItem); + if( !tastyListItem ) + return; + + KPopupMenu menu(this); + + menu.insertTitle(tastyListItem->text(c)); + if( tastyListItem->getPath() != "" ) + { + if( tastyListItem->getType() == TastyListViewItem::ServiceGroup ) + menu.insertItem(SmallIcon("kmenuedit"), i18n("&Edit submenu..."), 1); + else if( tastyListItem->getType() == TastyListViewItem::Service ) + { + menu.insertItem(SmallIcon("kmenuedit"), i18n("&Edit item..."), 1); + menu.insertItem(SmallIcon("desktop"), i18n("&Add to desktop"), 3); + } + } + + if( tastyListItem->getActionType() == TastyListViewItem::AddBookMark ) + menu.insertItem(SmallIcon("bookmark_add"), i18n("&Add to favourite applications"), 2); + else if( tastyListItem->getActionType() == TastyListViewItem::RemoveBookMark ) + menu.insertItem(SmallIcon("remove"), i18n("&Remove from favourite applications"), 2); + + int choice; + if( menu.count() > 1 ) + choice = menu.exec(coord); + else + return; + + switch( choice ) + { + case 1: + KRun::runCommand ("kmenuedit /" + + tastyListItem->getPath() + + " " + + tastyListItem->getMenuId()); + if( !_isNormalWindow ) + close(); + break; + + case 2: + listClicked( tastyListItem, QPoint(tastyListItem->listView()->width(), 0) ); + break; + + case 3: + KRun::runCommand( "cp " + tastyListItem->getDeskopEntryPath() + " ~/Desktop" ); + break; + + default: + break; + } + +} + +void MenuHandler::doLogout() +{ + close(); + DCOPRef kdesktopKDesktopIface ("kdesktop", "KDesktopIface"); + kdesktopKDesktopIface.call ("logout()"); +} + +void MenuHandler::doLock() +{ + hide(); + DCOPRef kdesktopKScreensaverIface ("kdesktop", "KScreensaverIface"); + kdesktopKScreensaverIface.call ("lock()"); + close(); +} + +void MenuHandler::runDialog() +{ + close(); + DCOPRef kdesktopKDesktopIface ("kdesktop", "KDesktopIface"); + kdesktopKDesktopIface.call ("popupExecuteCommand()"); +} + +/*Following functions are from official KDE Kmenu, Copyright 1996-2006 the kicker authors*/ + +void MenuHandler::slotPopulateSessions() +{ + int p = 0; + DM dm; + + sessionsMenu->clear(); + + sessionsMenu->insertItem(SmallIconSet("personal"), i18n("Edit user profile..."), 100 ); + sessionsMenu->insertSeparator(); + + //optional save/restore session entries + if(prefSkel->showSaveSession()) + { + sessionsMenu->insertItem( i18n("Save current session"), 101 ); + } + + if (kapp->authorize("start_new_session") && (p = dm.numReserve()) >= 0) + { + if( kapp->authorize("lock_screen") ) + sessionsMenu->insertItem( i18n("Lock session and start a new one"), 102 ); + sessionsMenu->insertItem(SmallIconSet("fork"), i18n("Start New Session"), 103 ); + if (!p) { + sessionsMenu->setItemEnabled( 101, false ); + sessionsMenu->setItemEnabled( 102, false ); + } + sessionsMenu->insertSeparator(); + } + + SessList sess; + if (dm.localSessions( sess )) + for (SessList::ConstIterator it = sess.begin(); it != sess.end(); ++it) { + int id = sessionsMenu->insertItem( DM::sess2Str( *it ), (*it).vt ); + if (!(*it).vt) + sessionsMenu->setItemEnabled( id, false ); + if ((*it).self) + sessionsMenu->setItemChecked( id, true ); + } +} + +void MenuHandler::slotSessionActivated( int ent ) +{ + close(); + + switch(ent) + { + case 100: + close(); + KRun::runCommand ("kcmshell kcm_useraccount"); + break; + case 101: + { + close(); + DCOPRef ksmserverKsmserver ("ksmserver", "ksmserver"); + ksmserverKsmserver.call ("saveCurrentSession()"); + break; + } + case 102: + doNewSession( true ); + break; + case 103: + doNewSession( false ); + break; + } + + //switch on an other session + if (!sessionsMenu->isItemChecked( ent )) + DM().lockSwitchVT( ent ); +} + +void MenuHandler::doNewSession( bool lock ) +{ + int result = KMessageBox::warningContinueCancel( + kapp->desktop()->screen(kapp->desktop()->screenNumber(menu)), + i18n("<p>You have chosen to open another desktop session.<br>" + "The current session will be hidden " + "and a new login screen will be displayed.<br>" + "An F-key is assigned to each session; " + "F%1 is usually assigned to the first session, " + "F%2 to the second session and so on. " + "You can switch between sessions by pressing " + "Ctrl, Alt and the appropriate F-key at the same time. " + "Additionally, the KDE Panel and Desktop menus have " + "actions for switching between sessions.</p>") + .arg(7).arg(8), + i18n("Warning - New Session"), + KGuiItem(i18n("&Start New Session"), "fork"), + ":confirmNewSession", + KMessageBox::PlainCaption | KMessageBox::Notify); + + if (result==KMessageBox::Cancel) + return; + + if (lock) + doLock(); + + DM().startReserve(); +} + + +void MenuHandler::initializeSearch( const QString & query ) +{ + if( !searchMode && query.length() > 2 ) + { + if( !menu->searchLine->listView() ) + menu->searchLine->setListView((KListView *)(menu->dynamicList)); + searchMode = true; + menu->menuModes->setEnabled(false); + KServiceGroup::Ptr service = KServiceGroup::root(); + menu->dynamicList->clear(); + setCursor(QCursor(Qt::BusyCursor)); + populateList( service, menu->dynamicList, NULL, true, query ); + setCursor(QCursor(Qt::ArrowCursor)); + } + else if( query.length() < 3 ) + { + if( menu->searchLine->listView() ) + { + menu->searchLine->setListView(NULL); + menu->searchLine->setEnabled(true); + menu->searchLine->setFocus(); + menu->searchLine->setContextMenuEnabled(false); + } + searchMode = false; + menu->menuModes->setEnabled(true); + menu->dynamicList->clear(); + menuModeChanged(_menuMode); + } +} + +void MenuHandler::kerrySearch( const QString & query ) +{ + close(); + DCOPRef kdesktopKScreensaverIface ("kerry", "search"); + kdesktopKScreensaverIface.call ("search(QString)", query); +} + +void MenuHandler::strigiSearch( const QString & query ) +{ + close(); + KRun::runCommand ("konqueror strigi:/?q="+query); +} + +void MenuHandler::clearDynList( ) +{ + menu->dynamicList->clear(); + switch( currentMenuMode ) + { + case MoreUsed: + case RecentlyUsed: + { + DCOPRef kickerKMenuIface ("kicker", "kicker"); + kickerKMenuIface.call("clearQuickStartMenu()"); + slotModKickerConf(); + break; + } + case RecentDocuments: + KRecentDocument::clear(); + break; + default: + break; + } +} + +void MenuHandler::menuModeChanged( int index ) +{ + _menuMode = index; + prefSkel->setMenuMode(_menuMode); + + switch(index) + { + case Favourites: + fillFavourites(); + break; + + case MoreUsed: + fillMoreUsed(); + break; + + case RecentlyUsed: + fillRecentlyUsed(); + break; + + case RecentDocuments: + fillRecentDocuments(); + break; + + default: + break; + } +} + + +void MenuHandler::switchWindowMode() +{ + if( !_isNormalWindow /*testWFlags(Qt::WType_Popup)*/ ) + { + _isNormalWindow = true; + hide(); + reparent(static_cast<QWidget *>(parent()),Qt::WType_Dialog, pos(), true); + + menu->detachButton->setIconSet(QPixmap(uic_findImage("attach.png"))); + prefSkel->setIsNormalWindow(true); + } + else + { + hide(); + reparent(static_cast<QWidget *>(parent()), + Qt::WType_Popup|Qt::WNoAutoErase, + pos(), true); + + menu->detachButton->setIconSet(QPixmap(uic_findImage("detach.png"))); + prefSkel->setIsNormalWindow(false); + _isNormalWindow = false; + } + + prefSkel->writeConfig(); +} + +//EOF diff --git a/src/menuhandler.h b/src/menuhandler.h new file mode 100644 index 0000000..9fdf33f --- /dev/null +++ b/src/menuhandler.h @@ -0,0 +1,184 @@ +/*************************************************************************** + * Copyright (C) 2006 by Marco Martin * + * notmart@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public * + * License as published by the Free Software Foundation; * + * either version 2 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef MENUHANDLER_H +#define MENUHANDLER_H + +#include <kservicegroup.h> +#include <kservice.h> +#include <klistbox.h> +#include <klistview.h> +#include <klistviewsearchline.h> +#include <kapplication.h> +#include <kiconloader.h> +#include <kpushbutton.h> +#include <krecentdocument.h> +#include <krun.h> +#include <dcopref.h> +#include <qheader.h> +#include <qimage.h> +#include <qtoolbutton.h> +#include <qpopupmenu.h> +#include <qcombobox.h> +#include <kdirwatch.h> +#include <kdirlister.h> +#include <qlayout.h> +#include <kfileitem.h> + +#include "dmctl.h" +#include "menu.h" +#include "prefs.h" +#include "tastylistview.h" + +/** + @author Marco Martin <notmart@gmail.com> +*/ +class MenuHandler : public QFrame{ + Q_OBJECT + +public: + MenuHandler( QWidget *parent, Prefs *prefs, char *name=0, WFlags fl=WType_TopLevel ); + + ~MenuHandler(); + + void readConfig(); + void popup(QPoint pos); + void updateConfig(); + void focusNextChild(){focusNextPrevChild(true);} + void focusPrevChild(){focusNextPrevChild(false);} + void clearNewInstalledApplications() + {newInstalledList.clear();newInstalledTimeStamps.clear(); + prefSkel->setNewInstalledApps( newInstalledList ); + prefSkel->setNewInstalledAppsTimeStamps( newInstalledTimeStamps );} + +signals: + void newApplications(int); + void kickerConfChanged(); + void hidden(); + +protected: + virtual bool eventFilter( QObject *o, QEvent * e ); + + virtual void closeEvent ( QCloseEvent *e); + virtual void resizeEvent ( QResizeEvent *e) + { QWidget::resizeEvent(e); menu->leftFrame->setMaximumWidth( (int)((width()-24)/3) ); } + virtual void mousePressEvent( QMouseEvent *e); + +private: + typedef QMap<ulong, QString> RecentlyUsedMap; + typedef enum + { + Favourites = 0, + MoreUsed = 1, + RecentlyUsed = 2, + RecentDocuments = 3 + } MenuMode; + + RecentlyUsedMap recentlyUsedMap; + + QStringList moreUsedList, favouriteList; + + Menu * menu; + MenuMode currentMenuMode; + KIconLoader *iconLoader; + QPopupMenu *sessionsMenu; + bool searchMode; + Prefs *prefSkel; + KConfig *kickerConf; + KDirWatch *kickerConfWatch; + //xdgMenuWatch for applications-kmenuedit.menu xdgMenuLister for all the desktop files + KDirWatch *xdgMenuWatch; + KDirLister *xdgMenuLister; + int firstListing; + QStringList oldInstalledList, newInstalledList; + QValueList<int> newInstalledTimeStamps; + + QPixmap bookMarkPix; + QVBoxLayout * MenuHandlerLayout; + +//configuration items + int _menuMode; + QString _currentCategory; + int _numRecentEntries; + int _iconSize1; + int _iconSize2; + int _iconSize3; + int _actionIconSize; + bool _displaySubText; + bool _newAppsNotification; + bool _kerryIntegration; + bool _strigiIntegration; + double _menuWidth; + double _menuHeight; + bool _isNormalWindow; + bool _showExpander; + bool _alwaysCollapsed; + bool _hideOneChild; + bool _alphabetical; + + typedef QMap<QString, KServiceGroup::List> SListMap; + SListMap sListMap; + //KServiceGroup::List getServiceGroupList( KServiceGroup *serviceGroup ); + void setupColumns(); + void loadNewInstalledApps(); + void initOldInstalledApps(KServiceGroup::Ptr group); + void initNewInstalledApps(KServiceGroup::Ptr group); + void populateList( KServiceGroup *serviceGroup, + TastyListView *listView, + TastyListViewItem *listItemFather, + bool recursive, const QString & query = NULL ); + + void listClicked( TastyListViewItem * listItem, const QPoint & coord ); + void fillRecentDocuments( ); + void fillMoreUsed( ); + void fillRecentlyUsed( ); + void fillFavourites( ); + void setupDynList( MenuMode mode ); + //FIXME: this thing is UBER HEAVY + bool searchNewItems(KServiceGroup::Ptr group); + +public slots: + void slotUpdateApplications(); + +private slots: + void dynListElemMoved( ); + void dynListClicked( QListViewItem * listItem, const QPoint & coord, int c ); + void slotContextMenu(QListViewItem *listItem, const QPoint &coord, int c ); + void initializeRecentlyUsed( ); + void slotModKickerConf(); + void slotApplicationsAdded(const KFileItemList & newItems); + void slotApplicationRemoved(); + void childListClicked( QListViewItem * listItem, const QPoint & coord, int c ); + void rootListClicked( QListViewItem * listItem, const QPoint & coord, int c ); + void doLogout(); + void doLock(); + void slotPopulateSessions(); + void slotSessionActivated( int ent ); + void doNewSession( bool lock ); + void runDialog(); + void initializeSearch( const QString & query ); + void kerrySearch( const QString & query ); + void strigiSearch( const QString & query ); + void clearDynList(); + void menuModeChanged( int index ); + void switchWindowMode(); +}; + +#endif diff --git a/src/misc.cpp b/src/misc.cpp new file mode 100644 index 0000000..2f1d30c --- /dev/null +++ b/src/misc.cpp @@ -0,0 +1,38 @@ +/* + * Copyright 2003, Sandro Giessl <ceebx@users.sourceforge.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <qcolor.h> +#include "misc.h" + +QColor alphaBlendColors(const QColor &bgColor, const QColor &fgColor, const int a) +{ + + // normal button... + QRgb rgb = bgColor.rgb(); + QRgb rgb_b = fgColor.rgb(); + int alpha = a; + if(alpha>255) alpha = 255; + if(alpha<0) alpha = 0; + int inv_alpha = 255 - alpha; + + QColor result = QColor( qRgb(qRed(rgb_b)*inv_alpha/255 + qRed(rgb)*alpha/255, + qGreen(rgb_b)*inv_alpha/255 + qGreen(rgb)*alpha/255, + qBlue(rgb_b)*inv_alpha/255 + qBlue(rgb)*alpha/255) ); + + return result; +} diff --git a/src/misc.h b/src/misc.h new file mode 100644 index 0000000..835c277 --- /dev/null +++ b/src/misc.h @@ -0,0 +1,24 @@ +/* + * Copyright 2003, Sandro Giessl <ceebx@users.sourceforge.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License version 2 as published by the Free Software Foundation. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#ifndef __MISC_H +#define __MISC_H + +QColor alphaBlendColors(const QColor &backgroundColor, const QColor &foregroundColor, const int alpha); + +#endif // __MISC_H diff --git a/src/pics/.svn/all-wcprops b/src/pics/.svn/all-wcprops new file mode 100644 index 0000000..771d36b --- /dev/null +++ b/src/pics/.svn/all-wcprops @@ -0,0 +1,17 @@ +K 25 +svn:wc:ra_dav:version-url +V 30 +/svn/!svn/ver/2/trunk/src/pics +END +attach.png +K 25 +svn:wc:ra_dav:version-url +V 41 +/svn/!svn/ver/2/trunk/src/pics/attach.png +END +detach.png +K 25 +svn:wc:ra_dav:version-url +V 41 +/svn/!svn/ver/2/trunk/src/pics/detach.png +END diff --git a/src/pics/.svn/entries b/src/pics/.svn/entries new file mode 100644 index 0000000..257243f --- /dev/null +++ b/src/pics/.svn/entries @@ -0,0 +1,42 @@ +9 + +dir +2 +https://tastymenu.googlecode.com/svn/trunk/src/pics +https://tastymenu.googlecode.com/svn + + + +2007-05-27T14:44:26.685461Z +2 +notmart + + +svn:special svn:externals svn:needs-lock + +attach.png +file + + + + +2006-07-28T13:14:20.000000Z +ed780d9ddd2ed20aaa4b9fa7a5c9ecec +2007-05-27T14:44:26.685461Z +2 +notmart +has-props + +detach.png +file + + + + +2006-07-28T13:14:01.000000Z +f7ea9ad2adbf18c94d8fa4badc7e07db +2007-05-27T14:44:26.685461Z +2 +notmart +has-props + diff --git a/src/pics/.svn/format b/src/pics/.svn/format new file mode 100644 index 0000000..ec63514 --- /dev/null +++ b/src/pics/.svn/format @@ -0,0 +1 @@ +9 diff --git a/src/pics/.svn/prop-base/attach.png.svn-base b/src/pics/.svn/prop-base/attach.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/src/pics/.svn/prop-base/attach.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/src/pics/.svn/prop-base/detach.png.svn-base b/src/pics/.svn/prop-base/detach.png.svn-base new file mode 100644 index 0000000..5e9587e --- /dev/null +++ b/src/pics/.svn/prop-base/detach.png.svn-base @@ -0,0 +1,5 @@ +K 13 +svn:mime-type +V 24 +application/octet-stream +END diff --git a/src/pics/.svn/text-base/attach.png.svn-base b/src/pics/.svn/text-base/attach.png.svn-base Binary files differnew file mode 100644 index 0000000..9a2382f --- /dev/null +++ b/src/pics/.svn/text-base/attach.png.svn-base diff --git a/src/pics/.svn/text-base/detach.png.svn-base b/src/pics/.svn/text-base/detach.png.svn-base Binary files differnew file mode 100644 index 0000000..53986e4 --- /dev/null +++ b/src/pics/.svn/text-base/detach.png.svn-base diff --git a/src/pics/attach.png b/src/pics/attach.png Binary files differnew file mode 100644 index 0000000..9a2382f --- /dev/null +++ b/src/pics/attach.png diff --git a/src/pics/detach.png b/src/pics/detach.png Binary files differnew file mode 100644 index 0000000..53986e4 --- /dev/null +++ b/src/pics/detach.png diff --git a/src/prefs.kcfgc b/src/prefs.kcfgc new file mode 100644 index 0000000..b996f04 --- /dev/null +++ b/src/prefs.kcfgc @@ -0,0 +1,6 @@ +# Code generation options for kconfig_compiler +File=tastymenu.kcfg +ClassName=Prefs +Singleton=false +Mutators=true + diff --git a/src/tastybutton.cpp b/src/tastybutton.cpp new file mode 100644 index 0000000..9f89c05 --- /dev/null +++ b/src/tastybutton.cpp @@ -0,0 +1,116 @@ +/*************************************************************************** + * Copyright (C) 2006 by Marco Martin * + * notmart@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "tastybutton.h" + +#include <qimage.h> +#include <qpainter.h> +#include <qpalette.h> +#include <qcolor.h> +#include <kimageeffect.h> + +TastyButton::TastyButton(QWidget *parent) + : QToolButton(parent) +{ + iconEffect = new KIconEffect(); + iconEffect->init(); +} + + +TastyButton::~TastyButton() +{ +} + +void TastyButton::drawButton( QPainter * p ) +{ + //background image or pseudo transparency + if( parentWidget()->erasePixmap() ) + { + const QPixmap erasePix(size()); + QPainter buffPainter(&erasePix); + buffPainter.drawPixmap(QPoint(0, 0), *parentWidget()->erasePixmap(), geometry()); + buffPainter.end(); + + QImage eraseImg = erasePix.convertToImage(); + if( isDown() ) + { + KImageEffect::fade(eraseImg, 0.25, black); + p->drawPixmap(rect(), eraseImg ); + } + else if( uses3D() ) + { + KImageEffect::fade(eraseImg, 0.4, white); + p->drawPixmap(rect(), eraseImg ); + } + else + p->drawPixmap(rect(), erasePix ); + + //lightScore = 0 grey, >0 lighter <0 darker + int lightScore = 0; + //chacks the brightness of 10 pixels of the diagonal + //we won't be 100% sure if this reflects the global brightess of the image + //but it seems to work well + int x = 0; + int y = 0; + while( x < eraseImg.width() && y < eraseImg.height() && x < 10 ) + { + x=++y; + //deciding the text color to use + int h,s,v; + QColor(eraseImg.pixel(x,y)).getHsv( h, s, v ); + // is purely empirical :) + if( v > 140) lightScore++; + else lightScore--; + } + if( lightScore < 0 ) + setPaletteForegroundColor(white); + else + setPaletteForegroundColor(black); + } + //simple background color + else + { + if( isDown() ) + { + p->fillRect(rect(), colorGroup().background().dark(120)); + } + else if( uses3D() ) + { + p->fillRect(rect(), colorGroup().background().light(120)); + } + else + { + p->fillRect(rect(), colorGroup().background()); + } + } + + drawButtonLabel(p); +} + +void TastyButton::setIconSet( QIconSet is ) +{ + is.setPixmap(iconEffect->apply(is.pixmap(), + (int)KIcon::Panel, + (int)KIcon::ActiveState), + QIconSet::Automatic, + QIconSet::Active); + + QToolButton::setIconSet( is ); +} + diff --git a/src/tastybutton.h b/src/tastybutton.h new file mode 100644 index 0000000..4fa0d89 --- /dev/null +++ b/src/tastybutton.h @@ -0,0 +1,49 @@ +/*************************************************************************** + * Copyright (C) 2006 by Marco Martin * + * notmart@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef TASTYBUTTON_H +#define TASTYBUTTON_H + +#include <kiconeffect.h> +#include <kicontheme.h> +#include <qiconset.h> +#include <qtoolbutton.h> + +/** + @author Marco Martin <notmart@gmail.com> +*/ +class TastyButton : public QToolButton +{ +public: + TastyButton(QWidget *parent); + + ~TastyButton(); + + void setIconSet( QIconSet is ); + +protected: + void drawButton( QPainter * p ); + + private: + QPixmap highLightedIcon; + KIconEffect *iconEffect; + +}; + +#endif diff --git a/src/tastylistview.cpp b/src/tastylistview.cpp new file mode 100644 index 0000000..ed5e2a7 --- /dev/null +++ b/src/tastylistview.cpp @@ -0,0 +1,600 @@ +/*************************************************************************** + * Copyright (C) 2006 by Marco Martin * + * notmart@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public * + * License as published by the Free Software Foundation; * + * either version 2 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#include "tastylistview.h" +#include "misc.h" + +#include <kdeversion.h> +#include <kstringhandler.h> +#include <qimage.h> +#include <qpainter.h> +#include <qheader.h> +#include <klocale.h> +#include <kurldrag.h> +#include <qapplication.h> + + +TastyListView::TastyListView( QWidget * parent, const char * name) + : KListView(parent, name), highLightGroups(true), easyOpen(true) +{ + + onItemTimer = new QTimer(this, "onItemTimer"); + underCursorItem = openItem = NULL; + mouseDown = false; + actionIconSize = 16; + actionIconSpace = 32; + + listItemTip = new TastyListViewToolTip(viewport(), this); + + connect(this, SIGNAL(onItem(QListViewItem *) ), SLOT(slotOnItem(QListViewItem *) ) ); + connect(onItemTimer, SIGNAL(timeout()), this, SLOT(slotTimeout()) ); +} + + +TastyListView::~TastyListView() +{ +} + +void TastyListView::startDrag() +{ + if( !currentItem() ) + return; + TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem()); + if( !item ) + return; + + QDragObject *d = new KURLDrag( KURL(item->getDeskopEntryPath()) , viewport() ); + if(!d) + return; + + if (d->drag() && d->target() != viewport()) + emit moved(); +} + +void TastyListView::contentsMouseReleaseEvent( QMouseEvent * e ) +{ + int x = e->x(); + if( x > width() || x < 0) + return; + + if( !currentItem() ) + return; + TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem()); + if( !item ) + return; + + if( e->button() == RightButton ) + emit(contextMenuRequested( currentItem(), e->globalPos(), 0) ); + else + emit(activated( currentItem(), QPoint(x, e->y()), 0) ); + + if(item && (item->getType() == TastyListViewItem::ServiceGroup)) + { + if( !openItem ) + { + openItem = currentItem(); + return; + } + + TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem); + openItem = currentItem(); + if( !oldOpenItem || !oldOpenItem->listView() ) + return; + + oldOpenItem->repaint(); + } + KListView::contentsMouseReleaseEvent(e); +} + +void TastyListView::contentsMouseMoveEvent( QMouseEvent * e ) +{ + KListView::contentsMouseMoveEvent(e); + mouseDown = (e->state() & Qt::LeftButton); + + if( itemAt( contentsToViewport(QPoint(e->x(), e->y()))) == 0 ) + underCursorItem = NULL; +} + +void TastyListView::leaveEvent( QEvent * e ) +{ + KListView::leaveEvent( e ); + onItemTimer->stop(); + if( openItem ) + setCurrentItem( openItem ); +} + + +void TastyListView::keyPressEvent( QKeyEvent * e ) +{ + + switch(e->key()) + { + case Qt::Key_Enter: + case Qt::Key_Return: + case Qt::Key_Space: + { + emit(activated(currentItem(), QPoint(0,0), 0)); + if(!currentItem()) + return; + TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem()); + if(item && item->getType() == TastyListViewItem::ServiceGroup) + { + if( !openItem ) + { + openItem = currentItem(); + return; + } + TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem); + openItem = currentItem(); + if( !oldOpenItem || !oldOpenItem->listView() ) + return; + + oldOpenItem->repaint(); + } + } + break; + + case Qt::Key_Up: + case Qt::Key_Down: + KListView::keyPressEvent( e ); + break; + + case Qt::Key_Right: + { + if(!currentItem()) + return; + TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem()); + if(item && easyOpen && !QApplication::reverseLayout() && item->getType() == TastyListViewItem::ServiceGroup) + { + emit( activated( currentItem(), QPoint(0,0), 0)); + if( !openItem ) + { + openItem = currentItem(); + return; + } + TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem); + openItem = currentItem(); + if( !oldOpenItem || !oldOpenItem->listView() ) + return; + + oldOpenItem->repaint(); + } + focusNextPrevChild(!QApplication::reverseLayout()); + break; + } + case Qt::Key_Left: + { + if( !currentItem() ) + return; + TastyListViewItem *item = dynamic_cast<TastyListViewItem *>(currentItem()); + if(item && easyOpen && QApplication::reverseLayout() && item->getType() == TastyListViewItem::ServiceGroup) + { + emit( activated( currentItem(), QPoint(0,0), 0)); + if( !openItem ) + { + openItem = currentItem(); + return; + } + TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem); + openItem = currentItem(); + if( !oldOpenItem || !oldOpenItem->listView() ) + return; + + oldOpenItem->repaint(); + } + focusNextPrevChild(QApplication::reverseLayout()); + break; + } + case Qt::Key_Tab: + KListView::keyPressEvent( e ); + break; + + default: + break; + } +} + + +void TastyListView::slotOnItem( QListViewItem * listItem ) +{ + if( !listItem || listItem->listView() != this ) + return; + + if( listItem != underCursorItem ) + { + underCursorItem = listItem; + setCurrentItem(listItem); + if(mouseDown) + onItemTimer->start(250, true); + else + onItemTimer->start(1000, true); + } +} + +void TastyListView::slotTimeout( ) +{ + if( !underCursorItem /*|| !openItem*/ ) + return; + + TastyListViewItem *tastyUnderCursorItem = dynamic_cast<TastyListViewItem *>(underCursorItem); + + if( easyOpen && tastyUnderCursorItem && + tastyUnderCursorItem->getType() == TastyListViewItem::ServiceGroup ) + { + emit(activated(underCursorItem, QPoint(underCursorItem->listView()->width()/2,1), 0)); + TastyListViewItem *oldOpenItem = dynamic_cast<TastyListViewItem *>(openItem); + openItem = currentItem(); + if( !oldOpenItem || !oldOpenItem->listView() ) + return; + + oldOpenItem->repaint(); + } +} + +///////////TASTYLISTVIEWTOOLTIP +TastyListViewToolTip::TastyListViewToolTip( QWidget *parent, TastyListView *tListView ) + : QToolTip( parent ), listView( tListView ) +{ +} + +void TastyListViewToolTip::maybeTip( const QPoint &pos ) +{ + if( !parentWidget() || !listView || !listView->showToolTips() ) + return; + + TastyListViewItem *item = static_cast<TastyListViewItem *>(listView->itemAt( pos )); + QPoint contentsPos = listView->viewportToContents( pos ); + if( !item || !listView->columns() ) + return; + + int actionWidth = 0; + TastyListViewItem::ActionType actionType = item->getActionType(); + if( actionType != TastyListViewItem::NoAction ) + actionWidth = listView->getActionIconSpace(); + + int column = listView->header()->sectionAt( contentsPos.x() ); + + + QRect r = listView->itemRect( item ); + int headerPos = listView->header()->sectionPos( column ); + r.setLeft( headerPos ); + r.setRight( headerPos + listView->header()->sectionSize( column ) ); + + int actionLeft = r.right()-actionWidth; + if( pos.x() >= actionLeft ) + { + r.setLeft( actionLeft ); + switch( actionType ) + { + case TastyListViewItem::AddBookMark: + tip( r, i18n( "Add" )+" \""+item->text( column )+"\" "+i18n( "to your favourite applications" ) ); + return; + + case TastyListViewItem::RemoveBookMark: + tip( r, i18n( "Remove" )+" \""+item->text( column )+"\" "+i18n( "from your favourite applications" ) ); + return; + + case TastyListViewItem::OpenGroup: + tip( r, i18n( "Browse" )+" \""+item->text( column )+"\"" ); + return; + + case TastyListViewItem::Expand: + tip( r, i18n( "Expand" )+" \""+item->text( column )+"\"" ); + return; + + case TastyListViewItem::Collapse: + tip( r, i18n( "Collapse" )+" \""+item->text( column )+"\"" ); + return; + + default: + break; + } + } + else if( actionType == TastyListViewItem::OpenGroup && !item->hasEllipsis() ) + { + tip( r, i18n( "Browse" )+" \""+item->text( column )+"\"" ); + return; + } + + if( !item->hasEllipsis() ) + return; + tip( r, item->text( column )+"\n"+item->getSubText() ); +} + + +///////////TASTYLISTVIEWITEM + +TastyListViewItem::TastyListViewItem( TastyListView * parent ) + : KListViewItem(parent) +{commonConstructor();} + +TastyListViewItem::TastyListViewItem( TastyListViewItem * parent ) + : KListViewItem(parent) +{commonConstructor();} + +TastyListViewItem::TastyListViewItem( TastyListView * parent, TastyListViewItem * after, QString label1 ) + : KListViewItem(parent, after, label1) +{commonConstructor();cellText = label1;} + +TastyListViewItem::TastyListViewItem( TastyListViewItem * parent, TastyListViewItem * after, QString label1 ) + : KListViewItem(parent, after, label1) +{commonConstructor();cellText = label1;} + +TastyListViewItem::TastyListViewItem( TastyListView * parent, QString label1 ) + : KListViewItem(parent, label1) +{commonConstructor();cellText = label1;} + +TastyListViewItem::TastyListViewItem( TastyListViewItem * parent, QString label1 ) + : KListViewItem(parent, label1) +{commonConstructor();cellText = label1;} + +TastyListViewItem::~TastyListViewItem() +{ +} + +void TastyListViewItem::commonConstructor() +{ + + subText="";cellText=""; + + actionType = NoAction; + + actionPix = QPixmap(); + + menuId = QString(); + desktopEntryPath = QString(); + path = QString(); + + ellipsis = false; + highLight = false; + displaySubText = true; + +} + +void TastyListViewItem::loadPixmap() +{ + QString iconFile = ""; + iconLoader = KGlobal::iconLoader(); + + if( !listView() ) + return; + + TastyListView *lv = dynamic_cast<TastyListView *>(listView()); + if( !lv ) + return; + + switch( actionType ) + { + case AddBookMark: + actionPix = iconLoader->loadIcon("bookmark_add", KIcon::Small, lv->getActionIconSize()); + break; + + case RemoveBookMark: + actionPix = iconLoader->loadIcon("remove", KIcon::Small, lv->getActionIconSize()); + break; + + case OpenGroup: + if( QApplication::reverseLayout() ) + actionPix = iconLoader->loadIcon("1leftarrow", KIcon::Small, lv->getActionIconSize()); + else + actionPix = iconLoader->loadIcon("1rightarrow", KIcon::Small, lv->getActionIconSize()); + break; + + case Expand: + actionPix = iconLoader->loadIcon("1downarrow", KIcon::Small, lv->getActionIconSize()); + break; + + case Collapse: + actionPix = iconLoader->loadIcon("1uparrow", KIcon::Small, lv->getActionIconSize()); + break; + + default: + return; + } + + + if ( actionPix.height () > lv->getActionIconSize()) + { + QImage img = actionPix.convertToImage(); + if( !img.isNull() ) + { + img = img.smoothScale ( lv->getActionIconSize(), lv->getActionIconSize()); + actionPix = QPixmap (img); + } + } +} + +QString TastyListViewItem::key( int column, bool ascending ) const +{ + ascending = ascending; + QString prefix; + + //ensure all the categories are before the leaf items + if( itemType == ServiceGroup ) + prefix = "0"; + else + prefix = "1"; + return prefix.append(text( column )); +} + +void TastyListViewItem::setup ( ) +{ + //KListViewItem::setup(); + + //calculate listitem height + QFontMetrics fm( listView()->font() ); + + int pixmapHeight = 5; + if( pixmap(0) ) + pixmapHeight = pixmap(0)->height()+4; + + if( displaySubText && !subText.isEmpty() ) + { + int textHeight = (int)(fm.height()*2.5); + setHeight( (pixmapHeight > textHeight) ? pixmapHeight : textHeight ); + } + else + setHeight( pixmapHeight ); +} + + +/*Adapted from Amarok's Statistic listView Copyright (c) 2006 Seb Ruiz*/ +void TastyListViewItem::paintCell ( QPainter * p, const QColorGroup & cg, int column, int width, int align ) +{ + int textHeight = height(); + QString name = cellText; + + int textX = 0; + + QColor fillColor, textColor; + +# if KDE_VERSION < KDE_MAKE_VERSION(3,3,91) +# define BackgroundColor backgroundColor() +# else +# define BackgroundColor backgroundColor(0) +# endif + + + fillColor = isSelected() ? cg.highlight() : BackgroundColor; + + textColor = isSelected() ? cg.highlightedText() : cg.text(); + + if( !listView() ) + return; + TastyListView *lv = dynamic_cast<TastyListView *>( listView() ); + if( !lv ) + return; + + QFont font( lv->font() ); + + if( !isSelected() && (lv->getOpenItem() == this|| + (lv->getHighLightGroups() && itemType == ServiceGroup)) ) + fillColor = alphaBlendColors( fillColor, cg.highlight(), 200); + + else if( !isSelected() && highLight ) + { + int hue, saturation, value; + cg.highlight().getHsv(&hue, &saturation, &value); + //calculate the inverse color 128 means rotating the spectral value by 180 degrees + fillColor.setHsv( (hue+128)%256, saturation/2, value ); + } + + else if( isSelected() && !lv->hasFocus() ) + fillColor = alphaBlendColors( fillColor, BackgroundColor, 150); + + QFontMetrics fm( font ); + widthChanged(column); + + QPixmap buffer(width*2, textHeight); + + if( buffer.isNull() ) + return; + + buffer.fill( fillColor ); + + QPainter pBuf(&buffer); + + + if( pixmap( column ) ) + { + int y = (textHeight - pixmap(column)->height())/2; + pBuf.drawPixmap( 0, y, *pixmap(column) ); + textX += pixmap(column)->width() + 4; + } + + //Calculate the ellipsis for the MAIN text + int extraSpace = fm.width("...") + textX + lv->getActionIconSpace(); + ellipsis = false; + while( (fm.width(name)+extraSpace) > width && name.length() > 4) + { + name.truncate(name.length()-1); + ellipsis = true; + } + if( ellipsis ) + name.append("..."); + + + if( name == "separator" ) + { + int y = textHeight/2; + pBuf.setPen(cg.background().dark(140)); + pBuf.drawLine(textX, y, width, y); + pBuf.setPen(textColor); + pBuf.end(); + p->drawPixmap( 0, 0, buffer ); + return; + } + + if( fm.width( name ) + textX + lv->itemMargin()*2 > width ) + { + const int _width = width - textX - lv->itemMargin()*2; + name = KStringHandler::rPixelSqueeze( name, pBuf.fontMetrics(), _width ); + } + + pBuf.setPen(textColor); + pBuf.drawText( textX, 3, width, textHeight, AlignTop, name ); + + if( displaySubText && !subText.isEmpty() ) + { + font.setPointSize( max((int)(font.pointSize()/1.2), 7) ); + pBuf.setFont( font ); + + QString subTextCopy = subText; + QFontMetrics sfm( font ); + + //Calculate the ellipsis for the subtext + int extraSpace = fm.width("...") + textX + lv->getActionIconSpace(); + bool ellipsisSubText = false; + while( (sfm.width(subTextCopy)+extraSpace) > width && subTextCopy.length() > 4) + { + subTextCopy.truncate(subTextCopy.length()-1); + ellipsisSubText = true; + } + if( ellipsisSubText ) + { + subTextCopy.append("..."); + ellipsis = true; + } + + pBuf.setPen(cg.background().dark(140)); + pBuf.drawLine(textX, fm.height() + 3, width-textX-5, fm.height() + 3); + pBuf.setPen(textColor.light(130)); + pBuf.drawText( textX, fm.height() + 4, width, fm.height(), AlignTop, subTextCopy ); + } + + if( !actionPix.isNull() && + (actionType == OpenGroup || + actionType == Expand || + actionType == Collapse || + lv->currentItem() == this) ) + { + int y = (textHeight - actionPix.height())/2; + pBuf.drawPixmap( width-(actionPix.width()+5), y, actionPix ); + } + + + pBuf.end(); + p->drawPixmap( 0, 0, buffer ); +} + + + +//EOF diff --git a/src/tastylistview.h b/src/tastylistview.h new file mode 100644 index 0000000..fae9d02 --- /dev/null +++ b/src/tastylistview.h @@ -0,0 +1,225 @@ +/*************************************************************************** + * Copyright (C) 2006 by Marco Martin * + * notmart@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public * + * License as published by the Free Software Foundation; * + * either version 2 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + +#ifndef TASTYLISTVIEW_H +#define TASTYLISTVIEW_H + +#include <klistview.h> +#include <kiconloader.h> +#include <kservice.h> +#include <kservicegroup.h> +#include <kdesktopfile.h> +#include <qpixmap.h> +#include <qtooltip.h> +#include <qtimer.h> +#include <qheader.h> + +class TastyListView; + +//the space reserved for the action icon (bookmark, remove bookmark etc + +/** + @author Marco Martin <notmart@gmail.com> + */ +class TastyListViewToolTip: public QToolTip +{ + public: + TastyListViewToolTip( QWidget *parent, TastyListView *tListView ); + + void maybeTip( const QPoint &pos ); + + private: + TastyListView *listView; +}; + +/** + @author Marco Martin <notmart@gmail.com> +*/ +class TastyListView : public KListView +{ + Q_OBJECT +private: + bool highLightGroups; + QTimer *onItemTimer; + QListViewItem *underCursorItem; + QListViewItem *openItem; + bool mouseDown; + bool easyOpen; + int actionIconSize; + int actionIconSpace; + + TastyListViewToolTip *listItemTip; + +public: + TastyListView( QWidget * parent = 0, const char * name = 0); + + ~TastyListView(); + + bool getHighLightGroups(){ return highLightGroups;} + void setHighLightGroups(bool highLight){highLightGroups = highLight;} + + bool getEasyOpen(){ return easyOpen;} + void setEasyOpen(bool easy){easyOpen = easy;} + void startDrag(); + + void setActionIconSize(int newSize){ actionIconSize = newSize; actionIconSpace = newSize*2; } + int getActionIconSize(){return actionIconSize;} + int getActionIconSpace(){return actionIconSpace;} + + QListViewItem * getOpenItem(){ return openItem;} + void setOpenItem( QListViewItem * listItem ){openItem = listItem;} + +public slots: + virtual void clear(){openItem = underCursorItem = NULL; KListView::clear();} + + +protected: + virtual void leaveEvent( QEvent * e ); + virtual void contentsMouseMoveEvent( QMouseEvent * e ); + virtual void contentsMouseReleaseEvent( QMouseEvent * e ); + virtual void keyPressEvent( QKeyEvent * e ); + + +private slots: + void slotOnItem( QListViewItem * listItem ); + void slotTimeout(); + +signals: + //Using own signal instead of clicked() in order to avoid launching two times the same app :-) + void activated(QListViewItem *, const QPoint &, int ); + +}; + + +/** + @author Marco Martin <notmart@gmail.com> +*/ +class TastyListViewItem : public KListViewItem +{ + //Q_OBJECT +friend class TastyListView; + public: + typedef enum + { + Service, + ServiceGroup, + DesktopFile, + Empty + }Type; + + typedef enum + { + AddBookMark, + RemoveBookMark, + OpenGroup, + Expand, + Collapse, + NoAction + }ActionType; + + TastyListViewItem( TastyListView * parent ); + TastyListViewItem( TastyListViewItem * parent ); + TastyListViewItem( TastyListView * parent, TastyListViewItem * after, QString label1 ); + TastyListViewItem( TastyListViewItem * parent, TastyListViewItem * after, QString label1 ); + TastyListViewItem( TastyListView * parent, TastyListViewItem * after ); + TastyListViewItem( TastyListViewItem * parent, TastyListViewItem * after ); + TastyListViewItem( TastyListView * parent, QString label1 ); + TastyListViewItem( TastyListViewItem * parent, QString label1 ); + + ~TastyListViewItem(); + + //QString text(int column) const {return cellText;} + QString getCellText(int column) const {return cellText;} + + //TastyListViewItem *parent(); + void paintCell ( QPainter * p, const QColorGroup & cg, int column, int width, int align ); + + Type getType(){return itemType;} + void setType( Type newItemType ){itemType = newItemType;} + + ActionType getActionType(){return actionType;} + void setActionType( ActionType newActionType ){ actionType = newActionType;loadPixmap();} + void loadPixmap(); + + void setPath( QString newPath){ path = newPath;} + QString getPath(){return path;} + + void setDeskopEntryPath( QString newPath){ desktopEntryPath = newPath;} + QString getDeskopEntryPath(){return desktopEntryPath;} + + QString getSubText(){return subText;} + bool xOnDecoration( int x ) + { QListView *lv = listView(); + if( !lv ) return false; + return !( x > lv->header()->sectionPos( lv->header()->mapToIndex( 0 ) ) + + lv->treeStepSize() * ( depth() + ( lv->rootIsDecorated() ? 1 : 0) ) + lv->itemMargin() || + x < lv->header()->sectionPos( lv->header()->mapToIndex( 0 ) ) );} + + void setSubText(QString text) //FIXME: add the column + {if(cellText.isEmpty())cellText=KListViewItem::text(0); + KListViewItem::setText(0,cellText+text);subText = QString(text);} + void setText(int column, const QString & text ) + {KListViewItem::setText(column, cellText+text); cellText = text;} + void setDisplaySubText( bool display ){ displaySubText = display; } + + bool hasEllipsis(){return ellipsis;} + void setHighLight( bool newHighLight ){highLight=newHighLight;} + bool isHighLight(){return highLight;} + + void setMenuId( QString newMenuId ){ menuId = newMenuId;} + QString getMenuId(){ return menuId; } + + QString key( int column, bool ascending ) const; + + int width( const QFontMetrics & fm, const QListView * lv, int c ) + { TastyListView *tlv = dynamic_cast<TastyListView *>( listView() ); + if( tlv ) + return KListViewItem::width(fm, lv, c) + tlv->getActionIconSpace(); + else + return KListViewItem::width(fm, lv, c); + } + +protected: + virtual void setup(); + +private: + + Type itemType; + ActionType actionType; + QString path; + QString desktopEntryPath; + QString cellText; + QString subText; + QString menuId; + + bool ellipsis; + bool highLight; + bool displaySubText; + QPixmap actionPix; + KIconLoader *iconLoader; + + void commonConstructor(); + //a tiny reimplementation of max... + int max(int a, int b){return (a>b?a:b);} +}; + + +#endif diff --git a/src/tastymenu.cpp b/src/tastymenu.cpp new file mode 100644 index 0000000..1ec371e --- /dev/null +++ b/src/tastymenu.cpp @@ -0,0 +1,492 @@ +/*************************************************************************** + * Copyright (C) 2006 by Marco Martin * + * notmart@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public * + * License as published by the Free Software Foundation; * + * either version 2 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#include <kglobal.h> +#include <klocale.h> +#include <kconfig.h> +#include <kapplication.h> +#include <kmessagebox.h> +#include <kconfigdialog.h> +#include <kaboutdata.h> +#include <kaboutapplication.h> +#include <kpopupmenu.h> +#include <kkeydialog.h> +#include <kuser.h> +#include <kstandarddirs.h> +#include <kdebug.h> +#include <qstyle.h> + + +#include "tastymenu.h" +#include "appearance.h" +#include "behaviour.h" + + + +TastyMenu::TastyMenu(const QString& configFile, Type type, int actions, QWidget *parent, const char *name) + : KPanelApplet(configFile, type, actions, parent, name), numNewApplications(0) +{ + + // Get the current application configuration handle + kConfig = sharedConfig(); + prefSkel = new Prefs(kConfig); + prefSkel->readConfig(); + + kickerConf = KGlobal::config(); + kickerConf->setGroup("buttons"); + _showBigToolTip = kickerConf->readBoolEntry("EnableIconZoom", true); + + button = new TastyButton(this); + + menuHandler = new MenuHandler(this, prefSkel, "MenuHandler", + prefSkel->isNormalWindow() ? + Qt::WType_Dialog : + Qt::WType_Popup|Qt::WNoAutoErase); + + connect (button, SIGNAL (pressed()), this, SLOT (clickSlot ())); + connect (menuHandler, SIGNAL(hidden()), this, SLOT(setButtonUp())); + _menuButtonLabel = prefSkel->menuButtonLabel(); + if(_menuButtonLabel.isEmpty()) + button -> setTextLabel(i18n("Menu"), false); + else + button -> setTextLabel(_menuButtonLabel, false); + + button -> setUsesTextLabel(prefSkel->menuButtonLabelType() + != Prefs::EnumMenuButtonLabelType::MenuButtonNone); + button -> setTextPosition(QToolButton::BesideIcon); + menuTip = new TastyToolTip(button); + + _toolTipTitle = prefSkel->toolTipTitle(); + + if( !_toolTipTitle.isEmpty() ) + menuTip->setTitle(_toolTipTitle); + + if( height() >= KIcon::SizeMedium ) + button->setUsesBigPixmap(true); + else + button->setUsesBigPixmap(false); + + iconLoader = KGlobal::iconLoader(); + + loadMenuButtonIcon(); + + button->setAutoRaise(true); + + _newAppsNotification = prefSkel->newAppsNotification(); + if( _newAppsNotification ) + { + setNewApplicationsMessage(prefSkel->newInstalledApps().count()); + connect( menuHandler, SIGNAL(newApplications(int)), + this, SLOT(setNewApplicationsMessage(int))); + } + setGlobalAccel( prefSkel->overrideAltF1()); + + connect( menuHandler, SIGNAL(kickerConfChanged()), + this, SLOT(updateConfiguration()) ); +} + + +TastyMenu::~TastyMenu() +{ + //delete prefSkel; + KGlobal::locale()->removeCatalogue("tastymenu"); +} + +void TastyMenu::loadMenuButtonIcon() +{ + _menuButtonIcon = prefSkel->menuButtonIcon(); + //the tooltip has the same icon as the button + menuTip->loadIcon( _menuButtonIcon ); + + if( prefSkel->menuButtonIconType() + == Prefs::EnumMenuButtonIconType::IconNone ) + { + button->setIconSet(QIconSet()); + return; + } + + if( position() == pTop || position() == pBottom ) + _iconsize = height(); + else if(position() == pLeft || position() == pRight) + _iconsize = width(); + + QPixmap btnPixmap(iconLoader->loadIcon(_menuButtonIcon, KIcon::Panel, _iconsize)); + if( !btnPixmap.isNull() ) + button->setIconSet(btnPixmap); + else + { + kdDebug() << "Failed to load custom icon" << endl; + button->setIconSet(iconLoader->loadIcon("kmenu", KIcon::Panel, height())); + } +} + +QPoint TastyMenu::menupos(QWidget *widget) +{ + //now should look decent on all positions + switch( position() ) + { + case pLeft: + { + return QPoint( this->mapToGlobal( this->geometry().topRight() )); + break; + } + case pRight: + { + QPoint buttonCoord = this->mapToGlobal(this->geometry().topLeft()); + return QPoint( buttonCoord.x()-widget->width(), buttonCoord.y() ); + break; + } + case pTop: + { + return QPoint( this->mapToGlobal( this->geometry().bottomLeft() )); + break; + } + default: //pBottom or floating + { + QPoint buttonCoord = this->mapToGlobal(this->geometry().topLeft()); + return QPoint( buttonCoord.x(), buttonCoord.y()-widget->height() ); + break; + } + } +} + +void TastyMenu::clickSlot() +{ + menuTip->hideTip(); + + menuHandler->popup(menupos(menuHandler)); + + //QTimer::singleShot( 10000, this, SLOT(setButtonUp()) ); +} + +void TastyMenu::about() +{ + KAboutData data("tastymenu", + I18N_NOOP("Tasty Menu"), + "1.0.6", + I18N_NOOP("KMenu replacement"), + KAboutData::License_LGPL_V2, + "(c) 2006-2007, Marco Martin",0,0,"mart@notmart.org"); + + data.addAuthor("Marco Martin", + I18N_NOOP("Maintainer"), + "mart@notmart.org", + "http://www.notmart.org"); + + data.setTranslator(I18N_NOOP("_: NAME OF TRANSLATORS\\nYour names") + ,I18N_NOOP("_: EMAIL OF TRANSLATORS\\nYour emails")); + + data.addCredit("Yurkovsky Andrey", + I18N_NOOP("For the Russian translation"), + "anyr@tut.by"); + data.addCredit("Jannick Kuhr", + I18N_NOOP("For the German translation"), + "jannick.kuhr@kdemail.net"); + data.addCredit("Jesús S Fernández Prieto (xgoan)", + I18N_NOOP("For the Spanish translation"), + "jesus@infodps.com"); + data.addCredit("Motsyo Vitaliy", + I18N_NOOP("For the Ukrainian translation"), + "vitalikmotsyo@gmail.com"); + data.addCredit("Laurent Hilsz", + I18N_NOOP("For the French translation"), + "laurent.hilsz@gmail.com"); + data.addCredit("Tommi Nieminen", + I18N_NOOP("For the Finnish translation"), + "translator@legisign.org"); + data.addCredit("Matija Šuklje", + I18N_NOOP("For the Slovenian translation"), + "matija.suklje@rutka.net"); + data.addCredit("Tomasz Argasiński", + I18N_NOOP("For the Polish translation"), + "targasinski@o2.pl"); + data.addCredit("Ewerton de A. Dutra" + I18N_NOOP("For the Polish translation"), + "ea.dutra@gmail.com"); + + + data.addCredit("Oswald Buddenhagen and Stephan Kulow", + I18N_NOOP("For the Switch user code from KDM"), + "ossi@kde.org and coolo@kde.org"); + + data.addCredit("The whole KBFX team", + I18N_NOOP("For some inspirations here and there."), + "http://www.kbfx.org"); + + data.addCredit("Seb Ruiz", + I18N_NOOP("For some code taken from Amarok's statistics list view"), + "me@sebruiz.net"); + + KIconLoader *iconLoader = KGlobal::iconLoader(); + data.setProgramLogo(iconLoader->loadIcon("kmenu", KIcon::Panel).convertToImage()); + + KAboutApplication dialog(&data); + dialog.exec(); +} + + +void TastyMenu::help() +{ + KApplication::kdeinitExec("khelpcenter", "help:/tastymenu"); +} + + +void TastyMenu::preferences() +{ + if(KConfigDialog::showDialog("settings")) + return; + + Appearance *appearanceDialog = new Appearance(0, "appearance" ); + Behaviour *behaviourDialog = new Behaviour(0, "behaviour" ); + + KConfigDialog *dialog = new KConfigDialog(this, "settings", prefSkel, + KDialogBase::Tabbed, + KConfigDialog::Default| + KConfigDialog::Ok| + KConfigDialog::Apply| + KConfigDialog::Cancel ); + dialog->addPage(appearanceDialog, i18n("Appearance"), "appearance" ); + dialog->addPage(behaviourDialog, i18n("Behaviour"), "behaviour" ); + connect( dialog, SIGNAL(settingsChanged()), + this, SLOT(updateConfiguration()) ); + dialog->show(); +} + +void TastyMenu::setGlobalAccel( bool global ) +{ + globalAccel = new KGlobalAccel(this); + globalAccel->insert("Toggle Tasty Menu", i18n("Toggle Tasty Menu"), + i18n("Toggle Tasty Menu"), + 0, 0, this, SLOT(clickSlot())); + globalAccel->readSettings(); + globalAccel->updateConnections(); + + //deactivate or reactivate the global alt+f1 shotcut for kmenu + if( global ) + { + if( !kickerConf ) + kickerConf = KGlobal::config(); + + kickerConf->setGroup("Global Shortcuts"); + QString kmenuShortcut = kickerConf->readEntry("Popup Launch Menu", "default(Alt+F1)"); + if( kmenuShortcut=="none" ) + return; + QString tastyMenuShortcut = kickerConf->readEntry("Toggle Tasty Menu", ""); + KShortcut shortCutKey(tastyMenuShortcut); + kickerConf->writeEntry("Popup Launch Menu", "none"); + kickerConf->writeEntry("Toggle Tasty Menu", kmenuShortcut); + kickerConf->sync(); + + } + else + { + if( !kickerConf ) + kickerConf = KGlobal::config(); + kickerConf->setGroup("Global Shortcuts"); + kickerConf->deleteEntry("Popup Launch Menu"); + kickerConf->sync(); + } +} + + +void TastyMenu::updateConfiguration() +{ + kickerConf->setGroup("buttons"); + _showBigToolTip = kickerConf->readBoolEntry("EnableIconZoom", true); + + menuHandler->updateConfig(); + setGlobalAccel(prefSkel->overrideAltF1()); + _menuButtonLabel = prefSkel->menuButtonLabel(); + + if(_menuButtonLabel.isEmpty()) + button -> setTextLabel(i18n("Menu"), false); + else + button -> setTextLabel(_menuButtonLabel, false); + + button -> setUsesTextLabel(prefSkel->menuButtonLabelType() + != Prefs::EnumMenuButtonLabelType::MenuButtonNone); + loadMenuButtonIcon(); + + _newAppsNotification = prefSkel->newAppsNotification(); + + _toolTipTitle = prefSkel->toolTipTitle(); + + if( !_toolTipTitle.isEmpty() ) + menuTip->setTitle(_toolTipTitle); +} + + +void TastyMenu::setNewApplicationsMessage( int number ) +{ + if( number <= 0 ) + { + menuTip ->setMessage(""); + } + else + menuTip ->setMessage(i18n("There is one new installed application", + "There are %n new installed applications", number)); + /*else if( number == 1) + menuTip ->setMessage(i18n("There is one new installed application")); + else + menuTip ->setMessage(i18n("There are")+" " + QString().setNum(number) +" "+ i18n("new installed applications"));*/ + + if( _showBigToolTip && number > numNewApplications ) + menuTip->notify( menupos( menuTip ) ); + + numNewApplications = number; +} + + +int TastyMenu::widthForHeight(int height) const +{ + int buttonWidth = width(); + if( position() == pTop || position() == pBottom ) + { + button -> setTextPosition(QToolButton::BesideIcon); + if( prefSkel->menuButtonLabelType() + != Prefs::EnumMenuButtonLabelType::MenuButtonNone ) + return ((button->fontMetrics()).width(button->textLabel())) + _iconsize + 10; + else + return height; + } + else + return buttonWidth; +} + +int TastyMenu::heightForWidth(int width) const +{ + int buttonHeight = height(); + if( position() == pLeft || position() == pRight ) + { + if( prefSkel->menuButtonLabelType() + != Prefs::EnumMenuButtonLabelType::MenuButtonNone ) + { + button -> setTextPosition(QToolButton::BelowIcon); + return ((button->fontMetrics()).height()) + _iconsize + 10; + } + else + return width; + } + else + return buttonHeight; +} + +void TastyMenu::resizeEvent(QResizeEvent *e) +{ + int newHeight = e->size().height(); + button->setFixedHeight(newHeight); + button->setFixedWidth(e->size().width()); + + if( newHeight >= KIcon::SizeMedium ) + button->setUsesBigPixmap(true); + else + button->setUsesBigPixmap(false); + + + loadMenuButtonIcon(); +} + +void TastyMenu::mousePressEvent(QMouseEvent *e) +{ + menuTip->hideTip(); + + if(e->button()==RightButton) + { + KPopupMenu menu(this); + + menu.insertTitle("Tasty Menu"); + menu.insertItem(SmallIcon("kmenu"), i18n("&About"), 1); + menu.insertItem(SmallIcon("help"), i18n("&Help"), 2); + menu.insertItem(SmallIcon("kmenuedit"), i18n("&Edit Menu"), 3); + if( _newAppsNotification ) + menu.insertItem(SmallIcon("locationbar_erase"), i18n("&Clear recently installed applications list"), 4); + menu.insertSeparator(); + menu.insertItem(SmallIcon("configure_shortcuts"), i18n("&Configure Global Shortcuts..."), 5); + menu.insertItem(SmallIcon("configure"), i18n("&Configure..."), 6); + + int choice = menu.exec(this->mapToGlobal(e->pos())); + + switch( choice ) + { + case 1: + about(); + break; + + case 2: + help(); + break; + + case 3: + KRun::runCommand ("kmenuedit"); + break; + + case 4: + menuHandler->clearNewInstalledApplications(); + setNewApplicationsMessage(0); + menuHandler->slotUpdateApplications(); + break; + case 5: + { + kickerConf->sync(); + KKeyDialog::configure(globalAccel); + globalAccel->writeSettings(kickerConf); + globalAccel->updateConnections(); + kickerConf->sync(); + break; + } + case 6: + preferences(); + break; + + default: + break; + } + } +} + +void TastyMenu::enterEvent( QEvent * e ) +{ + e=e; + if( _showBigToolTip && !menuHandler->isVisible() ) + menuTip->showTip(menupos(menuTip)); + + KPanelApplet::enterEvent(e); +} + +void TastyMenu::leaveEvent( QEvent * e ) +{ + e=e; + menuTip->hideTip(); + + KPanelApplet::leaveEvent(e); +} + +extern "C" +{ + KPanelApplet* init( QWidget *parent, const QString& configFile) + { + KGlobal::locale()->insertCatalogue("tastymenu"); + return new TastyMenu(configFile, KPanelApplet::Normal, + KPanelApplet::About | KPanelApplet::Help | KPanelApplet::Preferences, + parent, "tastymenu"); + } +} diff --git a/src/tastymenu.desktop b/src/tastymenu.desktop new file mode 100644 index 0000000..475d8c0 --- /dev/null +++ b/src/tastymenu.desktop @@ -0,0 +1,10 @@ +[Desktop Entry] +Type=Plugin +Encoding=UTF-8 +Comment=A KMenu replacement + +Name=Tasty Menu + +Icon=kmenu +X-KDE-Library=tastymenu_panelapplet +X-KDE-UniqueApplet = false diff --git a/src/tastymenu.h b/src/tastymenu.h new file mode 100644 index 0000000..66900fe --- /dev/null +++ b/src/tastymenu.h @@ -0,0 +1,165 @@ +/*************************************************************************** + * Copyright (C) 2006 by Marco Martin * + * notmart@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the Lesser GNU General Public * + * License as published by the Free Software Foundation; * + * either version 2 of the License, or (at your option) * + * any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ + + +#ifndef TASTYMENU_H +#define TASTYMENU_H + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#include <kpanelapplet.h> +#include <kiconloader.h> +#include <qstring.h> +#include <kconfig.h> +#include <qpopupmenu.h> +#include <kconfigskeleton.h> +#include <kglobalaccel.h> +#include <qpainter.h> + +#include "menuhandler.h" +#include "prefs.h" +#include "tastytooltip.h" +#include "tastybutton.h" + +class TastyMenu : public KPanelApplet +{ + Q_OBJECT + +public: + /** + * Construct a @ref KPanelApplet just like any other widget. + * + * @param configFile The configFile handed over in the factory function. + * @param Type The applet @ref type(). + * @param actions Standard RMB menu actions supported by the applet (see @ref action() ). + * @param parent The pointer to the parent widget handed over in the factory function. + * @param name A Qt object name for your applet. + **/ + TastyMenu(const QString& configFile, Type t = Normal, int actions = 0, + QWidget *parent = 0, const char *name = 0); + /** destructor */ + ~TastyMenu(); + + /** + * Retrieve a suggested width for a given height. + * + * Every applet should reimplement this function. + * + * Depending on the panel orientation the height (horizontal panel) or the + * width (vertical panel) of the applets is fixed. + * The exact values of the fixed size component depend on the panel size. + * + * On a horizontal panel the applet height is fixed, the panel will + * call @ref widthForHeight(int height) with @p height + * equal to 'the fixed applet height' + * when laying out the applets. + * + * The applet can now choose the other size component (width) + * based on the given height. + * + * The width you return is granted. + **/ + virtual int widthForHeight(int height) const; + /** + * @return A suggested height for a given width. + * + * Every applet should reimplement this function. + * + * Depending on the panel orientation the height (horizontal panel) or the + * width (vertical panel) of the applets is fixed. + * The exact values of the fixed size component depend on the panel size. + * + * On a vertical panel the applet width is fixed, the panel will + * call @ref heightForWidth(int width) with @p width + * equal to 'the fixed applet width' + * when laying out the applets. + * + * The applet can now choose the other size component (height) + * based on the given width. + * + * The height you return is granted. + **/ + virtual int heightForWidth(int width) const; + /** + * Is called when the user selects "About" from the applets RMB menu. + * Reimplement this function to launch a about dialog. + * + * Note that this is called only when your applet supports the About action. + * See @ref Action and @ref KPanelApplet(). + **/ + virtual void about(); + /** + * Is called when the user selects "Help" from the applets RMB menu. + * Reimplement this function to launch a manual or help page. + * + * Note that this is called only when your applet supports the Help action. + * See @ref Action and @ref KPanelApplet(). + **/ + virtual void help(); + /** + * Is called when the user selects "Preferences" from the applets RMB menu. + * Reimplement this function to launch a preferences dialog or kcontrol module. + * + * Note that this is called only when your applet supports the preferences action. + * See @ref Action and @ref KPanelApplet(). + **/ + virtual void preferences(); + +protected: + virtual void resizeEvent(QResizeEvent *); + virtual void mousePressEvent(QMouseEvent *e); + virtual void enterEvent( QEvent * e ); + virtual void leaveEvent( QEvent * e ); + +private: + KSharedConfig *kConfig; + Prefs *prefSkel; + KConfig *kickerConf; + MenuHandler *menuHandler; + TastyButton *button; + TastyToolTip *menuTip; + QPoint menupos(QWidget *widget); + KGlobalAccel *globalAccel; + KIconLoader *iconLoader; + int numNewApplications; + + + QString _menuButtonLabel; + QString _menuButtonIcon; + QString _toolTipTitle; + bool _newAppsNotification; + int _iconsize; + bool _showBigToolTip; + + void setGlobalAccel( bool global ); + void loadMenuButtonIcon(); + +private slots: + + void clickSlot(); + void updateConfiguration(); + void setNewApplicationsMessage( int number ); + void setButtonUp(){ button->setDown(false); } +}; + +#endif diff --git a/src/tastymenu.kcfg b/src/tastymenu.kcfg new file mode 100644 index 0000000..792bbe7 --- /dev/null +++ b/src/tastymenu.kcfg @@ -0,0 +1,188 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE kcfg SYSTEM "http://www.kde.org/standards/kcfg/1.0/kcfg.dtd"> +<kcfg> + <kcfgfile arg="true"/> + + <group name="Appearance"> + <entry name="IconSize1" type="Int"> + <label>Size of the menu icons, first column.</label> + <default>22</default> + <min>16</min> + <max>64</max> + </entry> + <entry name="IconSize2" type="Int"> + <label>Size of the menu icons, first column.</label> + <default>22</default> + <min>16</min> + <max>64</max> + </entry> + <entry name="IconSize3" type="Int"> + <label>Size of the menu icons, first column.</label> + <default>22</default> + <min>16</min> + <max>64</max> + </entry> + <entry name="ActionIconSize" type="Int"> + <label>Size of the menu icons.</label> + <default>16</default> + <min>16</min> + <max>48</max> + </entry> + + <entry key="MenuButtonLabelType" type="Enum" > + <choices> + <choice name="MenuButtonText"> + <label>Text</label> + </choice> + <choice name="MenuButtonNone"> + <label>None</label> + </choice> + </choices> + <default>MenuButtonText</default> + <label>Botton label Type</label> + </entry> + + <entry name="MenuButtonLabel" type="String"> + <label>Button label text to be displayed.</label> + </entry> + + <entry key="MenuButtonIconType" type="Enum" > + <choices> + <choice name="Icon"> + <label>Icon</label> + </choice> + <choice name="IconNone"> + <label>None</label> + </choice> + </choices> + <default>Icon</default> + <label>Button icon Type</label> + </entry> + + <entry name="MenuButtonIcon" type="String"> + <label>Button icon to be displayed.</label> + <default>kmenu</default> + </entry> + + <entry name="DisplaySubText" type="Bool"> + <label>If display the application descritption under the name</label> + <default>true</default> + </entry> + + <entry name="ToolTipTitle" type="String"> + <label>Text of the main tooltip</label> + </entry> + + <entry name="MenuWidth" type="Int"> + <label>Percentage of the menu width compared to screen.</label> + <default>70</default> + <min>10</min> + <max>90</max> + </entry> + + <entry name="MenuHeight" type="Int"> + <label>Percentage of the menu height compared to screen.</label> + <default>70</default> + <min>10</min> + <max>90</max> + </entry> + + <entry name="ShowExpander" type="Bool"> + <label>If the three expanders should be shown in the applications list</label> + <default>false</default> + </entry> + + <entry name="AlwaysCollapsed" type="Bool"> + <label>If the applications categories should be collapsed by default</label> + <default>false</default> + </entry> + </group> + + <group name="Behaviour"> + <entry name="KerryIntegration" type="Bool"> + <label>If the search field should perform queries with Kerry Beagle</label> + <default>false</default> + </entry> + <entry name="StrigiIntegration" type="Bool"> + <label>If the search field should perform queries with Strigi. + It has no effect when Kerry integration is enabled</label> + <default>false</default> + </entry> + + <entry name="OverrideAltF1" type="Bool"> + <label>If Alt+F1 shortcut should be used by Tasty Menu</label> + <default>false</default> + </entry> + + <entry name="NewAppsNotification" type="Bool"> + <label>notify and highlight entries of recently installed applications.</label> + <default>true</default> + </entry> + + <entry name="HideOneChild" type="Bool"> + <label>If Categories with only one child should be hidden</label> + <default>true</default> + </entry> + + <entry name="Alphabetical" type="Bool"> + <label>Always show entries in alphabetical order</label> + <default>false</default> + </entry> + + <entry name="ShowSaveSession" type="Bool"> + <label>Show "Save session" entry in the user menu</label> + <default>false</default> + </entry> + </group> + + <group name="AppState"> + <entry name="MenuMode" type="Int"> + <label>The current mode for the first column.</label> + <default>0</default> + </entry> + + <entry name="CurrentCategory" type="String"> + <label>currently open submenu.</label> + </entry> + + <entry name="FavouriteApps" type="PathList"> + <label>The list for favourite apps.</label> + </entry> + + <entry name="OldInstalledApps" type="PathList"> + <label>The list of all installed apps.</label> + </entry> + <entry name="OldInstalledAppsAge" type="Int"> + <label>When the list of old installed apps was built.</label> + </entry> + + <entry name="NewInstalledApps" type="PathList"> + <label>The list of recently installed apps.</label> + </entry> + <entry name="NewInstalledAppsTimeStamps" type="IntList"> + <label>Timestamps for the list of recently installed apps.</label> + </entry> + + + <entry name="IsNormalWindow" type="Bool"> + <label>If the menu is a Popup or a normal window</label> + <default>false</default> + </entry> + <entry name="NormalWindowWidth" type="Int"> + <label>Width of the normal window (ignored in PopUp mode)</label> + <default>400</default> + </entry> + <entry name="NormalWindowHeight" type="Int"> + <label>Height of the normal window (ignored in PopUp mode)</label> + <default>300</default> + </entry> + <entry name="NormalWindowX" type="Int"> + <label>X coord of the normal window (ignored in PopUp mode)</label> + <default>0</default> + </entry> + <entry name="NormalWindowY" type="Int"> + <label>Y coord of the normal window (ignored in PopUp mode)</label> + <default>0</default> + </entry> + </group> +</kcfg>
\ No newline at end of file diff --git a/src/tastymenu.lsm b/src/tastymenu.lsm new file mode 100644 index 0000000..44d525c --- /dev/null +++ b/src/tastymenu.lsm @@ -0,0 +1,16 @@ +Begin3 +Title: TastyMenu -- Some description +Version: 0.1 +Entered-date: +Description: +Keywords: KDE Qt +Author: Marco Martin <notmart@gmail.com> +Maintained-by: Marco Martin <notmart@gmail.com> +Home-page: +Alternate-site: +Primary-site: ftp://ftp.kde.org/pub/kde/unstable/apps/utils + xxxxxx tastymenu-0.1.tar.gz + xxx tastymenu-0.1.lsm +Platform: Linux. Needs KDE +Copying-policy: GPL +End diff --git a/src/tastytooltip.cpp b/src/tastytooltip.cpp new file mode 100644 index 0000000..27f6a12 --- /dev/null +++ b/src/tastytooltip.cpp @@ -0,0 +1,100 @@ +/*************************************************************************** + * Copyright (C) 2006 by Marco Martin * + * notmart@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#include "tastytooltip.h" + +#include <qlabel.h> +#include <qtimer.h> +#include <kiconloader.h> +#include <kdebug.h> +#include <qbutton.h> + + +TastyToolTip::TastyToolTip( QWidget * parent,const char * name, WFlags fl) + : QWidget(parent, name, fl|WX11BypassWM ) +{ + iconName = "kmenu"; + tastyToolTipLayout = new QVBoxLayout( this, 0, 0, "tastyToolTipLayout"); + toolTipWidget = new TastyToolTipWidget(this); + tastyToolTipLayout->addWidget(toolTipWidget); +} + + +void TastyToolTip::loadIcon( QString icon ) +{ + iconName = icon; + KIconLoader *iconLoader = KGlobal::iconLoader(); + QPixmap btnPixmap(iconLoader->loadIcon(icon, KIcon::Panel, KIcon::SizeHuge)); + + if( !btnPixmap.isNull() ) + toolTipWidget->iconPixmap->setPixmap(btnPixmap); + else + { + kdDebug() << "Failed to load custom icon" << endl; + toolTipWidget->iconPixmap->setPixmap(iconLoader->loadIcon("kmenu", KIcon::Panel, KIcon::SizeHuge)); + } +} + +TastyToolTip::~TastyToolTip() +{ +} + + +void TastyToolTip::showTip(const QPoint & point) +{ + move(point); + QTimer::singleShot(250, this, SLOT(show())); +} + +void TastyToolTip::show() +{ + QButton *button = dynamic_cast<QButton *>(parent()); + if(button && button->hasMouse() && !button->isDown()) + QWidget::show(); +} + +void TastyToolTip::hideTip( ) +{ + QTimer::singleShot(250, this, SLOT(hide())); +} + +void TastyToolTip::notify(const QPoint & point ) +{ + move(point); + show(); + QTimer::singleShot(5000, this, SLOT(hide())); +} + +void TastyToolTip::setMessage( QString message ) +{ + KIconLoader *iconLoader = KGlobal::iconLoader(); + toolTipWidget->MessageLabel->setText(message); + + if( message.length() > 0 ) + toolTipWidget->iconPixmap->setPixmap(iconLoader->loadIcon("messagebox_info", KIcon::Panel, KIcon::SizeHuge)); + else + loadIcon(iconName); +} + +void TastyToolTip::setTitle( QString title ) +{ + if( !title.isEmpty() ) + toolTipWidget->appNameLabel->setText( title ); +} + diff --git a/src/tastytooltip.h b/src/tastytooltip.h new file mode 100644 index 0000000..aea0838 --- /dev/null +++ b/src/tastytooltip.h @@ -0,0 +1,59 @@ +/*************************************************************************** + * Copyright (C) 2006 by Marco Martin * + * notmart@gmail.com * + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU Library General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU General Public License for more details. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program; if not, write to the * + * Free Software Foundation, Inc., * + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * + ***************************************************************************/ +#ifndef TASTYTOOLTIP_H +#define TASTYTOOLTIP_H + +#include <qwidget.h> +#include <qlayout.h> + +#include "tastytooltipwidget.h" + +/** + @author Marco Martin <notmart@gmail.com> +*/ +class TastyToolTip : public QWidget +{ +public: + TastyToolTip( QWidget * parent=0,const char * name=0, WFlags fl=WType_TopLevel); + + ~TastyToolTip(); + + void showTip(const QPoint & point); + void hideTip( ); + void loadIcon( QString iconName ); + void notify(const QPoint & point ); + void setMessage( QString message ); + void setTitle( QString title ); + +protected: + virtual void mousePressEvent( QMouseEvent * e ){e=e;hide();} + +private: + + QString iconName; + TastyToolTipWidget * toolTipWidget; + QVBoxLayout *tastyToolTipLayout; + +public slots: + virtual void show(); + +}; + +#endif diff --git a/src/tastytooltipwidget.ui b/src/tastytooltipwidget.ui new file mode 100644 index 0000000..faacb2e --- /dev/null +++ b/src/tastytooltipwidget.ui @@ -0,0 +1,109 @@ +<!DOCTYPE UI><UI version="3.3" stdsetdef="1"> +<class>TastyToolTipWidget</class> +<widget class="QWidget"> + <property name="name"> + <cstring>TastyToolTipWidget</cstring> + </property> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>391</width> + <height>127</height> + </rect> + </property> + <property name="caption"> + <string>Tasty Menu</string> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <property name="margin"> + <number>0</number> + </property> + <property name="spacing"> + <number>0</number> + </property> + <widget class="QFrame"> + <property name="name"> + <cstring>frame5</cstring> + </property> + <property name="frameShape"> + <enum>StyledPanel</enum> + </property> + <property name="frameShadow"> + <enum>Raised</enum> + </property> + <hbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>iconPixmap</cstring> + </property> + <property name="sizePolicy"> + <sizepolicy> + <hsizetype>0</hsizetype> + <vsizetype>0</vsizetype> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>64</width> + <height>64</height> + </size> + </property> + <property name="scaledContents"> + <bool>true</bool> + </property> + </widget> + <widget class="QLayoutWidget"> + <property name="name"> + <cstring>layout2</cstring> + </property> + <vbox> + <property name="name"> + <cstring>unnamed</cstring> + </property> + <widget class="QLabel"> + <property name="name"> + <cstring>appNameLabel</cstring> + </property> + <property name="font"> + <font> + <pointsize>14</pointsize> + <bold>1</bold> + </font> + </property> + <property name="text"> + <string><b>Tasty Menu</b></string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>DescLabel</cstring> + </property> + <property name="text"> + <string>Applications, tasks and desktop sessions</string> + </property> + </widget> + <widget class="QLabel"> + <property name="name"> + <cstring>MessageLabel</cstring> + </property> + <property name="text"> + <string></string> + </property> + </widget> + </vbox> + </widget> + </hbox> + </widget> + </vbox> +</widget> +<layoutdefaults spacing="6" margin="11"/> +</UI> |