From 0d02094e2b261de9aadac40f65a574e9fbee66e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sl=C3=A1vek=20Banko?= Date: Sun, 10 Apr 2016 02:08:09 +0200 Subject: Initial import of tastymenu 1.0.8 --- src/Makefile.am | 22 + src/appearance.ui | 608 ++++++++++ src/appearance.ui.h | 23 + src/behaviour.ui | 234 ++++ src/create_buttons.sh | 1 + src/dmctl.cpp | 434 +++++++ src/dmctl.h | 93 ++ src/menu.ui | 379 ++++++ src/menuhandler.cpp | 1712 +++++++++++++++++++++++++++ src/menuhandler.h | 184 +++ src/misc.cpp | 38 + src/misc.h | 24 + src/pics/.svn/all-wcprops | 17 + src/pics/.svn/entries | 42 + src/pics/.svn/format | 1 + src/pics/.svn/prop-base/attach.png.svn-base | 5 + src/pics/.svn/prop-base/detach.png.svn-base | 5 + src/pics/.svn/text-base/attach.png.svn-base | Bin 0 -> 192 bytes src/pics/.svn/text-base/detach.png.svn-base | Bin 0 -> 199 bytes src/pics/attach.png | Bin 0 -> 192 bytes src/pics/detach.png | Bin 0 -> 199 bytes src/prefs.kcfgc | 6 + src/tastybutton.cpp | 116 ++ src/tastybutton.h | 49 + src/tastylistview.cpp | 600 ++++++++++ src/tastylistview.h | 225 ++++ src/tastymenu.cpp | 492 ++++++++ src/tastymenu.desktop | 10 + src/tastymenu.h | 165 +++ src/tastymenu.kcfg | 188 +++ src/tastymenu.lsm | 16 + src/tastytooltip.cpp | 100 ++ src/tastytooltip.h | 59 + src/tastytooltipwidget.ui | 109 ++ 34 files changed, 5957 insertions(+) create mode 100644 src/Makefile.am create mode 100644 src/appearance.ui create mode 100644 src/appearance.ui.h create mode 100644 src/behaviour.ui create mode 100644 src/create_buttons.sh create mode 100644 src/dmctl.cpp create mode 100644 src/dmctl.h create mode 100644 src/menu.ui create mode 100644 src/menuhandler.cpp create mode 100644 src/menuhandler.h create mode 100644 src/misc.cpp create mode 100644 src/misc.h create mode 100644 src/pics/.svn/all-wcprops create mode 100644 src/pics/.svn/entries create mode 100644 src/pics/.svn/format create mode 100644 src/pics/.svn/prop-base/attach.png.svn-base create mode 100644 src/pics/.svn/prop-base/detach.png.svn-base create mode 100644 src/pics/.svn/text-base/attach.png.svn-base create mode 100644 src/pics/.svn/text-base/detach.png.svn-base create mode 100644 src/pics/attach.png create mode 100644 src/pics/detach.png create mode 100644 src/prefs.kcfgc create mode 100644 src/tastybutton.cpp create mode 100644 src/tastybutton.h create mode 100644 src/tastylistview.cpp create mode 100644 src/tastylistview.h create mode 100644 src/tastymenu.cpp create mode 100644 src/tastymenu.desktop create mode 100644 src/tastymenu.h create mode 100644 src/tastymenu.kcfg create mode 100644 src/tastymenu.lsm create mode 100644 src/tastytooltip.cpp create mode 100644 src/tastytooltip.h create mode 100644 src/tastytooltipwidget.ui (limited to 'src') 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 @@ + +Appearance + + + Appearance + + + + 0 + 0 + 520 + 465 + + + + Form1 + + + + unnamed + + + + Icons + + + + 1 + + + + Applications list + + + AlignBottom + + + + + kcfg_ShowExpander + + + Show tree e&xpanders + + + Alt+X + + + + + kcfg_AlwaysCollapsed + + + true + + + Alwa&ys collapsed categories + + + Alt+Y + + + + + textLabel2 + + + Hei&ght: + + + AlignVCenter|AlignRight + + + kcfg_MenuHeight + + + + + textLabel1_4 + + + Button label: + + + AlignVCenter|AlignRight + + + + + spacer6 + + + Horizontal + + + Expanding + + + + 79 + 20 + + + + + + kcfg_ToolTipTitle + + + What text should be displayed in the Kicker button tooltip instead of "Tasty Menu". +Empty means default. + + + + + textLabel1_2 + + + Wi&dth: + + + AlignVCenter|AlignRight + + + kcfg_MenuWidth + + + + + textLabel5 + + + Tootip title: + + + AlignVCenter|AlignRight + + + + + ApplicationsList + + + + 1 + + + + Applications list + + + AlignBottom + + + + + textLabel3 + + + of the screen width + + + + + MenuButton + + + + 1 + + + + Menu button + + + AlignBottom + + + + + textLabel4 + + + of the screen height + + + + + kcfg_MenuButtonIconType + + + + 5 + 0 + 0 + 0 + + + + NoFrame + + + Plain + + + + + + + unnamed + + + 0 + + + + kcfg_Icon + + + &Icon: + + + Alt+I + + + true + + + + + MenuButtonIcon + + + + 7 + 0 + 0 + 0 + + + + + + + What icon should be displayed in the kicker button instead of the KMenu one. +Empty means default. + + + + + kcfg_MenuButtonIcon + + + false + + + + + kcfg_IconNone + + + N&one + + + Alt+O + + + + + + + menuSize + + + + 1 + + + + Menu size + + + AlignBottom + + + + + textLabel1_3 + + + &Action icons size: + + + AlignVCenter|AlignRight + + + kcfg_ActionIconSize + + + + + kcfg_DisplaySubText + + + &Display applications descriptions + + + Alt+D + + + + + kcfg_MenuButtonLabelType + + + + 5 + 0 + 0 + 0 + + + + NoFrame + + + Plain + + + + + + + unnamed + + + 0 + + + + kcfg_MenuButtonText + + + &Text: + + + Alt+T + + + true + + + + + kcfg_MenuButtonLabel + + + + + + What label instead of "Menu" should be shown for the Kicker button. +Empty means default. + + + + + kcfg_MenuButtonNone + + + &None + + + Alt+N + + + + + + + kcfg_MenuWidth + + + % + + + 90 + + + 10 + + + 70 + + + + + kcfg_MenuHeight + + + % + + + 90 + + + 10 + + + 70 + + + + + textLabel2_2 + + + Button icon: + + + AlignVCenter|AlignRight + + + + + spacer4 + + + Horizontal + + + Expanding + + + + 78 + 21 + + + + + + layout1 + + + + unnamed + + + + textLabel1_5_3 + + + third column: + + + + + textLabel1_5_2 + + + second column: + + + + + kcfg_IconSize1 + + + pixels + + + 48 + + + 16 + + + 22 + + + + + kcfg_IconSize3 + + + pixels + + + 48 + + + 16 + + + 22 + + + + + kcfg_IconSize2 + + + pixels + + + 48 + + + 16 + + + 22 + + + + + textLabel1_5 + + + first column: + + + + + + + textLabel1 + + + + 7 + 5 + 0 + 0 + + + + Icons sizes: + + + AlignVCenter|AlignRight + + + kcfg_IconSize + + + + + kcfg_ActionIconSize + + + pixels + + + 48 + + + 16 + + + + + + + + + kcfg_MenuButtonText + toggled(bool) + kcfg_IconNone + setEnabled(bool) + + + kcfg_Icon + toggled(bool) + kcfg_MenuButtonNone + setEnabled(bool) + + + kcfg_MenuButtonText + toggled(bool) + kcfg_MenuButtonLabel + setEnabled(bool) + + + kcfg_Icon + toggled(bool) + MenuButtonIcon + setEnabled(bool) + + + MenuButtonIcon + iconChanged(QString) + Appearance + iconChanged(QString) + + + + appearance.ui.h + + + kcfg_ShowExpander_toggled( bool status ) + iconChanged( QString icon ) + + + + kicondialog.h + + 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 @@ + +Behaviour + + + Behaviour + + + + 0 + 0 + 516 + 400 + + + + Form1 + + + + unnamed + + + + GeneralBehaviour + + + + 1 + + + + General behaviour + + + AlignBottom + + + + + spacer8 + + + Horizontal + + + Expanding + + + + 31 + 21 + + + + + + kcfg_OverrideAltF1 + + + &Override alt+F1 keyboard shortcut (requires kicker restart) + + + Alt+O + + + Pops up Tasty Menu instead of KMenu when the shortcut sequence Alt+F1 is pressed + + + + + spacer7 + + + Horizontal + + + Expanding + + + + 81 + 20 + + + + + + kcfg_ShowSaveSession + + + Show save session functionalit&y + + + Alt+Y + + + 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. + + + + + kcfg_HideOneChild + + + &Hide groups with only one application + + + Alt+H + + + true + + + + + kcfg_Alphabetical + + + Show items in alpha&betical order + + + Alt+B + + + + + SearchFieldBehaviour + + + + 1 + + + + Search field behaviour + + + AlignBottom + + + + + buttonGroup1 + + + NoFrame + + + + + + + unnamed + + + 0 + + + + simpleSearch + + + Si&mple searches + + + Alt+M + + + true + + + Filters the applications list + + + It displays in the first column all the applications whose name or description matches with the query you entered + + + + + kcfg_KerryIntegration + + + &Kerry Beagle integration + + + Alt+K + + + Perform queries with Kerry Beagle + + + Perform queries with Kerry Beagle instead of using the first column. +It needs Kerry Beagle up and running. + + + + + kcfg_StrigiIntegration + + + Stri&gi Integration + + + Alt+G + + + Perform queries with Strigi + + + Perform queries with Strigi instead of using the first column. +It needs the Strigi Daemon up and running + + + + + + + kcfg_NewAppsNotification + + + &Notify recently installed applications (requires kicker restart) + + + Alt+N + + + true + + + + + + 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 + + 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 +#include + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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 = ""; + 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 == "" ? + i18n("X login on remote host") : + i18n("... host", "X login on %1").arg( se.session ) : + se.session == "" ? + 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 + Copyright (C) 2005 Stephan Kulow + + 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 + +struct SessEnt { + QString display, from, user, session; + int vt; + bool self:1, tty:1; +}; + +typedef QValueList 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 @@ + +Menu + + + Menu + + + + 0 + 0 + 538 + 428 + + + + Tasty Menu + + + + unnamed + + + 0 + + + 0 + + + + frame9 + + + PopupPanel + + + Raised + + + + unnamed + + + + leftFrame + + + + 200 + 32767 + + + + StyledPanel + + + Plain + + + 0 + + + 0 + + + 0 + + + + unnamed + + + 0 + + + + dynamicList + + + + + clearRecentButton + + + &Clear list + + + Alt+C + + + + + searchLabel + + + Se&arch: + + + AutoText + + + searchLine + + + + + + Favourite applications + + + + + Most used applications + + + + + Recent applications + + + + + Recent documents + + + + menuModes + + + + 7 + 0 + 0 + 0 + + + + Use this dropdown menu to change the behaviour of the list below + + + + + searchLine + + + true + + + + 7 + 5 + 0 + 0 + + + + + + clearButton + + + ... + + + + + showLabel + + + &Show: + + + menuModes + + + + + + + rootList + + + + 0 + 0 + + + + + + childList + + + + 0 + 0 + + + + + + allAppsFrame + + + + 7 + 5 + 0 + 0 + + + + StyledPanel + + + Sunken + + + + unnamed + + + 2 + + + + textLabel1 + + + + 1 + + + + All Applications + + + AlignCenter + + + + + detachButton + + + + 0 + 0 + 0 + 0 + + + + ... + + + true + + + Make this menu a normal window + + + + + + + layout6 + + + + unnamed + + + + runButton + + + Ru&n program... + + + Alt+N + + + If you know exatly how a program is named you can directly enter the application name + + + + + switchButton + + + + + + + + + Start a new session, switch the active user or edit your user profile + + + + + lockButton + + + L&ock Session + + + Alt+O + + + Lock the pc with a password if you are going away for a while + + + + + logoutButton + + + Lo&g Out... + + + Alt+G + + + Logout, reboot or shoutdown the computer + + + + + + + + + + + TastyListView +
tastylistview.h
+ + -1 + -1 + + 0 + + 7 + 7 + 0 + 0 + + image0 +
+
+ + + 789c534e494dcbcc4b554829cdcdad8c2fcf4c29c95030e0524611cd48cd4ccf28010a1797249664262b2467241641a592324b8aa363156c15aab914146aadb90067111b1f + + + + searchLine + menuModes + dynamicList + rootList + childList + runButton + switchButton + lockButton + logoutButton + + + + tastylistview.h + klistviewsearchline.h + tastylistview.h + tastylistview.h + kpushbutton.h + kpushbutton.h + kpushbutton.h + kpushbutton.h + +
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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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 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(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(o) ) + return false; + fData->prev()->setFocus(); + break; + + case Qt::Key_Down: + { + if( dynamic_cast(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(o) ) + return false; + fData->next()->setFocus(); + break; + + case Qt::Key_Left: + if( dynamic_cast(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(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(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(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(listItem); + if( !tastyListItem ) + return; + + listClicked( static_cast(listItem), coord ); +} + +void MenuHandler::childListClicked( QListViewItem * listItem, const QPoint & coord, int c ) +{ + c=c; + if( !listItem ) + return; + + TastyListViewItem *tastyListItem = dynamic_cast(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(listItem), coord ); +} + +void MenuHandler::rootListClicked( QListViewItem * listItem, const QPoint & coord, int c ) +{ + c=c; + if( !listItem ) + return; + + TastyListViewItem *tastyListItem = dynamic_cast(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(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("

You have chosen to open another desktop session.
" + "The current session will be hidden " + "and a new login screen will be displayed.
" + "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.

") + .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(parent()),Qt::WType_Dialog, pos(), true); + + menu->detachButton->setIconSet(QPixmap(uic_findImage("attach.png"))); + prefSkel->setIsNormalWindow(true); + } + else + { + hide(); + reparent(static_cast(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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "dmctl.h" +#include "menu.h" +#include "prefs.h" +#include "tastylistview.h" + +/** + @author Marco Martin +*/ +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 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 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 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 + * + * 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 +#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 + * + * 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 new file mode 100644 index 0000000..9a2382f Binary files /dev/null and b/src/pics/.svn/text-base/attach.png.svn-base differ diff --git a/src/pics/.svn/text-base/detach.png.svn-base b/src/pics/.svn/text-base/detach.png.svn-base new file mode 100644 index 0000000..53986e4 Binary files /dev/null and b/src/pics/.svn/text-base/detach.png.svn-base differ diff --git a/src/pics/attach.png b/src/pics/attach.png new file mode 100644 index 0000000..9a2382f Binary files /dev/null and b/src/pics/attach.png differ diff --git a/src/pics/detach.png b/src/pics/detach.png new file mode 100644 index 0000000..53986e4 Binary files /dev/null and b/src/pics/detach.png differ 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 +#include +#include +#include +#include + +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 +#include +#include +#include + +/** + @author Marco Martin +*/ +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 +#include +#include +#include +#include +#include +#include +#include + + +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(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(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(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(currentItem()); + if(item && item->getType() == TastyListViewItem::ServiceGroup) + { + if( !openItem ) + { + openItem = currentItem(); + return; + } + TastyListViewItem *oldOpenItem = dynamic_cast(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(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(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(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(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(underCursorItem); + + if( easyOpen && tastyUnderCursorItem && + tastyUnderCursorItem->getType() == TastyListViewItem::ServiceGroup ) + { + emit(activated(underCursorItem, QPoint(underCursorItem->listView()->width()/2,1), 0)); + TastyListViewItem *oldOpenItem = dynamic_cast(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(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(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( 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 +#include +#include +#include +#include +#include +#include +#include +#include + +class TastyListView; + +//the space reserved for the action icon (bookmark, remove bookmark etc + +/** + @author Marco Martin + */ +class TastyListViewToolTip: public QToolTip +{ + public: + TastyListViewToolTip( QWidget *parent, TastyListView *tListView ); + + void maybeTip( const QPoint &pos ); + + private: + TastyListView *listView; +}; + +/** + @author Marco Martin +*/ +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 +*/ +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( 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#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 +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#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 @@ + + + + + + + + + 22 + 16 + 64 + + + + 22 + 16 + 64 + + + + 22 + 16 + 64 + + + + 16 + 16 + 48 + + + + + + + + + + + + MenuButtonText + + + + + + + + + + + + + + + + + Icon + + + + + + kmenu + + + + + true + + + + + + + + + 70 + 10 + 90 + + + + + 70 + 10 + 90 + + + + + false + + + + + false + + + + + + + false + + + + false + + + + + false + + + + + true + + + + + true + + + + + false + + + + + false + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + + + 400 + + + + 300 + + + + 0 + + + + 0 + + + \ 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 +Maintained-by: Marco Martin +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 +#include +#include +#include +#include + + +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(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 +#include + +#include "tastytooltipwidget.h" + +/** + @author Marco Martin +*/ +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 @@ + +TastyToolTipWidget + + + TastyToolTipWidget + + + + 0 + 0 + 391 + 127 + + + + Tasty Menu + + + + unnamed + + + 0 + + + 0 + + + + frame5 + + + StyledPanel + + + Raised + + + + unnamed + + + + iconPixmap + + + + 0 + 0 + 0 + 0 + + + + + 64 + 64 + + + + true + + + + + layout2 + + + + unnamed + + + + appNameLabel + + + + 14 + 1 + + + + <b>Tasty Menu</b> + + + + + DescLabel + + + Applications, tasks and desktop sessions + + + + + MessageLabel + + + + + + + + + + + + + -- cgit v1.2.1