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

Index: applets/kickoff/core/applicationmodel.h
===================================================================
--- plasma/applets/kickoff/core/applicationmodel.h	(revision 1011046)
+++ plasma/applets/kickoff/core/applicationmodel.h	(working copy)
@@ -114,14 +114,25 @@
     virtual QModelIndex parent(const QModelIndex &index) const;
     virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
 
+    void setShowRecentlyInstalled(bool showRecentlyInstalled);
+    bool showRecentlyInstalled() const;
+
+    void setReduceMenuDepth(bool reduceMenuDepth);
+    bool reduceMenuDepth() const;
+   
 public slots:
     void reloadMenu();
     void checkSycocaChange();
 
+protected slots:
+    void createNewProgramList();
+
 private:
     friend class ApplicationModelPrivate;
     ApplicationModelPrivate *const d;
 
+    bool createNewProgramList(QString relPath);
+
     Q_DISABLE_COPY(ApplicationModel)
 };
 
Index: applets/kickoff/core/applicationmodel.cpp
===================================================================
--- plasma/applets/kickoff/core/applicationmodel.cpp	(revision 1011046)
+++ plasma/applets/kickoff/core/applicationmodel.cpp	(working copy)
@@ -109,7 +109,9 @@
               systemApplicationPolicy(ApplicationModel::ShowSystemOnlyPolicy),
               primaryNamePolicy(ApplicationModel::GenericNamePrimary),
               displayOrder(NameAfterDescription),
-              allowSeparators(_allowSeparators)
+              allowSeparators(_allowSeparators),
+              showRecentlyInstalled(true),
+              reduceMenuDepth(true)
     {
         systemApplications = Kickoff::systemApplicationList();
     }
@@ -120,6 +122,8 @@
     }
 
     void fillNode(const QString &relPath, AppNode *node);
+    void addAppNode(const QString &icon, const QString &appName, const QString &genericName,
+       const QString& relPath, const QString &desktopEntry, bool isDir, AppNode *parent);
     static QHash<QString, QString> iconNameMap();
 
     ApplicationModel *q;
@@ -130,77 +134,115 @@
     QStringList systemApplications;
     DisplayOrder displayOrder;
     bool allowSeparators;
+    bool showRecentlyInstalled;
+    bool reduceMenuDepth;
+
+    QStringList newInstalledPrograms, seenPrograms;
+    QString currentDate;
 };
 
 void ApplicationModelPrivate::fillNode(const QString &_relPath, AppNode *node)
 {
+    if (_relPath=="new/") {
+       for (QStringList::ConstIterator it = newInstalledPrograms.begin(); it != newInstalledPrograms.end(); ++it) {
+            KService::Ptr p = KService::serviceByStorageId((*it));
+
+            if (p->noDisplay()) {
+                continue;
+            }
+
+            AppNode *newnode = new AppNode();
+            newnode->icon = KIcon(p->icon());
+            newnode->appName = p->name();
+            newnode->genericName = p->genericName();
+            newnode->relPath = QString();
+            newnode->desktopEntry = p->entryPath();
+            newnode->isDir = false;
+            newnode->parent = node;
+            node->children.append(newnode);
+        }
+        return;
+    }
+
     KServiceGroup::Ptr root = KServiceGroup::group(_relPath);
 
     if (!root || !root->isValid()) {
         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;
+    QMap<QString,QString> specialTitle;
+    QMap<QString,QString> categoryIcon;
+    QMap<QString,QString> shortenedMenuPath;
 
     // generic name <-> node mapping to determinate duplicate generic names
     QHash<QString,QList<AppNode*> > genericNames;
 
+    bool isSeparator = false;
     for (KServiceGroup::List::ConstIterator it = list.constBegin(); it != list.constEnd(); ++it) {
-        QString icon;
-        QString appName;
-        QString genericName;
-        QString relPath = _relPath;
-        QString desktopEntry;
-        bool isDir = false;
-        bool isSeparator = false;
         const KSycocaEntry::Ptr p = (*it);
 
         if (p->isType(KST_KService)) {
             const KService::Ptr service = KService::Ptr::staticCast(p);
 
-            if (service->noDisplay()) {
-                continue;
+         slist.insert( service->name().toLocal8Bit(), 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);
+                   QString key;
+                     if ( serviceGroup->SuSEgeneralDescription() )
+                        key = s->name();
+                   else {
+                      // we use the normal menu description
+                      key = s->name();
+                        if( !s->genericName().isEmpty() && serviceGroup->caption()!=s->genericName())
+                           key = serviceGroup->caption() + " (" + s->name() + ")";
+                   }
+                   specialTitle.insert( _e->name(), key );
+                   categoryIcon.insert( _e->name(), serviceGroup->icon() );
+                     slist.insert( key.toLocal8Bit(), _e );
+                   shortenedMenuPath.insert( _e->name(), serviceGroup->relPath() );
+                     // and escape from here
+                     continue;
+                  }
+               }
             }
+            glist.insert( serviceGroup->caption().toLocal8Bit(), p );
+      }
+      else
+         slist.insert( p->name().toLocal8Bit(), p);
+   }
 
-            icon = service->icon();
-            appName = service->name();
-            genericName = service->genericName();
-            desktopEntry = service->entryPath();
+   list = root->SuSEsortEntries( slist, glist, true /*excludeNoDisplay_*/, false );
 
-            // check for duplicates (eg. KDE 3 and KDE 4 versions of application
-            // both present)
-            if (duplicatePolicy == ApplicationModel::ShowLatestOnlyPolicy &&
-                existingServices.contains(appName)) {
-                if (Kickoff::isLaterVersion(existingServices[appName], service)) {
-                    continue;
-                } else {
-                    // find and remove the existing entry with the same name
-                    for (int i = node->children.count() - 1; i >= 0; --i) {
-                        AppNode *app = node->children.at(i);
-                        if (app->appName == appName &&
-                            app->genericName == genericName &&
-                            app->iconName == icon) {
-                            app = node->children.takeAt(i);
-                            const QString s = app->genericName.toLower();
-                            if (genericNames.contains(s)) {
-                                QList<AppNode*> list = genericNames[s];
-                                for (int j = list.count() - 1; j >= 0; --j) {
-                                    if(list.at(j) == app) {
-                                        list.takeAt(j);
-                                    }
-                                }
-                                genericNames[s] = list;
-                            }
-                            delete app;
-                        }
-                    }
-                }
+   for( KServiceGroup::List::ConstIterator it = list.begin();
+       it != list.end(); ++it)
+   {
+      const KSycocaEntry::Ptr p = (*it);
+      if (p->isType(KST_KService))
+      {
+         const KService::Ptr service = KService::Ptr::staticCast(p);
+
+
+            if (service->noDisplay()) {
+                continue;
             }
 
             if (systemApplicationPolicy == ApplicationModel::ShowSystemOnlyPolicy &&
@@ -210,10 +252,41 @@
                 continue;
             }
 
-            existingServices[appName] = service;
+         QString menuPath;
+         if (shortenedMenuPath[service->name()].isEmpty())
+             menuPath=_relPath+service->menuId();
+         else
+             menuPath=shortenedMenuPath[service->name()]+service->menuId();
+
+         QString icon = categoryIcon[service->name()];
+         if (icon.isEmpty())
+            icon = service->icon();
+
+         QString name = specialTitle[service->name()];
+         if (name.isEmpty())
+            name = service->name();
+
+         QString genericName = service->genericName();
+         if (genericName.isEmpty()) {
+            genericName = name;
+            name = QString::null;
+         }
+
+         if (name==genericName)
+            name = QString::null;
+
+         addAppNode(icon, name, genericName, _relPath, service->entryPath(), false, node);
         } 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;
             }
@@ -221,15 +294,11 @@
             kDebug(250) << "Service group" << serviceGroup->entryPath() << serviceGroup->icon()
             << serviceGroup->relPath() << serviceGroup->directoryEntryPath();
 
-            icon = serviceGroup->icon();
-            if (iconNameMap().contains(icon)) {
+            QString icon = serviceGroup->icon();
+            if (iconNameMap().contains(icon))
                 icon = iconNameMap().value(icon);
-            }
 
-            genericName = serviceGroup->caption();
-            relPath = serviceGroup->relPath();
-            appName = serviceGroup->comment();
-            isDir = true;
+            addAppNode(icon, serviceGroup->comment(), serviceGroup->caption(), serviceGroup->relPath(), QString::null, true, node);
         } else if (p->isType(KST_KServiceSeparator)) {
             isSeparator = true;
         } else {
@@ -237,26 +306,29 @@
             continue;
         }
 
+   }
+
+   if (showRecentlyInstalled && _relPath.isEmpty() && newInstalledPrograms.count()) {
+
         AppNode *newnode = new AppNode();
-        newnode->iconName = icon;
-        newnode->icon = KIcon(icon);
-        newnode->appName = appName;
-        newnode->genericName = genericName;
-        newnode->relPath = relPath;
-        newnode->desktopEntry = desktopEntry;
-        newnode->isDir = isDir;
-        newnode->isSeparator = isSeparator;
+        newnode->icon = KIcon("chronometer");
+        newnode->appName = QString();
+        newnode->genericName = i18n("Recently Installed");
+        newnode->relPath = "new/";
+        newnode->desktopEntry = QString();
+        newnode->isDir = true;
         newnode->parent = node;
-        node->children.append(newnode);
+        node->children.prepend(newnode);
 
+#if 0
         if (p->isType(KST_KService)) {
             const QString s = genericName.toLower();
             QList<AppNode*> list = genericNames.contains(s) ? genericNames[s] : QList<AppNode*>();
             list.append(newnode);
             genericNames[s] = list;
         }
+#endif
     }
-
     // set the subTitleMandatory field for nodes that do not provide a unique generic
     // name what may help us on display to show in such cases also the subtitle to
     // provide a hint to the user what the duplicate entries are about.
@@ -269,6 +341,20 @@
     }
 }
 
+void ApplicationModelPrivate::addAppNode(const QString &icon, const QString &appName, const QString &genericName,
+    const QString& relPath, const QString &desktopEntry, bool isDir, AppNode *parent )
+{
+    AppNode *newnode = new AppNode();
+    newnode->icon = KIcon(icon);
+    newnode->appName = appName;
+    newnode->genericName = genericName;
+    newnode->relPath = relPath;
+    newnode->desktopEntry = desktopEntry;
+    newnode->isDir = isDir;
+    newnode->parent = parent;
+    parent->children.append(newnode);
+}
+
 ApplicationModel::ApplicationModel(QObject *parent, bool allowSeparators)
   : KickoffAbstractModel(parent),
     d(new ApplicationModelPrivate(this, allowSeparators))
@@ -278,6 +364,7 @@
     QDBusConnection::sessionBus().registerObject("/kickoff", this);
     dbus.connect(QString(), "/kickoff", "org.kde.plasma", "reloadMenu", this, SLOT(reloadMenu()));
     connect(KSycoca::self(), SIGNAL(databaseChanged()), this, SLOT(checkSycocaChange()));
+    createNewProgramList();
     d->fillNode(QString(), d->root);
 }
 
@@ -305,6 +392,32 @@
    return d->displayOrder;
 }
 
+void ApplicationModel::setShowRecentlyInstalled(bool showRecentlyInstalled) 
+{
+    if (d->showRecentlyInstalled != showRecentlyInstalled) {
+        d->showRecentlyInstalled = showRecentlyInstalled;
+        reloadMenu();
+    }
+}
+
+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)
@@ -469,8 +582,9 @@
 {
     delete d->root;
     d->root = new AppNode();
+    reset();
+    createNewProgramList();
     d->fillNode(QString(), d->root);
-    reset();
 }
 
 void ApplicationModel::checkSycocaChange()
@@ -490,6 +604,104 @@
     return d->systemApplicationPolicy;
 }
 
+void ApplicationModel::createNewProgramList()
+{
+    d->newInstalledPrograms.clear();
+    if (!d->showRecentlyInstalled) {
+        return;
+    }
+    
+    KConfigGroup kickoffrc = Kickoff::componentData().config()->group("Applications");
+    d->seenPrograms = kickoffrc.readEntry("FirstSeen", QStringList());
+
+    d->currentDate = QDate::currentDate().toString(Qt::ISODate);
+
+    bool initialize = (d->seenPrograms.count() == 0);
+
+    bool seenProgramsChanged = createNewProgramList(QString::null);
+
+    if (initialize) {
+       for (QStringList::Iterator it = d->seenPrograms.begin(); it != d->seenPrograms.end(); ++it) {
+          *(++it) = "-";
+       }
+
+       d->newInstalledPrograms.clear();
+    }
+
+    if (seenProgramsChanged) {
+        kickoffrc.writeEntry("FirstSeen", d->seenPrograms);
+        kickoffrc.sync();
+    }
+}
+
+bool ApplicationModel::createNewProgramList(QString relPath)
+{
+    bool seenProgramsChanged = false;
+
+    KServiceGroup::Ptr group = KServiceGroup::group(relPath);
+    if (!group || !group->isValid()) {
+        return false;
+    }
+
+    KServiceGroup::List list = group->entries();
+    if (list.isEmpty()) {
+        return false;
+    }
+
+    KServiceGroup::List::ConstIterator it = list.begin();
+    for (; it != list.end(); ++it) {
+	KSycocaEntry::Ptr e = (*it);
+
+	if (e) {
+	    if (e->isType(KST_KServiceGroup)) {
+		KServiceGroup::Ptr g(KServiceGroup::Ptr::staticCast(e));
+		if(!g->noDisplay()) {
+		    seenProgramsChanged |= createNewProgramList(g->relPath());
+		}
+	    } else if (e->isType(KST_KService)) {
+	        KService::Ptr s(KService::Ptr::staticCast(e));
+		if (s->isApplication() && !s->noDisplay() ) {
+                    QString shortStorageId = s->storageId().replace(".desktop", QString::null);
+                    QStringList::Iterator it_find = d->seenPrograms.begin();
+                    QStringList::Iterator it_end = d->seenPrograms.end();
+ 	            bool found = false;
+		    for (; it_find != it_end; ++it_find) {
+			if (*(it_find)==shortStorageId) {
+			   found = true;
+			   break;
+                        }
+                        ++it_find;
+                    }
+                    if (!found) {
+                        seenProgramsChanged = true;
+                        d->seenPrograms += shortStorageId;
+                        d->seenPrograms += d->currentDate;
+                        if (d->newInstalledPrograms.indexOf(s->storageId())==-1) {
+                            d->newInstalledPrograms += s->storageId();
+                        }
+                    }
+                    else {
+                        ++it_find;
+                        if (*(it_find)!="-") {
+                            QDate date = QDate::fromString(*(it_find), Qt::ISODate);
+                            if (date.daysTo(QDate::currentDate())<3) {
+                                if (d->newInstalledPrograms.indexOf(s->storageId())==-1) {
+                                     d->newInstalledPrograms += s->storageId();
+                                }
+                            }
+                            else {
+                                seenProgramsChanged=true;
+                                (*it_find)="-";
+                            }
+                        }
+                    }
+                }
+            }
+	}
+    }
+    return seenProgramsChanged;
+}
+
 /**
  * FIXME This is a temporary workaround to map the icon names found
  * in the desktop directory files (from /usr/share/desktop-directories)
Index: applets/kickoff/ui/launcher.h
===================================================================
--- plasma/applets/kickoff/ui/launcher.h	(revision 1011046)
+++ plasma/applets/kickoff/ui/launcher.h	(working copy)
@@ -77,6 +77,14 @@
     we don't display old searches and switch back to the favorite-view. */
     void reset();
 
+    /** Specifies whether 'Recently Installed' hierarchy shall be shown in application view */
+    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();
Index: applets/kickoff/ui/launcher.cpp
===================================================================
--- plasma/applets/kickoff/ui/launcher.cpp	(revision 1011046)
+++ plasma/applets/kickoff/ui/launcher.cpp	(working copy)
@@ -642,6 +642,30 @@
     }
 }
 
+void Launcher::setShowRecentlyInstalled(bool showRecentlyInstalled)
+{
+    const bool wasShowRecentlyInstalled = d->applicationModel->showRecentlyInstalled();
+    if (d->applet && showRecentlyInstalled != wasShowRecentlyInstalled) {
+        KConfigGroup cg = d->applet->config();
+        cg.writeEntry("ShowRecentlyInstalled", showRecentlyInstalled);
+        emit configNeedsSaving();
+    }
+
+    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();
@@ -652,6 +676,16 @@
   return d->applicationModel->nameDisplayOrder() == Kickoff::NameBeforeDescription;
 }
 
+bool Launcher::showRecentlyInstalled() const
+{
+  return d->applicationModel->showRecentlyInstalled();
+}
+
+bool Launcher::reduceMenuDepth() const
+{
+  return d->applicationModel->reduceMenuDepth();
+}
+
 void Launcher::setVisibleItemCount(int count)
 {
     d->visibleItemCount = count;
@@ -670,6 +704,8 @@
     cg = applet->config();
     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);
--- plasma/applets/kickoff/applet/applet.cpp	2009/08/14 10:23:48	1.1
+++ plasma/applets/kickoff/applet/applet.cpp	2009/08/14 11:24:25
@@ -62,6 +62,8 @@
     KIconButton *iconButton;
     QCheckBox *switchOnHoverCheckBox;
     QCheckBox *appsByNameCheckBox;
+    QCheckBox *showRecentlyInstalledCheckBox;
+    QCheckBox *reduceMenuDepthCheckBox;
     QList<QAction*> actions;
     QAction* switcher;
     LauncherApplet *q;
@@ -179,6 +181,20 @@
     label->setBuddy(d->appsByNameCheckBox);
     widgetLayout->addWidget(d->appsByNameCheckBox, 2, 1);
 
+    label = new QLabel(i18n("Show 'Recently Installed':"), widget);
+    label->setAlignment(Qt::AlignRight);
+    widgetLayout->addWidget(label, 3, 0);
+    d->showRecentlyInstalledCheckBox = new QCheckBox(widget);
+    label->setBuddy(d->showRecentlyInstalledCheckBox);
+    widgetLayout->addWidget(d->showRecentlyInstalledCheckBox, 3, 1);
+
+    label = new QLabel(i18n("Reduce menu depth:"), widget);
+    label->setAlignment(Qt::AlignRight);
+    widgetLayout->addWidget(label, 4, 0);
+    d->reduceMenuDepthCheckBox = new QCheckBox(widget);
+    label->setBuddy(d->reduceMenuDepthCheckBox);
+    widgetLayout->addWidget(d->reduceMenuDepthCheckBox, 4, 1);
+
     connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
     connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
     parent->addPage(widget, i18n("General"), icon());
@@ -186,6 +202,9 @@
     d->createLauncher();
     d->switchOnHoverCheckBox->setChecked(d->launcher->switchTabsOnHover());
     d->appsByNameCheckBox->setChecked(d->launcher->showAppsByName());
+    
+    d->showRecentlyInstalledCheckBox->setChecked(d->launcher->showRecentlyInstalled());
+    d->reduceMenuDepthCheckBox->setChecked(d->launcher->reduceMenuDepth());  
 }
 
 void LauncherApplet::popupEvent(bool show)
@@ -211,6 +230,9 @@
     bool switchTabsOnHover = d->switchOnHoverCheckBox->isChecked();
     bool showAppsByName = d->appsByNameCheckBox->isChecked();
 
+    bool showRecentlyInstalled = d->showRecentlyInstalledCheckBox->isChecked();
+    bool reduceMenuDepth = d->reduceMenuDepthCheckBox->isChecked();
+    
     const QString iconname = d->iconButton->icon();
 
     // TODO: should this be moved into Launcher as well? perhaps even the config itself?
@@ -231,6 +253,9 @@
 
     d->launcher->setSwitchTabsOnHover(switchTabsOnHover);
     d->launcher->setShowAppsByName(showAppsByName);
+
+    d->launcher->setShowRecentlyInstalled(showRecentlyInstalled);
+    d->launcher->setReduceMenuDepth(reduceMenuDepth);
 }
 
 QList<QAction*> LauncherApplet::contextualActions()
--- plasma/applets/kickoff/simpleapplet/simpleapplet.cpp	2009/08/14 10:23:53	1.1
+++ plasma/applets/kickoff/simpleapplet/simpleapplet.cpp	2009/08/14 12:08:04
@@ -108,6 +108,8 @@
     MenuLauncherApplet::FormatType formattype;
     int maxRecentApps;
     bool showMenuTitles;
+    bool showRecentlyInstalled;
+    bool reduceMenuDepth;
 
     QListWidget *view;
     KIconButton *iconButton;
@@ -115,6 +117,9 @@
     QSpinBox *recentApplicationsSpinBox;
     QCheckBox *showMenuTitlesCheckBox;
 
+    QCheckBox *showRecentlyInstalledCheckBox;
+    QCheckBox *reduceMenuDepthCheckBox;
+    
     QList<QAction*> actions;
     QAction* switcher;
     Kickoff::ContextMenuFactory *contextMenuFactory;
@@ -124,7 +129,7 @@
             menuview(0), icon(0), launcher(0),
             bookmarkcollection(0), bookmarkowner(0), bookmarkmenu(0),
             view(0), iconButton(0), formatComboBox(0), showMenuTitlesCheckBox(0),
-            switcher(0), contextMenuFactory(0) {}
+            showRecentlyInstalledCheckBox(0), reduceMenuDepthCheckBox(0), switcher(0), contextMenuFactory(0) {}
     ~Private() {
         delete bookmarkmenu;
         delete bookmarkowner;
@@ -308,6 +313,8 @@
 
     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", iconname)));
 
@@ -438,7 +445,19 @@
     d->showMenuTitlesCheckBox->setChecked(d->showMenuTitles);
     grid->addWidget(d->showMenuTitlesCheckBox, 3, 1);
 
-    grid->addItem(new QSpacerItem(0, 0, QSizePolicy::Minimum, QSizePolicy::Expanding), 4, 0, 1, 3);
+    QLabel *showMenuRecentlyInstalledLabel = new QLabel(i18n("Show 'Recently Installed':"), p);
+    grid->addWidget(showMenuRecentlyInstalledLabel, 4, 0, Qt::AlignRight);
+    d->showRecentlyInstalledCheckBox = new QCheckBox(p);
+    d->showRecentlyInstalledCheckBox->setChecked(d->showRecentlyInstalled);
+    grid->addWidget(d->showRecentlyInstalledCheckBox, 4, 1);
+
+    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()));
@@ -496,6 +515,19 @@
         cg.writeEntry("showMenuTitles", showMenuTitles);
     }
 
+    const bool showRecentlyInstalled = d->showRecentlyInstalledCheckBox->isChecked();
+    if (showRecentlyInstalled != d->showRecentlyInstalled) {
+        needssaving = true;
+        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();
         emit configNeedsSaving();
@@ -549,6 +581,9 @@
                     appModel->setPrimaryNamePolicy(Kickoff::ApplicationModel::AppNamePrimary);
                 appModel->setSystemApplicationPolicy(Kickoff::ApplicationModel::ShowApplicationAndSystemPolicy);
 
+                appModel->setShowRecentlyInstalled(d->showRecentlyInstalled);
+                appModel->setReduceMenuDepth(d->reduceMenuDepth);
+
                 d->menuview->addModel(appModel);
 
                 if (d->showMenuTitles) {
openSUSE Build Service is sponsored by