File plasma-kickoff-newly-collapsing.diff of Package kdebase4-workspace

diff --git a/plasma/desktop/applets/kickoff/applet/applet.cpp b/plasma/desktop/applets/kickoff/applet/applet.cpp
index 8de1efe..c597741 100644
--- a/plasma/desktop/applets/kickoff/applet/applet.cpp
+++ b/plasma/desktop/applets/kickoff/applet/applet.cpp
@@ -166,10 +166,12 @@ void LauncherApplet::createConfigurationInterface(KConfigDialog *parent)
     d->ui.switchOnHoverCheckBox->setChecked(d->launcher->switchTabsOnHover());
     d->ui.appsByNameCheckBox->setChecked(d->launcher->showAppsByName());
     d->ui.showRecentlyInstalledCheckBox->setChecked(d->launcher->showRecentlyInstalled());
+    d->ui.reduceMenuDepthCheckBox->setChecked(d->launcher->reduceMenuDepth());
     connect(d->ui.iconButton, SIGNAL(iconChanged(QString)), parent, SLOT(settingsModified()));
     connect(d->ui.switchOnHoverCheckBox, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
     connect(d->ui.appsByNameCheckBox, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
     connect(d->ui.showRecentlyInstalledCheckBox, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
+    connect(d->ui.reduceMenuDepthCheckBox, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
 }
 
 void LauncherApplet::popupEvent(bool show)
@@ -206,6 +208,7 @@ void LauncherApplet::configAccepted()
     bool switchTabsOnHover = d->ui.switchOnHoverCheckBox->isChecked();
     bool showAppsByName = d->ui.appsByNameCheckBox->isChecked();
     bool showRecentlyInstalled = d->ui.showRecentlyInstalledCheckBox->isChecked();
+    bool reduceMenuDepth = d->ui.reduceMenuDepthCheckBox->isChecked();
 
     const QString iconname = d->ui.iconButton->icon();
 
@@ -227,6 +230,7 @@ void LauncherApplet::configAccepted()
     d->launcher->setSwitchTabsOnHover(switchTabsOnHover);
     d->launcher->setShowAppsByName(showAppsByName);
     d->launcher->setShowRecentlyInstalled(showRecentlyInstalled);
+    d->launcher->setReduceMenuDepth(reduceMenuDepth);
 }
 
 QList<QAction*> LauncherApplet::contextualActions()
diff --git a/plasma/desktop/applets/kickoff/applet/kickoffConfig.ui b/plasma/desktop/applets/kickoff/applet/kickoffConfig.ui
index d5117c8..3e49ea7 100644
--- a/plasma/desktop/applets/kickoff/applet/kickoffConfig.ui
+++ b/plasma/desktop/applets/kickoff/applet/kickoffConfig.ui
@@ -54,7 +54,7 @@
      </property>
     </spacer>
    </item>
-   <item row="4" column="1">
+   <item row="5" column="1">
     <spacer name="verticalSpacer">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
@@ -100,6 +100,26 @@
      </property>
     </widget>
    </item>
+   <item row="4" column="2">
+    <widget class="QCheckBox" name="reduceMenuDepthCheckBox">
+     <property name="text">
+      <string/>
+     </property>
+    </widget>
+   </item>
+   <item row="4" column="1">
+    <widget class="QLabel" name="label_4">
+     <property name="text">
+      <string>Reduce menu depth:</string>
+     </property>
+     <property name="alignment">
+      <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+     </property>
+     <property name="buddy">
+      <cstring>reduceMenuDepthCheckBox</cstring>
+     </property>
+    </widget>
+   </item>
    <item row="3" column="1">
     <widget class="QLabel" name="label">
      <property name="text">
diff --git a/plasma/desktop/applets/kickoff/core/applicationmodel.cpp b/plasma/desktop/applets/kickoff/core/applicationmodel.cpp
index 82edade..6809dbb 100644
--- a/plasma/desktop/applets/kickoff/core/applicationmodel.cpp
+++ b/plasma/desktop/applets/kickoff/core/applicationmodel.cpp
@@ -111,7 +111,8 @@ public:
               primaryNamePolicy(ApplicationModel::GenericNamePrimary),
               displayOrder(NameAfterDescription),
               allowSeparators(_allowSeparators),
-              showRecentlyInstalled(true)
+              showRecentlyInstalled(true),
+              reduceMenuDepth(true)
     {
         systemApplications = Kickoff::systemApplicationList();
         reloadTimer = new QTimer(qq);
@@ -137,12 +138,42 @@ public:
     DisplayOrder displayOrder;
     bool allowSeparators;
     bool showRecentlyInstalled;
+    bool reduceMenuDepth;
     QTimer *reloadTimer;
 
     QStringList newInstalledPrograms;
     QHash<QString, QDate> seenPrograms;
 };
 
+QByteArray nameToKey(const QString &name)
+{
+    // code taken from libkdecore
+    const QByteArray nameStr = name.toLocal8Bit();
+
+    QByteArray key;
+    // strxfrm() crashes on Solaris and strxfrm is not defined under wince
+#if !defined(USE_SOLARIS) && !defined(_WIN32_WCE)
+    // maybe it'd be better to use wcsxfrm() where available
+    key.resize( name.length() * 4 + 1 );
+    size_t ln = strxfrm(key.data(), nameStr.constData(), key.size());
+    if( ln != size_t( -1 ))
+    {
+        key.resize(ln);
+        if( (int)ln >= key.size())
+        { // didn't fit?
+            ln = strxfrm( key.data(), nameStr.constData(), key.size());
+            if( ln == size_t( -1 ))
+                key = nameStr;
+        }
+    }
+    else
+#endif
+    {
+        key = nameStr;
+    }
+    return key;
+}
+
 void ApplicationModelPrivate::fillNode(const QString &_relPath, AppNode *node)
 {
      if (_relPath=="new/") {
@@ -170,17 +201,59 @@ void ApplicationModelPrivate::fillNode(const QString &_relPath, AppNode *node)
         return;
     }
 
-    const KServiceGroup::List list = root->entries(true /* sorted */,
+    KServiceGroup::List list = root->entries(true /* sorted */,
                                                    true /* exclude no display entries */,
-                                                   allowSeparators /* allow separators */,
+                                                   false /* allow separators */,
                                                    primaryNamePolicy == ApplicationModel::GenericNamePrimary /* sort by generic name */);
 
     // application name <-> service map for detecting duplicate entries
     QHash<QString, KService::Ptr> existingServices;
+    KSortableList<KSharedPtr<KSycocaEntry>,QByteArray> slist;
+    KSortableList<KSharedPtr<KSycocaEntry>,QByteArray> glist;
 
     // generic name <-> node mapping to determinate duplicate generic names
     QHash<QString,QList<AppNode*> > genericNames;
 
+    for (KServiceGroup::List::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) {
+        const KSycocaEntry::Ptr p = (*it);
+
+        if (p->isType(KST_KService)) {
+            const KService::Ptr service = KService::Ptr::staticCast(p);
+
+            if( primaryNamePolicy == ApplicationModel::GenericNamePrimary )
+                slist.insert( nameToKey( service->genericName() + QLatin1Char(' ') + service->name() ), p);
+            else slist.insert( nameToKey( service->name() + QLatin1Char(' ') + service->genericName() ), p);
+        }
+        else if (p->isType(KST_KServiceGroup))
+        {
+            KServiceGroup::Ptr serviceGroup = KServiceGroup::Ptr::staticCast(p);
+            if ( reduceMenuDepth && serviceGroup->SuSEshortMenu() ){
+                KServiceGroup::List l = serviceGroup->entries(true, true /*excludeNoDisplay_*/, false );
+                if ( l.count() == 1 ) {
+
+                    // the special case, we want to short the menu.
+                    // TOFIX? : this works only for one level
+                    KServiceGroup::List::ConstIterator _it=l.begin();
+                    const KSycocaEntry::Ptr _e = (*_it);
+                    if (_e->isType(KST_KService)) {
+                        const KService::Ptr s = KService::Ptr::staticCast(_e);
+                        if( primaryNamePolicy == ApplicationModel::GenericNamePrimary )
+                            slist.insert( nameToKey( s->genericName() + QLatin1Char(' ') + s->name() ), _e );
+                        else slist.insert( nameToKey( s->name() + QLatin1Char(' ') + s->genericName() ), _e );
+                        // and escape from here
+                        continue;
+                    }
+                }
+            }
+            glist.insert( nameToKey( serviceGroup->caption() ), p );
+        }
+        else
+            slist.insert( nameToKey( p->name() ), p);
+    }
+
+    if (reduceMenuDepth) root->addSortOrderEntry(":F"); // add ":F" to the sortOrder list if it isn't included (bnc#356553)
+    list = root->SuSEsortEntries( slist, glist, true /*excludeNoDisplay_*/, allowSeparators );
+
     for (KServiceGroup::List::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) {
         QString icon;
         QString appName;
@@ -238,7 +251,15 @@ void ApplicationModelPrivate::fillNode(const QString &_relPath, AppNode *node)
 
             existingServices[appName] = service;
         } else if (p->isType(KST_KServiceGroup)) {
-            const KServiceGroup::Ptr serviceGroup = KServiceGroup::Ptr::staticCast(p);
+            KServiceGroup::Ptr serviceGroup = KServiceGroup::Ptr::staticCast(p);
+
+            if ( reduceMenuDepth && serviceGroup->SuSEshortMenu() ) {
+                KServiceGroup::List l = serviceGroup->entries(true, true /*excludeNoDisplay_*/ );
+            if ( l.count() == 1 )
+                continue;
+            }
+
+            // standard sub menu
 
             if (serviceGroup->noDisplay() || serviceGroup->childCount() == 0) {
                 continue;
@@ -358,6 +391,19 @@ bool ApplicationModel::showRecentlyInstalled() const
    return d->showRecentlyInstalled;
 }
 
+void ApplicationModel::setReduceMenuDepth(bool reduceMenuDepth)
+{
+    if (d->reduceMenuDepth != reduceMenuDepth) {
+        d->reduceMenuDepth = reduceMenuDepth;
+        reloadMenu();
+    }
+}
+
+bool ApplicationModel::reduceMenuDepth() const
+{
+   return d->reduceMenuDepth;
+}
+
 int ApplicationModel::columnCount(const QModelIndex &parent) const
 {
     Q_UNUSED(parent)
diff --git a/plasma/desktop/applets/kickoff/core/applicationmodel.h b/plasma/desktop/applets/kickoff/core/applicationmodel.h
index e3dfead..0b7df1e 100644
--- a/plasma/desktop/applets/kickoff/core/applicationmodel.h
+++ b/plasma/desktop/applets/kickoff/core/applicationmodel.h
@@ -122,6 +122,8 @@ public:
     void setApplet(Plasma::Applet *applet);
     void setShowRecentlyInstalled(bool showRecentlyInstalled);
     bool showRecentlyInstalled() const;
+    void setReduceMenuDepth(bool reduceMenuDepth);
+    bool reduceMenuDepth() const;
 
 public slots:
     void reloadMenu();
diff --git a/plasma/desktop/applets/kickoff/simpleapplet/simpleapplet.cpp b/plasma/desktop/applets/kickoff/simpleapplet/simpleapplet.cpp
index c54fc49..0b43a9e 100644
--- a/plasma/desktop/applets/kickoff/simpleapplet/simpleapplet.cpp
+++ b/plasma/desktop/applets/kickoff/simpleapplet/simpleapplet.cpp
@@ -115,6 +115,7 @@ public:
     int maxRecentApps;
     bool showMenuTitles;
     bool showRecentlyInstalled;
+    bool reduceMenuDepth;
 
     QListWidget *view;
     KIconButton *iconButton;
@@ -122,6 +123,7 @@ public:
     QSpinBox *recentApplicationsSpinBox;
     QCheckBox *showMenuTitlesCheckBox;
     QCheckBox *showRecentlyInstalledCheckBox;
+    QCheckBox *reduceMenuDepthCheckBox;
 
     QList<QAction*> actions;
     QAction* switcher;
@@ -140,6 +143,7 @@ public:
               formatComboBox(0),
               showMenuTitlesCheckBox(0),
               showRecentlyInstalledCheckBox(0),
+              reduceMenuDepthCheckBox(0),
               switcher(0),
               contextMenuFactory(0)
     {}
@@ -494,7 +498,13 @@ void MenuLauncherApplet::createConfigurationInterface(KConfigDialog *parent)
     d->showRecentlyInstalledCheckBox->setChecked(d->showRecentlyInstalled);
     grid->addWidget(d->showRecentlyInstalledCheckBox, 4, 1);
 
-    grid->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 5, 0, 1, 3);
+    QLabel *reduceMenuDepthLabel = new QLabel(i18n("Reduce menu depth:"), p);
+    grid->addWidget(reduceMenuDepthLabel, 5, 0, Qt::AlignRight);
+    d->reduceMenuDepthCheckBox = new QCheckBox(p);
+    d->reduceMenuDepthCheckBox->setChecked(d->reduceMenuDepth);
+    grid->addWidget(d->reduceMenuDepthCheckBox, 5, 1);
+
+    grid->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 6, 0, 1, 3);
     parent->addPage(p, i18n("Options"), "configure");
 
     connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
@@ -504,6 +514,7 @@ void MenuLauncherApplet::createConfigurationInterface(KConfigDialog *parent)
     connect(d->recentApplicationsSpinBox, SIGNAL(valueChanged(int)), parent, SLOT(settingsModified()));
     connect(d->showMenuTitlesCheckBox, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
     connect(d->showRecentlyInstalledCheckBox, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
+    connect(d->reduceMenuDepthCheckBox, SIGNAL(toggled(bool)), parent, SLOT(settingsModified()));
     connect(d->view, SIGNAL(currentTextChanged(QString)), parent, SLOT(settingsModified()));    
 }
 
@@ -568,6 +578,12 @@ void MenuLauncherApplet::configAccepted()
         d->showRecentlyInstalled = showRecentlyInstalled;
         cg.writeEntry("showRecentlyInstalled", showRecentlyInstalled);
     }
+    const bool reduceMenuDepth = d->reduceMenuDepthCheckBox->isChecked();
+    if (reduceMenuDepth != d->reduceMenuDepth) {
+        needssaving = true;
+        d->reduceMenuDepth = reduceMenuDepth;
+        cg.writeEntry("reduceMenuDepth", reduceMenuDepth);
+    }
 
     if (needssaving) {
         d->updateTooltip();
@@ -635,6 +651,7 @@ void MenuLauncherApplet::showMenu(bool pressed)
                 }
                 appModel->setSystemApplicationPolicy(Kickoff::ApplicationModel::ShowApplicationAndSystemPolicy);
                 appModel->setShowRecentlyInstalled(d->showRecentlyInstalled);
+                appModel->setReduceMenuDepth(d->reduceMenuDepth);
 
                 menuview->addModel(appModel, Kickoff::MenuView::None, d->relativePath);
 
@@ -892,6 +909,7 @@ void MenuLauncherApplet::configChanged()
     d->setMaxRecentApps(cg.readEntry("maxRecentApps", qMin(5, Kickoff::RecentApplications::self()->maximum())));
     d->showMenuTitles = cg.readEntry("showMenuTitles", false);
     d->showRecentlyInstalled = cg.readEntry("showRecentlyInstalled", true);
+    d->reduceMenuDepth = cg.readEntry("reduceMenuDepth", true);
 
     d->icon->setIcon(KIcon(cg.readEntry("icon", d->iconname)));
 
diff --git a/plasma/desktop/applets/kickoff/ui/launcher.cpp b/plasma/desktop/applets/kickoff/ui/launcher.cpp
index 8280509..014e23c 100644
--- a/plasma/desktop/applets/kickoff/ui/launcher.cpp
+++ b/plasma/desktop/applets/kickoff/ui/launcher.cpp
@@ -699,6 +699,18 @@ void Launcher::setShowRecentlyInstalled(bool showRecentlyInstalled)
     d->applicationModel->setShowRecentlyInstalled(showRecentlyInstalled);
 }
 
+void Launcher::setReduceMenuDepth(bool showReduceMenuDepth)
+{
+    const bool wasReduceMenuDepth = d->applicationModel->reduceMenuDepth();
+    if (d->applet && showReduceMenuDepth != wasReduceMenuDepth) {
+        KConfigGroup cg = d->applet->config();
+        cg.writeEntry("ReduceMenuDepth", showReduceMenuDepth);
+        emit configNeedsSaving();
+    }
+
+    d->applicationModel->setReduceMenuDepth(showReduceMenuDepth);
+}
+
 bool Launcher::switchTabsOnHover() const
 {
     return d->contentSwitcher->switchTabsOnHover();
@@ -714,6 +726,11 @@ bool Launcher::showRecentlyInstalled() const
   return d->applicationModel->showRecentlyInstalled();
 }
 
+bool Launcher::reduceMenuDepth() const
+{
+  return d->applicationModel->reduceMenuDepth();
+}
+
 void Launcher::setVisibleItemCount(int count)
 {
     d->visibleItemCount = count;
@@ -743,6 +760,7 @@ void Launcher::setApplet(Plasma::Applet *applet)
     setShowAppsByName(cg.readEntry("ShowAppsByName", showAppsByName()));
     setVisibleItemCount(cg.readEntry("VisibleItemsCount", visibleItemCount()));
     setShowRecentlyInstalled(cg.readEntry("ShowRecentlyInstalled", showRecentlyInstalled()));
+    setReduceMenuDepth(cg.readEntry("ReduceMenuDepth", reduceMenuDepth()));
 
     d->applet = applet;
     d->contextMenuFactory->setApplet(applet);
diff --git a/plasma/desktop/applets/kickoff/ui/launcher.h b/plasma/desktop/applets/kickoff/ui/launcher.h
index 7505c0a..e61327b 100644
--- a/plasma/desktop/applets/kickoff/ui/launcher.h
+++ b/plasma/desktop/applets/kickoff/ui/launcher.h
@@ -83,6 +83,10 @@ public:
     void setShowRecentlyInstalled(bool showRecentlyInstalled);
     bool showRecentlyInstalled() const;
 
+    /** Specifies whether single item sub-menus shall be collapsed to upper hierarchy */
+    void setReduceMenuDepth(bool reduceMenuDepth);
+    bool reduceMenuDepth() const;
+
 signals:
     void aboutToHide();
     void configNeedsSaving();