summaryrefslogtreecommitdiffstats
path: root/kicker/menuext/tom
diff options
context:
space:
mode:
authortoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
committertoma <toma@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2009-11-25 17:56:58 +0000
commit4aed2c8219774f5d797760606b8489a92ddc5163 (patch)
tree3f8c130f7d269626bf6a9447407ef6c35954426a /kicker/menuext/tom
downloadtdebase-4aed2c8219774f5d797760606b8489a92ddc5163.tar.gz
tdebase-4aed2c8219774f5d797760606b8489a92ddc5163.zip
Copy the KDE 3.5 branch to branches/trinity for new KDE 3.5 features.
BUG:215923 git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/kdebase@1054174 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'kicker/menuext/tom')
-rw-r--r--kicker/menuext/tom/Makefile.am19
-rw-r--r--kicker/menuext/tom/README65
-rw-r--r--kicker/menuext/tom/TASKGROUPS49
-rw-r--r--kicker/menuext/tom/destinations18
-rw-r--r--kicker/menuext/tom/tom.cc855
-rw-r--r--kicker/menuext/tom/tom.desktop75
-rw-r--r--kicker/menuext/tom/tom.h110
7 files changed, 1191 insertions, 0 deletions
diff --git a/kicker/menuext/tom/Makefile.am b/kicker/menuext/tom/Makefile.am
new file mode 100644
index 000000000..4947ef77b
--- /dev/null
+++ b/kicker/menuext/tom/Makefile.am
@@ -0,0 +1,19 @@
+INCLUDES = -I$(srcdir)/../../libkicker -I$(srcdir)/../../ui -I$(srcdir)/../../core $(all_includes)
+
+kde_module_LTLIBRARIES = kickermenu_tom.la
+
+kickermenu_tom_la_SOURCES = tom.cc
+kickermenu_tom_la_LDFLAGS = $(all_libraries) -module -avoid-version
+kickermenu_tom_la_LIBADD = $(LIB_KDEUI)
+#$(top_builddir)/kicker/ui/libkicker_ui.la
+
+kickermenu_tom_la_METASOURCES = AUTO
+
+desktopmenu_DATA = tom.desktop
+desktopmenudir = $(kde_datadir)/kicker/menuext
+
+tomdata_DATA = destinations
+tomdatadir = $(kde_datadir)/kicker/tom
+
+messages:
+ $(XGETTEXT) *.cc -o $(podir)/libkickermenu_tom.pot
diff --git a/kicker/menuext/tom/README b/kicker/menuext/tom/README
new file mode 100644
index 000000000..2616c76c5
--- /dev/null
+++ b/kicker/menuext/tom/README
@@ -0,0 +1,65 @@
+What is TOM?
+============
+TOM stands for Task Oriented Menu and is a work in progress that will become a
+viable alternative to the current KMenu. Its goals include:
+
+ o Be task oriented
+ o Be simple and clear to use
+ o Create a smaller but usable menu
+ o Limited configurability through sensible defaults
+ o Have all configuration needs built right into the menu, including:
+ o Editor dialogs that can be called up from entries in the menu
+ o Context menus accessed by RMB clicking on a task for powerusers
+ o Allow locking down of menus through immutable settings
+ o Obeys Kicker and KDE Kiosk settings
+ o By making the TOM group of kickerrc immutable all config is removed
+ o By making a task group's rc file immutable, config options are removed
+ o Not require any modifications to the KDE menu system (applnk, etc)
+
+
+What is a "Task Oriented Menu"?
+================================
+A task oriented menu displays it's entries as "things to do" (or tasks) rather
+than simply listing all items that are available. Each of these tasks has an
+application or command associated with it, and this associated command can be
+changed without changing the task name or placement within the menu. The tasks
+are grouped by function and may map to programs, documents or actions.
+
+
+Todo list
+=========
+Editor dialogs
+Make the Destination entries work (only Run A Command is done)
+Populate and track Recent Applications menu entries
+ o Application launching should be caught somewhere in the KDE libs, ala
+ Recent Documents
+Writing out of config files to reflect runtime changes (deleted entries, etc)
+ o This requires keeping track of the config files used in creating the menu
+KDEDIR merging
+ o KDEDIRS are already consulted for taskgroups, but groups of the same name
+ should be merged
+Sane merging of menuext entries
+ o "Recent" items should go into the recent section
+ o Replace TOM's builin recent docs with the menuext version?
+ o Create a Recent Applications menuext?
+ o Add a way to quickly add/remove menuext items from TOM
+ (ala "Modify These Tasks")
+Check for updates on launch and bring them down/install them if they exist
+ o this includes new apps installed on the local box showing up in the menu
+ o "Get cool stuff" integration?
+Further refinement of wording / ordering in main menu (a perpetual TODO ;)
+Creation of real-world task groups
+Support plugins which can add arbitrary functionality to the menu
+
+
+Debate list
+===========
+What should be the default task entry format be:
+ a) Task Name
+ b) Task Name (App Name)
+ c) App Name (Task Name) <-- silly option =)
+Should "Run A Command..." be replaced by an inline combobox?
+ Pros: It's more obvious and will work even if kdesktop is gone. The widget
+ is already written (in tom.cc)
+ Cons: It makes it stand out too much over other entries, takes up more room
+ and isn't as powerful as the full minicli
diff --git a/kicker/menuext/tom/TASKGROUPS b/kicker/menuext/tom/TASKGROUPS
new file mode 100644
index 000000000..c1aa1ed98
--- /dev/null
+++ b/kicker/menuext/tom/TASKGROUPS
@@ -0,0 +1,49 @@
+Task Groups
+===========
+Tasks are grouped into common families of functionality. These groups are then
+stored in a standard KDE configuration file. The General category in the
+config file defines the icon (Icon), user visible name (Name), the number
+of tasks in the group (NumTasks) and optionally whether or not it is hidden
+(Hidden).
+
+For each task there is a numbered section in the file in the form TaskN. Each
+section contains the user visible name for the task (Name), the associated
+.desktop file and optionally whether or not it is hidden (Hidden).
+
+An example file can be found below.
+
+Alternatives
+============
+Alternative formats are possible, including making it more like the servicemenu
+.desktop format or the new virtual menu freedesktop.org draft standard. Here are
+the pros and cons of each of these options:
+
+servicemenu style:
+ o CONS: no extra flexibility, still have to do most checking manually
+ o PROS: familiar format
+
+virtual menu style:
+ o CONS: it's XML and that's way more trouble that we need to go through, and it
+ isn't really designed with this sort of menu in mind
+ o PROS: it's becoming a standard for desktop menus
+
+
+Example
+=======
+[General]
+Icon=konqueror
+Name=Internet
+NumTasks=3
+Hidden=true
+
+[Task0]
+Name=Browse the web
+DesktopFile=applications/konqbrowser.desktop
+
+[Task1]
+Name=EMail
+DesktopFile=Internet/KMail.desktop
+
+[Task2]
+Name=VNC
+DesktopFile=Internet/keystone.desktop
diff --git a/kicker/menuext/tom/destinations b/kicker/menuext/tom/destinations
new file mode 100644
index 000000000..6e075237f
--- /dev/null
+++ b/kicker/menuext/tom/destinations
@@ -0,0 +1,18 @@
+[General]
+NumTasks=4
+
+[Task0]
+Name=Consult Manuals
+DesktopFile=Help.desktop
+
+[Task1]
+Name=Find Files
+DesktopFile=Kfind.desktop
+
+[Task2]
+Name=Adjust Settings
+DesktopFile=KControl.desktop
+
+[Task3]
+Name=Browse My Files
+DesktopFile=Home.desktop
diff --git a/kicker/menuext/tom/tom.cc b/kicker/menuext/tom/tom.cc
new file mode 100644
index 000000000..80ea3e71d
--- /dev/null
+++ b/kicker/menuext/tom/tom.cc
@@ -0,0 +1,855 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Aaron J. Seigo <[email protected]>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License version 2 as published by the Free Software Foundation.
+
+ 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; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#include <iostream>
+using namespace std;
+
+#include <unistd.h>
+#include <pwd.h>
+#include <sys/types.h>
+
+#include <qdir.h>
+#include <qimage.h>
+#include <qlabel.h>
+#include <qlayout.h>
+#include <qpainter.h>
+#include <qregexp.h>
+#include <qsettings.h>
+#include <qstyle.h>
+#include <qfile.h>
+
+#include <dcopclient.h>
+#include <kapplication.h>
+#include <kcombobox.h>
+#include <kdialog.h>
+#include <kglobal.h>
+#include <kiconloader.h>
+#include <kiconeffect.h>
+#include <klocale.h>
+#include <kmessagebox.h>
+#include <kmimetype.h>
+#include <kpixmap.h>
+#include <krecentdocument.h>
+#include <kservice.h>
+#include <kservicegroup.h>
+#include <kstandarddirs.h>
+#include <kstdaction.h>
+#include <ksycocaentry.h>
+
+#include "menuinfo.h"
+#include "service_mnu.h"
+#include "kicker.h"
+#include "tom.h"
+
+const int configureMenuID = 1000;
+const int contextMenuTitleID = 10000;
+const int destMenuTitleID = 10001;
+
+extern "C"
+{
+ KDE_EXPORT void* init_kickermenu_tom()
+ {
+ KGlobal::locale()->insertCatalogue("libkickermenu_tom");
+ return new TOMFactory;
+ }
+};
+
+TOMFactory::TOMFactory(QObject *parent, const char *name)
+: KLibFactory(parent, name)
+{
+}
+
+QObject* TOMFactory::createObject(QObject *parent, const char *name, const char*, const QStringList&)
+{
+ return new TOM((QWidget*)parent, name);
+}
+
+#include <qmenudata.h>
+/*
+ * TODO: switch the backgroundmode when translucency turns on/off
+ * TODO: catch font changes too?
+ */
+class runMenuWidget : public QWidget, public QMenuItem
+{
+ public:
+ runMenuWidget(KPopupMenu* parent, int index)
+ : QWidget (parent),
+ m_menu(parent),
+ m_index(index)
+ {
+ setFocusPolicy(StrongFocus);
+
+ QHBoxLayout* runLayout = new QHBoxLayout(this);
+ textRect = fontMetrics().boundingRect(i18n("Run:"));
+ runLayout->setSpacing(KDialog::spacingHint());
+ runLayout->addSpacing((KDialog::spacingHint() * 3) + KIcon::SizeMedium + textRect.width());
+ icon = DesktopIcon("run", KIcon::SizeMedium);
+ /*QLabel* l1 = new QLabel(this);
+ QPixmap foo = DesktopIcon("run", KIcon::SizeMedium);
+ cout << "foo is: " << foo.width() << " by " << foo.height() << endl;
+ l1->setPixmap(foo);
+ runLayout->addWidget(l1);*/
+ /*QLabel* l2 = new QLabel(i18n("&Run: "), this);
+ l2->setBackgroundMode(Qt::X11ParentRelative, Qt::X11ParentRelative);
+ l2->setBuddy(this);
+ runLayout->addWidget(l2);*/
+ m_runEdit = new KHistoryCombo(this);
+ m_runEdit->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred);
+ runLayout->addWidget(m_runEdit, 10);
+ runLayout->addSpacing(KDialog::spacingHint());
+
+ QSettings settings;
+ if (settings.readEntry("/KStyle/Settings/MenuTransparencyEngine", "Disabled") != "Disabled")
+ {
+ setBackgroundMode(Qt::X11ParentRelative, Qt::X11ParentRelative);
+ //l1->setBackgroundMode(Qt::X11ParentRelative, Qt::X11ParentRelative);
+ //l2->setBackgroundMode(Qt::X11ParentRelative, Qt::X11ParentRelative);
+ m_runEdit->setBackgroundMode(Qt::X11ParentRelative, Qt::X11ParentRelative);
+ }
+ else
+ {
+ /*setBackgroundMode(Qt::NoBackground, Qt::NoBackground);
+ l1->setBackgroundMode(Qt::NoBackground, Qt::NoBackground);
+ l2->setBackgroundMode(Qt::NoBackground, Qt::NoBackground);
+ m_runEdit->setBackgroundMode(Qt::NoBackground, Qt::NoBackground);*/
+ //l1->setAutoMask(true);
+ //l1->setBackgroundMode(Qt::NoBackground, Qt::NoBackground);
+ //l2->setBackgroundMode(Qt::X11ParentRelative, Qt::X11ParentRelative);
+ //m_runEdit->setBackgroundMode(Qt::X11ParentRelative, Qt::X11ParentRelative);
+ }
+
+ setMinimumHeight(KIcon::SizeMedium + 2);
+ }
+ ~runMenuWidget() {}
+
+
+ void paintEvent(QPaintEvent * /*e*/)
+ {
+ QPainter p(this);
+ QRect r(rect());
+ // ew, nasty hack. may result in coredumps due to horrid C-style cast???
+ kapp->style().drawControl(QStyle::CE_PopupMenuItem, &p, m_menu, r, palette().active(), QStyle::Style_Enabled,
+ QStyleOption(static_cast<QMenuItem*>(this), 0, KIcon::SizeMedium ));
+ p.drawPixmap(KDialog::spacingHint(), 1, icon);
+ p.drawText((KDialog::spacingHint() * 2) + KIcon::SizeMedium, textRect.height() + ((height() - textRect.height()) / 2), i18n("Run:"));
+ }
+
+ void focusInEvent (QFocusEvent* e)
+ {
+ if (!e || e->gotFocus())
+ {
+ m_menu->setActiveItem(m_index);
+ m_runEdit->setFocus();
+ }
+ }
+
+ void enterEvent(QEvent*)
+ {
+ focusInEvent(0);
+ }
+
+ private:
+ KPopupMenu* m_menu;
+ KHistoryCombo* m_runEdit;
+ QPixmap icon;
+ QRect textRect;
+ int m_index;
+};
+
+TOM::TOM(QWidget *parent, const char *name)
+ : KPanelMenu(parent, name),
+ m_maxIndex(0)
+{
+ disableAutoClear();
+ m_submenus.setAutoDelete(true);
+ setCaption(i18n("Task-Oriented Menu"));
+
+ // KMenu legacy: support some menu config options
+ KConfig* config = KGlobal::config();
+ config->setGroup("menus");
+ m_detailedTaskEntries = config->readBoolEntry("DetailedMenuEntries", false);
+ if (m_detailedTaskEntries)
+ {
+ m_detailedNamesFirst = config->readBoolEntry("DetailedEntriesNamesFirst", false);
+ }
+
+ m_isImmutable = Kicker::kicker()->isImmutable() || config->groupIsImmutable("TOM");
+ config->setGroup("TOM");
+ m_largerFont = font();
+ m_largerFont = config->readFontEntry("Font", &m_largerFont);
+}
+
+TOM::~TOM()
+{
+ slotClear();
+}
+
+void TOM::initializeRecentApps(QPopupMenu* menu)
+{
+ /*
+ * TODO: make this real
+ * add a KDE-wide "commands run" registry
+ */
+
+ if (!m_isImmutable)
+ {
+ menu->insertSeparator();
+ menu->insertItem(i18n("Configure This Menu"), configureMenuID);
+ }
+}
+
+void TOM::initializeRecentDocs()
+{
+ m_recentDocsMenu->clear();
+ m_recentDocsMenu->insertItem(SmallIconSet("history_clear"), i18n("Clear History"),
+ this, SLOT(clearRecentDocHistory()));
+ m_recentDocsMenu->insertSeparator();
+
+ m_recentDocURLs = KRecentDocument::recentDocuments();
+
+ if (m_recentDocURLs.isEmpty())
+ {
+ setItemEnabled(m_recentDocsMenu->insertItem(i18n("No Entries")), false);
+ return;
+ }
+
+ int id = 0;
+ for (QStringList::ConstIterator it = m_recentDocURLs.begin();
+ it != m_recentDocURLs.end();
+ ++it)
+ {
+ /*
+ * TODO: make the number of visible items configurable?
+ */
+
+ KDesktopFile f(*it, true /* read only */);
+ m_recentDocsMenu->insertItem(DesktopIcon(f.readIcon(), KIcon::SizeMedium),
+ f.readName().replace('&', "&&"), id);
+ ++id;
+ }
+}
+
+int TOM::appendTaskGroup(KConfig& config, bool inSubMenu)
+{
+ if (!config.hasGroup("General"))
+ {
+ return 0;
+ }
+
+ config.setGroup("General");
+
+ if (config.readBoolEntry("Hidden", false))
+ {
+ return 0;
+ }
+
+ QString name = config.readEntry("Name", i18n("Unknown"));
+ QString icon = config.readEntry("Icon");
+ int numTasks = config.readNumEntry("NumTasks", 0);
+
+ if (numTasks < 1)
+ {
+ return 0;
+ }
+
+ KPopupMenu* taskGroup;
+ if( inSubMenu )
+ {
+ taskGroup = new KPopupMenu(this);
+
+ if (icon != QString::null)
+ {
+ insertItem(DesktopIcon(icon, KIcon::SizeMedium), name, taskGroup);
+ }
+ else
+ {
+ insertItem(name, taskGroup);
+ }
+ }
+ else
+ {
+ taskGroup = this;
+ }
+
+ int foundTasks = 0;
+ for (int i = 0; i < numTasks; ++i)
+ {
+ QString groupName = QString("Task%1").arg(i);
+
+ if (config.hasGroup(groupName))
+ {
+ config.setGroup(groupName);
+
+ if (config.readBoolEntry("Hidden", false))
+ {
+ continue;
+ }
+
+ QString name = config.readEntry("Name");
+ // in case the name contains an ampersand, double 'em up
+ name.replace("&", "&&");
+ QString desktopfile = config.readPathEntry("DesktopFile");
+ KService::Ptr pService = KService::serviceByDesktopPath(desktopfile);
+
+ if (!pService)
+ {
+ pService = KService::serviceByDesktopName(desktopfile);
+
+ if (!pService)
+ {
+ continue;
+ }
+ }
+
+ QString execName = pService->name();
+ QString icon = pService->icon();
+
+ if (m_detailedTaskEntries && !execName.isEmpty())
+ {
+ QString tmp = i18n("%1 (%2)");
+
+ if (m_detailedNamesFirst)
+ {
+ name = tmp.arg(execName).arg(name);
+ }
+ else
+ {
+ name = tmp.arg(name).arg(execName);
+ }
+ }
+
+ ++m_maxIndex;
+ if (icon.isEmpty())
+ {
+ taskGroup->insertItem(name, m_maxIndex);
+ }
+ else
+ {
+
+ QIconSet iconset = BarIconSet(icon, 22);
+ if (iconset.isNull())
+ taskGroup->insertItem(name, m_maxIndex);
+ else
+ taskGroup->insertItem(iconset, name, m_maxIndex);
+
+ }
+
+ m_tasks.insert(m_maxIndex, pService);
+ ++foundTasks;
+ }
+ }
+
+ // if we end up with an empty task group, get rid of the menu
+ if (foundTasks == 0)
+ {
+ if (inSubMenu)
+ {
+ delete taskGroup;
+ }
+
+ return 0;
+ }
+
+ connect(taskGroup, SIGNAL(activated(int)), this, SLOT(runTask(int)));
+ // so we have an actual task group menu with tasks, let's add it
+
+ if (inSubMenu)
+ {
+ QObject::connect(taskGroup, SIGNAL(aboutToShowContextMenu(KPopupMenu*, int, QPopupMenu*)),
+ this, SLOT(contextualizeRMBmenu(KPopupMenu*, int, QPopupMenu*)));
+
+ m_submenus.append(taskGroup);
+
+ taskGroup->setFont(m_largerFont);
+ taskGroup->setKeyboardShortcutsEnabled(true);
+
+ if (!m_isImmutable && !config.isImmutable())
+ {
+ taskGroup->insertSeparator();
+ taskGroup->insertItem("Modify These Tasks", configureMenuID);
+ QPopupMenu* rmbMenu = taskGroup->contextMenu();
+ rmbMenu->setFont(m_largerFont);
+ KPopupTitle* title = new KPopupTitle();
+ title->setText(i18n("%1 Menu Editor").arg(name));
+ rmbMenu->insertItem(title, contextMenuTitleID);
+ rmbMenu->insertItem(i18n("Add This Task to Panel"));
+ rmbMenu->insertItem(i18n("Modify This Task..."));
+ rmbMenu->insertItem(i18n("Remove This Task..."), this, SLOT(removeTask()));
+ rmbMenu->insertItem(i18n("Insert New Task..."));
+ }
+ }
+
+ return foundTasks;
+}
+/*
+ * TODO: track configuration file changes.
+void TOM::configChanged()
+{
+ KPanelMenu::configChanged();
+ setInitialized(false);
+ initialize();
+}
+*/
+void TOM::initialize()
+{
+ if (initialized())
+ {
+ return;
+ }
+
+ setInitialized(true);
+ m_submenus.clear();
+ m_tasks.clear();
+ clear();
+
+ /* hack to make items larger. should use something better?
+ m_largerFont = font();
+ if (m_largerFont.pointSize() < 18)
+ {
+ m_largerFont.setPointSize(18);
+ }
+ setFont(m_largerFont);*/
+
+
+ /*if (!loadSidePixmap())
+ {
+ m_sidePixmap = m_sideTilePixmap = QPixmap();
+ }
+ else
+ {
+ connect(kapp, SIGNAL(kdisplayPaletteChanged()), SLOT(paletteChanged()));
+ }*/
+
+ // TASKS
+ insertTitle(i18n("Tasks"), contextMenuTitleID);
+
+ QStringList dirs = KGlobal::dirs()->findDirs("data", "kicker/tom/");
+ QStringList::ConstIterator dIt = dirs.begin();
+ QStringList::ConstIterator dEnd = dirs.end();
+
+ for (; dIt != dEnd; ++dIt )
+ {
+ QDir dir(*dIt);
+
+ QStringList entries = dir.entryList("*rc", QDir::Files);
+ QStringList::ConstIterator eIt = entries.begin();
+ QStringList::ConstIterator eEnd = entries.end();
+
+ for (; eIt != eEnd; ++eIt )
+ {
+ KConfig config(*dIt + *eIt);
+ appendTaskGroup(config);
+ }
+ }
+
+ PanelServiceMenu* moreApps = new PanelServiceMenu(QString::null, QString::null, this, "More Applications");
+ moreApps->setFont(m_largerFont);
+ insertItem(DesktopIcon("misc", KIcon::SizeMedium), i18n("More Applications"), moreApps);
+ m_submenus.append(moreApps);
+
+ if (!m_isImmutable)
+ {
+ insertSeparator();
+ // TODO: connect to taskgroup edits
+ insertItem("Modify The Above Tasks");
+ }
+
+ // DESTINATIONS
+ insertTitle(i18n("Destinations"), destMenuTitleID);
+ int numDests = 0;
+ QString destinationsConfig = locate("appdata", "tom/destinations");
+
+ if (!destinationsConfig.isEmpty() && QFile::exists(destinationsConfig))
+ {
+ KConfig config(destinationsConfig);
+ numDests = appendTaskGroup(config, false);
+ }
+
+ if (numDests == 0)
+ {
+ removeItem(destMenuTitleID);
+ }
+ else if (kapp->authorize("run_command"))
+ {
+ insertItem(DesktopIcon("run", KIcon::SizeMedium), i18n("Run Command..."), this, SLOT(runCommand()));
+ }
+
+ // RECENTLY USED ITEMS
+ insertTitle(i18n("Recently Used Items"), contextMenuTitleID);
+
+ m_recentDocsMenu = new KPopupMenu(this, "recentDocs");
+ m_recentDocsMenu->setFont(m_largerFont);
+ connect(m_recentDocsMenu, SIGNAL(aboutToShow()), this, SLOT(initializeRecentDocs()));
+ connect(m_recentDocsMenu, SIGNAL(activated(int)), this, SLOT(openRecentDocument(int)));
+ insertItem(DesktopIcon("document", KIcon::SizeMedium), i18n("Recent Documents"), m_recentDocsMenu);
+ m_submenus.append(m_recentDocsMenu);
+
+ KPopupMenu* recentApps = new KPopupMenu(this, "recentApps");
+ recentApps->setFont(m_largerFont);
+ recentApps->setKeyboardShortcutsEnabled(true);
+ initializeRecentApps(recentApps);
+ insertItem(i18n("Recent Applications"), recentApps);
+ m_submenus.append(recentApps);
+
+ // SPECIAL ITEMS
+ insertTitle(i18n("Special Items"), contextMenuTitleID);
+
+ // if we have no destinations, put the run command here
+ if (numDests == 0 && kapp->authorize("run_command"))
+ {
+ insertItem(DesktopIcon("run", KIcon::SizeMedium), i18n("Run Command..."), this, SLOT(runCommand()));
+ }
+
+
+ KConfig* config = KGlobal::config();
+ QStringList menu_ext = config->readListEntry("Extensions");
+ if (!menu_ext.isEmpty())
+ {
+ bool needSeparator = false;
+ for (QStringList::ConstIterator it = menu_ext.begin(); it != menu_ext.end(); ++it)
+ {
+ MenuInfo info(*it);
+ if (!info.isValid())
+ {
+ continue;
+ }
+
+ KPanelMenu *menu = info.load();
+ if (menu)
+ {
+ ++m_maxIndex;
+ insertItem(DesktopIcon(info.icon(), KIcon::SizeMedium), info.name(), menu, m_maxIndex);
+ m_submenus.append(menu);
+ needSeparator = true;
+ }
+ }
+
+ if (needSeparator)
+ {
+ insertSeparator();
+ }
+ }
+
+
+ QString username;
+ struct passwd *userInfo = getpwuid(getuid());
+ if (userInfo)
+ {
+ username = QString::fromLocal8Bit(userInfo->pw_gecos);
+ if (username.find(',') != -1)
+ {
+ // Remove everything from and including first comma
+ username.truncate(username.find(','));
+ }
+
+ if (username.isEmpty())
+ {
+ username = QString::fromLocal8Bit(userInfo->pw_name);
+ }
+ }
+
+ insertItem(DesktopIcon("exit", KIcon::SizeMedium),
+ i18n("Logout %1").arg(username), this, SLOT(logout()));
+}
+
+void TOM::reload()
+{
+ setInitialized(false);
+ initialize();
+}
+
+void TOM::contextualizeRMBmenu(KPopupMenu* menu, int menuItem, QPopupMenu* ctxMenu)
+{
+ if (menuItem == configureMenuID)
+ {
+ menu->hideContextMenu();
+ return;
+ }
+
+ ctxMenu->removeItem(contextMenuTitleID);
+ QString text = menu->text(menuItem);
+ int parens = text.find('(') - 1;
+ if (parens > 0)
+ {
+ text = text.left(parens);
+ }
+ KPopupTitle* title = new KPopupTitle();
+ title->setText(i18n("The \"%2\" Task").arg(text));
+ ctxMenu->insertItem(title, contextMenuTitleID, 0);
+}
+
+void TOM::slotClear()
+{
+ KPanelMenu::slotClear();
+ m_submenus.clear();
+ m_tasks.clear();
+}
+
+void TOM::slotExec(int /* id */)
+{
+ // Reimplemented because we have to
+}
+
+void TOM::removeTask()
+{
+ // TODO: write this change out to the appropriate config file
+ QString task = KPopupMenu::contextMenuFocus()->text(KPopupMenu::contextMenuFocusItem());
+ if (KMessageBox::warningContinueCancel(this,
+ i18n("<qt>Are you sure you want to remove the <strong>%1</strong> task?<p>"
+ "<em>Tip: You can restore the task after it has been removed by selecting the &quot;Modify These Tasks&quot; entry</em></qt>").arg(task),
+ i18n("Remove Task?"),KStdGuiItem::del()) == KMessageBox::Continue)
+ {
+ m_tasks.remove(KPopupMenu::contextMenuFocusItem());
+ KPopupMenu::contextMenuFocus()->removeItem(KPopupMenu::contextMenuFocusItem());
+ }
+}
+
+// TODO: should we have a side graphic like the K Menu? personally, i don't
+// think it helps usability. it's nice for branding, but that's it. perhaps a
+// top image, or something blended with the actual menu background? deffinitely
+// a job for a graphic artist.
+/*
+ * if this goes in, it should be shared w/the kmenu
+ *
+bool TOM::loadSidePixmap()
+{
+ KConfig *config = KGlobal::config();
+ QColor color = palette().active().highlight();
+ QImage image;
+
+ config->setGroup("WM");
+ QColor activeTitle = config->readColorEntry("activeBackground", &color);
+ QColor inactiveTitle = config->readColorEntry("inactiveBackground", &color);
+
+ config->setGroup("KMenu");
+ if (!config->readBoolEntry("Usem_sidePixmap", true))
+ return false;
+
+ // figure out which color is most suitable for recoloring to
+ int h1, s1, v1, h2, s2, v2, h3, s3, v3;
+ activeTitle.hsv(&h1, &s1, &v1);
+ inactiveTitle.hsv(&h2, &s2, &v2);
+ palette().active().background().hsv(&h3, &s3, &v3);
+
+ if ( (abs(h1-h3)+abs(s1-s3)+abs(v1-v3) < abs(h2-h3)+abs(s2-s3)+abs(v2-v3)) &&
+ ((abs(h1-h3)+abs(s1-s3)+abs(v1-v3) < 32) || (s1 < 32)) && (s2 > s1))
+ color = inactiveTitle;
+ else
+ color = activeTitle;
+
+ // limit max/min brightness
+ int r, g, b;
+ color.rgb(&r, &g, &b);
+ int gray = qGray(r, g, b);
+ if (gray > 180) {
+ r = (r - (gray - 180) < 0 ? 0 : r - (gray - 180));
+ g = (g - (gray - 180) < 0 ? 0 : g - (gray - 180));
+ b = (b - (gray - 180) < 0 ? 0 : b - (gray - 180));
+ } else if (gray < 76) {
+ r = (r + (76 - gray) > 255 ? 255 : r + (76 - gray));
+ g = (g + (76 - gray) > 255 ? 255 : g + (76 - gray));
+ b = (b + (76 - gray) > 255 ? 255 : b + (76 - gray));
+ }
+ color.setRgb(r, g, b);
+
+ QString sideName = config->readEntry("SideName", "kside.png");
+ QString sideTileName = config->readEntry("SideTileName", "kside_tile.png");
+
+ image.load(locate("data", "kicker/pics/" + sideName));
+
+ if (image.isNull())
+ {
+ return false;
+ }
+
+ KIconEffect::colorize(image, color, 1.0);
+ m_sidePixmap.convertFromImage(image);
+
+ image.load(locate("data", "kicker/pics/" + sideTileName));
+
+ if (image.isNull())
+ {
+ return false;
+ }
+
+ KIconEffect::colorize(image, color, 1.0);
+ m_sideTilePixmap.convertFromImage(image);
+
+ if (m_sidePixmap.width() != m_sideTilePixmap.width())
+ {
+ return false;
+ }
+
+ // pretile the pixmap to a height of at least 100 pixels
+ if (m_sideTilePixmap.height() < 100)
+ {
+ int tiles = (int)(100 / m_sideTilePixmap.height()) + 1;
+ QPixmap preTiledPixmap(m_sideTilePixmap.width(), m_sideTilePixmap.height() * tiles);
+ QPainter p(&preTiledPixmap);
+ p.drawTiledPixmap(preTiledPixmap.rect(), m_sideTilePixmap);
+ m_sideTilePixmap = preTiledPixmap;
+ }
+
+ return true;
+}
+
+void TOM::paletteChanged()
+{
+ if (!loadSidePixmap())
+ m_sidePixmap = m_sideTilePixmap = QPixmap();
+}
+
+void TOM::setMinimumSize(const QSize & s)
+{
+ KPanelMenu::setMinimumSize(s.width() + m_sidePixmap.width(), s.height());
+}
+
+void TOM::setMaximumSize(const QSize & s)
+{
+ KPanelMenu::setMaximumSize(s.width() + m_sidePixmap.width(), s.height());
+}
+
+void TOM::setMinimumSize(int w, int h)
+{
+ KPanelMenu::setMinimumSize(w + m_sidePixmap.width(), h);
+}
+
+void TOM::setMaximumSize(int w, int h)
+{
+ KPanelMenu::setMaximumSize(w + m_sidePixmap.width(), h);
+}
+
+QRect TOM::sideImageRect()
+{
+ return QStyle::visualRect( QRect( frameWidth(), frameWidth(), m_sidePixmap.width(),
+ height() - 2*frameWidth() ), this );
+}
+
+void TOM::resizeEvent(QResizeEvent * e)
+{
+ setFrameRect( QStyle::visualRect( QRect( m_sidePixmap.width(), 0,
+ width() - m_sidePixmap.width(), height() ), this ) );
+}
+
+void TOM::paintEvent(QPaintEvent * e)
+{
+ if (m_sidePixmap.isNull()) {
+ KPanelMenu::paintEvent(e);
+ return;
+ }
+
+ QPainter p(this);
+
+ style().drawPrimitive( QStyle::PE_PanelPopup, &p,
+ QRect( 0, 0, width(), height() ),
+ colorGroup(), QStyle::Style_Default,
+ QStyleOption( frameWidth(), 0 ) );
+
+ QRect r = sideImageRect();
+ r.setBottom( r.bottom() - m_sidePixmap.height() );
+ p.drawTiledPixmap( r, m_sideTilePixmap );
+
+ r = sideImageRect();
+ r.setTop( r.bottom() - m_sidePixmap.height() );
+ p.drawPixmap( r, m_sidePixmap );
+
+ drawContents( &p );
+}
+
+QMouseEvent TOM::translateMouseEvent( QMouseEvent* e )
+{
+ QRect side = sideImageRect();
+
+ if ( !side.contains( e->pos() ) )
+ return *e;
+
+ QPoint newpos( e->pos() );
+ QApplication::reverseLayout() ?
+ newpos.setX( newpos.x() - side.width() ) :
+ newpos.setX( newpos.x() + side.width() );
+ QPoint newglobal( e->globalPos() );
+ QApplication::reverseLayout() ?
+ newglobal.setX( newpos.x() - side.width() ) :
+ newglobal.setX( newpos.x() + side.width() );
+
+ return QMouseEvent( e->type(), newpos, newglobal, e->button(), e->state() );
+}
+
+void TOM::mousePressEvent(QMouseEvent * e)
+{
+ QMouseEvent newEvent = translateMouseEvent(e);
+ KPanelMenu::mousePressEvent( &newEvent );
+}
+
+void TOM::mouseReleaseEvent(QMouseEvent *e)
+{
+ QMouseEvent newEvent = translateMouseEvent(e);
+ KPanelMenu::mouseReleaseEvent( &newEvent );
+}
+
+void TOM::mouseMoveEvent(QMouseEvent *e)
+{
+ QMouseEvent newEvent = translateMouseEvent(e);
+ KPanelMenu::mouseMoveEvent( &newEvent );
+}*/
+
+extern int kicker_screen_number;
+
+void TOM::runCommand()
+{
+ QByteArray data;
+ QCString appname( "kdesktop" );
+ if ( kicker_screen_number )
+ appname.sprintf("kdesktop-screen-%d", kicker_screen_number);
+
+ kapp->updateRemoteUserTimestamp( appname );
+ kapp->dcopClient()->send( appname, "KDesktopIface",
+ "popupExecuteCommand()", data );
+}
+
+void TOM::runTask(int id)
+{
+ if (!m_tasks.contains(id)) return;
+
+ kapp->propagateSessionManager();
+ KApplication::startServiceByDesktopPath(m_tasks[id]->desktopEntryPath(),
+ QStringList(), 0, 0, 0, "", true);
+}
+
+void TOM::clearRecentDocHistory()
+{
+ KRecentDocument::clear();
+}
+
+void TOM::openRecentDocument(int id)
+{
+ if (id >= 0)
+ {
+ kapp->propagateSessionManager();
+ KURL u;
+ u.setPath(m_recentDocURLs[id]);
+ KDEDesktopMimeType::run(u, true);
+ }
+}
+
+void TOM::logout()
+{
+ kapp->requestShutDown();
+}
+
+#include "tom.moc"
diff --git a/kicker/menuext/tom/tom.desktop b/kicker/menuext/tom/tom.desktop
new file mode 100644
index 000000000..f59a4beab
--- /dev/null
+++ b/kicker/menuext/tom/tom.desktop
@@ -0,0 +1,75 @@
+[Desktop Entry]
+Name=TOM
+Name[bg]=Задачно меню
+Name[eo]=PTMS
+Name[hi]=टीओएम
+Name[te]=టామ్
+Name[zh_CN]=任务菜单
+Comment=A task oriented menu system
+Comment[af]='n Taak geörienteerde kieslys stelsel
+Comment[ar]=نظام قوائم وظيفيّ المنحى
+Comment[be]=Сістэма меню, арыентаваная на выкананне шматлікіх задачаў
+Comment[bg]=Меню, ориентирано към изпълнение на задачи
+Comment[bn]=টাস্ক ওরিয়েন্টেড মেনু সিস্টেম
+Comment[bs]=Sistem menija orjentisan na zadatke
+Comment[ca]=Una tasca orientada al menú del sistema
+Comment[cs]=Úkolově orientovaný systém nabídek
+Comment[csb]=Systema menu skòncentrowónô na dzejaniô
+Comment[cy]=Cysawd dewislenni a gyfeirir at dasgau
+Comment[da]=Et opgaveorienteret menusystem
+Comment[de]=Aufgabenorientiertes Menüsystem
+Comment[el]=Ένα σύστημα μενού προσανατολισμένο στην εργασία
+Comment[eo]=Pertaska menusistemo
+Comment[es]=Sistema de menú orientado a tareas
+Comment[et]=Ülesandele orienteeritud menüüsüsteem
+Comment[eu]=Atazetara zuzendutako menu sistema
+Comment[fa]=یک سیستم گزینگان جهت‌دار تکلیف
+Comment[fi]=Tehtäväpohjainen valikkojärjestelmä
+Comment[fr]=Un menu système par tâches
+Comment[fy]=In taakoriïntearre menusysteem
+Comment[ga]=Córas roghchláir dírithe ar thascanna
+Comment[gl]=Un menu do sistema orientado a tarefas
+Comment[he]=מערכת תפריטים מונחת משימות
+Comment[hi]=कार्य अनुकूल मेन्यू तंत्र
+Comment[hr]=Sustav izbornika orijentiran zadacima
+Comment[hu]=Feladatorientált menürendszer
+Comment[is]=Verkefnabyggt valmyndakerfi
+Comment[it]=Un sistema di menu organizzato per funzione
+Comment[ja]=作業タスクベースのメニューシステム
+Comment[ka]=ამოცანაზე ორიენტირებული მენიუს სისტემა
+Comment[kk]=Тапсырмаларға арналған мәзір жүйесі
+Comment[km]=ប្រព័ន្ធ​ម៉ឺនុយ​ដែល​សម្រប​តាម​ភារកិច្ច
+Comment[lt]=Į užduotis orientuota meniu sistema
+Comment[lv]=Uz darbībām orientēta izvēlņu sistēma
+Comment[mk]=Систем од менија ориентиран кон задачи
+Comment[mn]=Үйлдэлд чиглэсэн цэсийн систем
+Comment[ms]=Sistem menu berorientasikan tugas
+Comment[mt]=Menu imqassam skond ix-xogħol li trid tagħmel
+Comment[nb]=Et oppgaveorientert menysystem
+Comment[nds]=Menüsysteem, dat an Opgaven utricht is
+Comment[ne]=कार्य उन्मुख मेनु प्रणाली
+Comment[nl]=Een taakgeörienteerd menusysteem
+Comment[nn]=Eit oppgåveorientert menysystem
+Comment[pa]=ਇੱਕ ਕੰਮ ਆਧਾਰਿਤ ਮੇਨੂ ਸਿਸਟਮ
+Comment[pl]=System menu zorientowany na zadania
+Comment[pt]=Um sistema de menus orientado por tarefas
+Comment[pt_BR]=Um sistema de menu orientado a tarefa
+Comment[ro]=Un meniu orientat pe probleme de rezolvat
+Comment[ru]=Система меню, ориентированная на задачи
+Comment[rw]=Ibikubiyemo Sisitemu bijyanye n'Igikorwa
+Comment[sk]=Systém menu podľa činnosti
+Comment[sl]=Opravilno narejen menijski sistem
+Comment[sr]=Систем менија оријентисан према задацима
+Comment[sr@Latn]=Sistem menija orijentisan prema zadacima
+Comment[sv]=Uppgiftsrelaterat menysystem
+Comment[ta]=பணி சார்ந்த பட்டியல் அமைப்பு
+Comment[th]=ระบบเมนูที่ใช้แนวทางของงาน
+Comment[tr]=Görev yönelimli bir menü sistemi
+Comment[tt]=Yökkä yünälgän saylaq sisteme
+Comment[uk]=Система меню орієнтована на завдання
+Comment[vi]=Hệ thực đơn hướng tác vụ
+Comment[wa]=Ene dressêye sistinme fwaite po les bouyes
+Comment[zh_CN]=面向任务的菜单系统
+Comment[zh_TW]=一個工作導向的選單系統
+Icon=kmenu
+X-KDE-Library=kickermenu_tom
diff --git a/kicker/menuext/tom/tom.h b/kicker/menuext/tom/tom.h
new file mode 100644
index 000000000..663cb05d6
--- /dev/null
+++ b/kicker/menuext/tom/tom.h
@@ -0,0 +1,110 @@
+/* This file is part of the KDE project
+ Copyright (C) 2003 Aaron J. Seigo <[email protected]>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public
+ License version 2 as published by the Free Software Foundation.
+
+ 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; see the file COPYING. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+
+#ifndef __tom_h_
+#define __tom_h_
+
+#include <qpixmap.h>
+
+#include <kpanelmenu.h>
+#include <klibloader.h>
+
+class KPopupMenu;
+class QPopupMenu;
+
+typedef QPtrList<QPopupMenu> PopupMenuList;
+typedef QMap<int, KService::Ptr> TaskMap;
+
+class TOM : public KPanelMenu
+{
+ Q_OBJECT
+
+ public:
+ TOM(QWidget *parent = 0, const char *name = 0);
+ ~TOM();
+
+ // for the side image
+ /*void setMinimumSize(const QSize &);
+ void setMaximumSize(const QSize &);
+ void setMinimumSize(int, int);
+ void setMaximumSize(int, int); */
+
+ protected slots:
+ void slotClear();
+ void slotExec(int);
+ //void configChanged();
+ void initialize();
+ void contextualizeRMBmenu(KPopupMenu* menu, int menuItem, QPopupMenu* ctxMenu);
+ //void paletteChanged();
+ void clearRecentDocHistory();
+ void runCommand();
+ void runTask(int id);
+ void initializeRecentDocs();
+ void openRecentDocument(int id);
+ void logout();
+
+ /*
+ * slots for the RMB menu on task group
+ */
+ void removeTask();
+
+ protected:
+ void reload();
+
+ int appendTaskGroup(KConfig& config, bool inSubMenu = true );
+ void initializeRecentApps(QPopupMenu* menu);
+ //int insertTOMTitle(QPopupMenu* menu, const QString &text, int id = -1, int index = -1);
+
+ /*
+ * this stuff should be shared w/the kmenu
+
+ QRect sideImageRect();
+ QMouseEvent translateMouseEvent( QMouseEvent* e );
+ void resizeEvent(QResizeEvent *);
+ void paintEvent(QPaintEvent *);
+ void mousePressEvent(QMouseEvent *);
+ void mouseReleaseEvent(QMouseEvent *);
+ void mouseMoveEvent(QMouseEvent *);
+ bool loadSidePixmap();
+
+ QPixmap m_sidePixmap;
+ QPixmap m_sideTilePixmap;*/
+ PopupMenuList m_submenus;
+ QFont m_largerFont;
+ int m_maxIndex;
+ bool m_isImmutable;
+ bool m_detailedTaskEntries;
+ bool m_detailedNamesFirst;
+ TaskMap m_tasks;
+ KPopupMenu* m_recentDocsMenu;
+ QStringList m_recentDocURLs;
+};
+
+class TOMFactory : public KLibFactory
+{
+ public:
+ TOMFactory(QObject *parent = 0, const char *name = 0);
+
+ protected:
+ QObject* createObject(QObject *parent = 0, const char *name = 0,
+ const char *classname = "QObject",
+ const QStringList& args = QStringList());
+};
+
+
+#endif