Sophie

Sophie

distrib > Mandriva > 2009.0 > i586 > by-pkgid > ca55d9a45f8a4e9bde633047c89ca24f > files > 6

kdebase4-workspace-4.1.2-13mdv2009.0.src.rpm

diff -u -N -r k/plasma/applets/kickoff/CMakeLists.txt km/plasma/applets/kickoff/CMakeLists.txt
--- k/plasma/applets/kickoff/CMakeLists.txt	2008-08-28 05:07:05.000000000 -0300
+++ km/plasma/applets/kickoff/CMakeLists.txt	2008-08-29 13:37:47.000000000 -0300
@@ -73,3 +73,15 @@
 target_link_libraries(plasma_applet_simplelauncher plasma ${Kickoff_LIBS}) 
 install(TARGETS plasma_applet_simplelauncher DESTINATION ${PLUGIN_INSTALL_DIR})
 install(FILES simpleapplet/plasma-applet-simplelauncher.desktop DESTINATION ${SERVICES_INSTALL_DIR})
+
+# Mandriva KMenu Plasma Applet
+set ( MandrivaApplet_SRCS
+    ${Kickoff_SRCS}
+    mandrivaapplet/menuview.cpp
+    mandrivaapplet/mandrivaapplet.cpp
+    )
+kde4_add_plugin(plasma_applet_mandrivalauncher ${MandrivaApplet_SRCS})
+target_link_libraries(plasma_applet_mandrivalauncher plasma ${Kickoff_LIBS})
+install(TARGETS plasma_applet_mandrivalauncher DESTINATION ${PLUGIN_INSTALL_DIR})
+install(FILES mandrivaapplet/plasma-applet-mandrivalauncher.desktop DESTINATION ${SERVICES_INSTALL_DIR})
+
diff -u -N -r k/plasma/applets/kickoff/mandrivaapplet/mandrivaapplet.cpp km/plasma/applets/kickoff/mandrivaapplet/mandrivaapplet.cpp
--- k/plasma/applets/kickoff/mandrivaapplet/mandrivaapplet.cpp	1969-12-31 21:00:00.000000000 -0300
+++ km/plasma/applets/kickoff/mandrivaapplet/mandrivaapplet.cpp	2008-08-29 13:13:28.000000000 -0300
@@ -0,0 +1,448 @@
+/*
+    Copyright 2007 Robert Knight <robertknight@gmail.com>
+    Copyright 2008 Sebastian Sauer <mail@dipe.org>
+    Copyright 2008 Helio Chissini de Castro <helio@kde.org>
+
+    This library 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 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., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+// Own
+#include "mandrivaapplet/mandrivaapplet.h"
+#include "mandrivaapplet/menuview.h"
+
+// Qt
+#include <QLabel>
+#include <QComboBox>
+#include <QGridLayout>
+#include <QGraphicsView>
+#include <QMetaObject>
+#include <QMetaEnum>
+#include <QPointer>
+#include <QGraphicsLinearLayout>
+#include <QDBusConnection>
+#include <QDBusMessage>
+#include <QSvgRenderer>
+#include <QWidgetAction>
+#include <QFile>
+
+// KDE
+#include <KIcon>
+#include <KConfigDialog>
+#include <KMenu>
+#include <KProcess>
+#include <KStandardDirs>
+
+// Plasma
+#include <plasma/widgets/icon.h>
+#include <plasma/containment.h>
+
+// Local
+#include "core/itemhandlers.h"
+#include "core/models.h"
+#include "core/applicationmodel.h"
+#include "core/favoritesmodel.h"
+#include "core/systemmodel.h"
+#include "core/recentlyusedmodel.h"
+#include "core/leavemodel.h"
+#include "core/urlitemlauncher.h"
+
+
+/// @internal d-pointer class
+class MenuLauncherApplet::Private
+{
+public:
+        QPointer<Kickoff::MenuView> menuview;
+        Plasma::Icon *icon;
+        QPointer<Kickoff::UrlItemLauncher> launcher;
+
+        MenuLauncherApplet::ViewType viewtype;
+        MenuLauncherApplet::FormatType formattype;
+
+        QComboBox *viewComboBox, *formatComboBox;
+
+        QList<QAction*> actions;
+        QAction* switcher;
+
+        class EventSniffer;
+        EventSniffer *eventSniffer;
+
+        Private();
+
+        ~Private() { delete menuview; }
+
+        void addItem(QComboBox* combo, const QString& caption, int index, const QString& icon = QString()) {
+            if( icon.isEmpty() ) {
+                combo->addItem(caption, index);
+            }
+            else {
+                combo->addItem(KIcon(icon), caption, index);
+            }
+        }
+
+        void setCurrentItem(QComboBox* combo, int currentIndex) {
+            for(int i = combo->count() - 1; i >= 0; --i) {
+                if( combo->itemData(i).toInt() == currentIndex ) {
+                    combo->setCurrentIndex(i);
+                    return;
+                }
+            }
+            if( combo->count() > 0 ) {
+                combo->setCurrentIndex(0);
+            }
+        }
+
+        Kickoff::MenuView *createMenuView(QAbstractItemModel *model = 0) {
+            Kickoff::MenuView *view = new Kickoff::MenuView(menuview);
+            view->setFormatType( (Kickoff::MenuView::FormatType) formattype );
+            if( model ) {
+                view->setModel(model);
+            }
+            return view;
+        }
+
+        void addMenu(Kickoff::MenuView *view, bool mergeFirstLevel) {
+            QList<QAction*> actions = view->actions();
+            foreach(QAction *action, actions) {
+                if( action->menu() && mergeFirstLevel ) {
+                    QMetaObject::invokeMethod(action->menu(),"aboutToShow"); //fetch the children
+                    if( actions.count() > 1 && action->menu()->actions().count() > 0 ) {
+                        menuview->addTitle(action->text());
+                    }
+                    foreach(QAction *a, action->menu()->actions()) {
+                        a->setVisible(a->menu() || ! view->indexForAction(a).data(Kickoff::UrlRole).isNull());
+                        menuview->addAction(a);
+                    }
+                }
+                else {
+                    action->setVisible(action->menu() || ! view->indexForAction(action).data(Kickoff::UrlRole).isNull());
+                    menuview->addAction(action);
+                }
+            }
+
+            // if the model asks us for a reset we can't do much except to invalidate our
+            // menuview to be able to rebuild it what is needed to prevent dealing with
+            // invalid items.
+            // the problem here is, that if the menu is currently displayed, it will just
+            // close itself what is evil++ but still better then crashes. anyway, the
+            // right(TM) solution would be to introduce logic to update the content of the
+            // menu even on a reset.
+            connect(view->model(), SIGNAL(modelReset()), menuview, SLOT(deleteLater()));
+        }
+
+};
+
+class MenuLauncherApplet::Private::EventSniffer
+    : public QObject
+{
+    public:
+        EventSniffer(QObject *parent = 0)
+            : QObject(parent) { }
+        ~EventSniffer() { }
+
+        bool eventFilter(QObject *object, QEvent *event)
+        {
+            Q_UNUSED(object);
+            if (event->type() == QEvent::Paint ||
+                    event->type() == QEvent::KeyPress ||
+                    event->type() == QEvent::KeyRelease) { return false; }
+            event->accept();
+            return true;
+        }
+};
+
+MenuLauncherApplet::Private::Private() :
+    menuview(0),
+    launcher(0), 
+    switcher(0), 
+    eventSniffer(new EventSniffer) {}
+
+MenuLauncherApplet::MenuLauncherApplet(QObject *parent, const QVariantList &args)
+    : Plasma::Applet(parent,args),
+      d(new Private)
+{
+    KGlobal::locale()->insertCatalog("plasma_applet_launcher");
+
+    setHasConfigurationInterface(true);
+    setBackgroundHints(NoBackground);
+
+    
+
+    d->icon = new Plasma::Icon(true, this);
+    d->icon->setFlag(ItemIsMovable, false);
+    connect(d->icon, SIGNAL(pressed(bool)), this, SLOT(toggleMenu(bool)));
+    connect(this, SIGNAL(activate()), this, SLOT(toggleMenu()));
+
+    d->viewtype = Combined;
+    d->formattype = NameDescription;
+}
+
+MenuLauncherApplet::~MenuLauncherApplet()
+{
+    delete d;
+}
+
+void MenuLauncherApplet::init()
+{
+    QGraphicsLinearLayout *layout = new QGraphicsLinearLayout(this);
+    layout->setContentsMargins(0, 0, 0, 0);
+    layout->setSpacing(0);
+
+    KConfigGroup cg = config();
+
+    {
+        QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("ViewType"));
+        QByteArray ba = cg.readEntry("view", QByteArray(e.valueToKey(d->viewtype)));
+        d->viewtype = (MenuLauncherApplet::ViewType) e.keyToValue(ba);
+    }
+    {
+        QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("FormatType"));
+        QByteArray ba = cg.readEntry("format", QByteArray(e.valueToKey(d->formattype)));
+        d->formattype = (MenuLauncherApplet::FormatType) e.keyToValue(ba);
+    }
+
+    KConfig *cfg = new KConfig("mandrivarc");
+    KConfigGroup grp = cfg->group("menu");
+    QString fileName = grp.exists() ? 
+        grp.readEntry( "menubutton", KStandardDirs::locate("data", "mandriva/pics/mdvbutton.svg") ) :
+        KStandardDirs::locate("data", "mandriva/pics/mdvbutton.svg");
+
+    bool haveSvg = false;
+    if(QFile::exists(fileName))
+    {
+        QSvgRenderer *renderer = new QSvgRenderer(fileName);
+        if( renderer->isValid() )
+        {
+            setMinimumWidth( renderer->defaultSize().width() );
+            d->icon->setSvg( fileName );
+            haveSvg = true;
+        }
+    }
+
+    if( ! haveSvg )
+        d->icon->setIcon(KIcon("start-here-kde"));
+
+    layout->addItem( d->icon );
+
+    Kickoff::UrlItemLauncher::addGlobalHandler(Kickoff::UrlItemLauncher::ExtensionHandler,"desktop",new Kickoff::ServiceItemHandler);
+    Kickoff::UrlItemLauncher::addGlobalHandler(Kickoff::UrlItemLauncher::ProtocolHandler, "leave", new Kickoff::LeaveItemHandler);
+
+    if (KService::serviceByStorageId("kde4-kmenuedit.desktop")) {
+        QAction* menueditor = new QAction(i18n("Menu Editor"), this);
+        d->actions.append(menueditor);
+        connect(menueditor, SIGNAL(triggered(bool)), this, SLOT(startMenuEditor()));
+    }
+
+    Q_ASSERT( ! d->switcher );
+    d->switcher = new QAction(i18n("Switch to Kickoff Menu Style"), this);
+    d->actions.append(d->switcher);
+    connect(d->switcher, SIGNAL(triggered(bool)), this, SLOT(switchMenuStyle()));
+
+    constraintsEvent(Plasma::ImmutableConstraint);
+}
+
+void MenuLauncherApplet::constraintsEvent(Plasma::Constraints constraints)
+{
+    setBackgroundHints(NoBackground);
+    if (constraints & Plasma::FormFactorConstraint) {
+        if (formFactor() == Plasma::Planar ||
+            formFactor() == Plasma::MediaCenter) {
+            //FIXME set correct minimum size
+            //setMinimumContentSize(d->icon->sizeFromIconSize(IconSize(KIconLoader::Desktop)));
+        } else {
+            //setMinimumContentSize(d->icon->sizeFromIconSize(IconSize(KIconLoader::Small)));
+        }
+    }
+
+    if ((constraints & Plasma::ImmutableConstraint) && d->switcher) {
+        d->switcher->setVisible(immutability() == Plasma::Mutable);
+    }
+}
+
+void MenuLauncherApplet::switchMenuStyle()
+{
+    if (containment()) {
+        containment()->addApplet("launcher", QVariantList(), geometry());
+        destroy();
+    }
+}
+
+void MenuLauncherApplet::startMenuEditor()
+{
+    KProcess::execute("kmenuedit");
+}
+
+void MenuLauncherApplet::runCommand()
+{
+    QDBusMessage msg = QDBusMessage::createMethodCall("org.kde.krunner", "/App", QString(), "newInstance");
+    QDBusConnection::sessionBus().send(msg);
+}
+
+
+void MenuLauncherApplet::createConfigurationInterface(KConfigDialog *parent)
+{
+    QWidget *p = new QWidget;
+    QGridLayout *l = new QGridLayout(p);
+    p->setLayout(l);
+
+    QLabel *formatLabel = new QLabel(i18nc("@label:listbox How to present applications in a KMenu-like menu", "Format:"), p);
+    l->addWidget(formatLabel, 0, 0);
+    d->formatComboBox = new QComboBox(p);
+    formatLabel->setBuddy(d->formatComboBox);
+    d->addItem(d->formatComboBox, i18nc("@item:inlistbox Format:", "Name Only"), MenuLauncherApplet::Name);
+    d->addItem(d->formatComboBox, i18nc("@item:inlistbox Format:", "Description Only"), MenuLauncherApplet::Description);
+    d->addItem(d->formatComboBox, i18nc("@item:inlistbox Format:", "Name Description"), MenuLauncherApplet::NameDescription);
+    d->addItem(d->formatComboBox, i18nc("@item:inlistbox Format:", "Description (Name)"), MenuLauncherApplet::DescriptionName);
+    d->addItem(d->formatComboBox, i18nc("@item:inlistbox Format:", "Name - Description"), MenuLauncherApplet::NameDashDescription);
+    l->addWidget(d->formatComboBox, 0, 1);
+
+    l->setColumnStretch(1,1);
+
+    d->setCurrentItem(d->formatComboBox, d->formattype);
+
+    parent->setButtons(KDialog::Ok | KDialog::Cancel | KDialog::Apply);
+    connect(parent, SIGNAL(applyClicked()), this, SLOT(configAccepted()));
+    connect(parent, SIGNAL(okClicked()), this, SLOT(configAccepted()));
+    parent->addPage(p, parent->windowTitle(), icon());
+}
+
+void MenuLauncherApplet::configAccepted()
+{
+    bool needssaving = false;
+    KConfigGroup cg = config();
+
+    int ft = d->formatComboBox->itemData(d->formatComboBox->currentIndex()).toInt();
+    if( ft != d->formattype ) {
+        d->formattype = (MenuLauncherApplet::FormatType) ft;
+        needssaving = true;
+
+        QMetaEnum e = metaObject()->enumerator(metaObject()->indexOfEnumerator("FormatType"));
+        cg.writeEntry("format", QByteArray(e.valueToKey(d->formattype)));
+    }
+
+    if( needssaving ) {
+        emit configNeedsSaving();
+
+        delete d->menuview;
+        d->menuview = 0;
+    }
+}
+
+void MenuLauncherApplet::toggleMenu(bool pressed)
+{
+    if (pressed) {
+        toggleMenu();
+    }
+}
+
+void MenuLauncherApplet::toggleMenu()
+{
+    if (!d->menuview) {
+        d->menuview = new Kickoff::MenuView();
+        connect(d->menuview, SIGNAL(triggered(QAction*)), this, SLOT(actionTriggered(QAction*)));
+        connect(d->menuview, SIGNAL(aboutToHide()), d->icon, SLOT(setUnpressed()));
+        //connect(d->menuview, SIGNAL(aboutToHide()), d->menuview, SLOT(deleteLater()));
+        
+        Kickoff::ApplicationModel *appModel = new Kickoff::ApplicationModel(d->menuview);
+        appModel->setDuplicatePolicy(Kickoff::ApplicationModel::ShowLatestOnlyPolicy);
+
+        //Top view
+        KConfig *cfg = new KConfig("mandrivarc");
+        KConfigGroup grp = cfg->group("menu");
+        QSvgRenderer *renderer;
+        QString fileName = grp.exists() ?
+            grp.readEntry( "topimage", KStandardDirs::locate("data", "mandriva/pics/top.svg") ) :
+            KStandardDirs::locate("data", "mandriva/pics/top.svg");
+        if(QFile::exists(fileName))
+        {
+            renderer = new QSvgRenderer(fileName);
+            if( renderer->isValid() )
+            {
+                QSize t = renderer->defaultSize();
+                QImage img(t, QImage::Format_ARGB32_Premultiplied);
+                img.fill(Qt::transparent);
+                QPainter p(&img);
+                renderer->render(&p);
+                p.end();
+                QWidgetAction *topAction = new QWidgetAction(d->menuview);
+                QLabel *top = new QLabel();
+                top->installEventFilter(d->eventSniffer);
+                top->setPixmap( QPixmap::fromImage( img ) );
+                topAction->setDefaultWidget( top );
+                d->menuview->addAction( topAction );
+            }
+        }
+
+        d->menuview->addTitle(i18n("All Applications"));
+        Kickoff::MenuView *appview = d->createMenuView(appModel);
+        d->addMenu(appview, false);
+
+        d->menuview->addTitle( i18n("Recently Used"));
+        Kickoff::MenuView *recentlyview = d->createMenuView(new Kickoff::RecentlyUsedModel(d->menuview));
+        d->addMenu(recentlyview, false);
+
+        d->menuview->addSeparator();
+        d->menuview->addTitle(i18n("Actions"));
+        QAction *runaction =  d->menuview->addAction(KIcon("system-run"),i18n("Run Command..."));
+        runaction->setData(KUrl("run:/command"));
+
+        d->menuview->addSeparator();
+        QAction *switchaction = d->menuview->addAction(KIcon("system-switch-user"),i18n("Switch User"));
+        switchaction->setData(KUrl("leave:/switch"));
+        QAction *lockaction = d->menuview->addAction(KIcon("system-lock-screen"),i18n("Lock"));
+        lockaction->setData(KUrl("leave:/lock"));
+        QAction *logoutaction = d->menuview->addAction(KIcon("system-shutdown"),i18n("Leave"));
+        logoutaction->setData(KUrl("leave:/logout"));
+        if( cfg )
+            delete cfg;
+    }
+
+    d->menuview->setAttribute(Qt::WA_DeleteOnClose);
+    d->menuview->popup(popupPosition(d->menuview->sizeHint()));
+    d->icon->setPressed();
+}
+
+void MenuLauncherApplet::actionTriggered(QAction *action)
+{
+    if( action->data().isNull() )
+        return;
+
+    KUrl url = action->data().value<KUrl>();
+    if (url.scheme() == "leave") {
+        if ( ! d->launcher ) {
+            d->launcher = new Kickoff::UrlItemLauncher(d->menuview);
+        }
+        d->launcher->openUrl(url.url());
+        return;
+    }
+    if (url.scheme() == "run")
+    {
+        runCommand();
+        return;
+    }
+    for(QWidget* w = action->parentWidget(); w; w = w->parentWidget()) {
+        if (Kickoff::MenuView *view = dynamic_cast<Kickoff::MenuView*>(w)) {
+            view->actionTriggered(action);
+            break;
+        }
+    }
+}
+
+QList<QAction*> MenuLauncherApplet::contextualActions()
+{
+    return d->actions;
+}
+
+#include "mandrivaapplet.moc"
diff -u -N -r k/plasma/applets/kickoff/mandrivaapplet/mandrivaapplet.h km/plasma/applets/kickoff/mandrivaapplet/mandrivaapplet.h
--- k/plasma/applets/kickoff/mandrivaapplet/mandrivaapplet.h	1969-12-31 21:00:00.000000000 -0300
+++ km/plasma/applets/kickoff/mandrivaapplet/mandrivaapplet.h	2008-08-25 15:40:44.000000000 -0300
@@ -0,0 +1,127 @@
+/*
+    Copyright 2007 Robert Knight <robertknight@gmail.com>
+    Copyright 2008 Sebastian Sauer <mail@dipe.org>
+
+    This library 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 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., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef SIMPLEAPPLET_H
+#define SIMPLEAPPLET_H
+
+// Plasma
+#include <plasma/applet.h>
+
+class QAction;
+
+/**
+* The MenuLauncherApplet class implements an applet that provides the traditional
+* aka classic KDE3 like KMenu application launcher using the Kickoff functionality.
+*/
+class MenuLauncherApplet : public Plasma::Applet
+{
+        Q_OBJECT
+        Q_ENUMS(ViewType)
+        Q_ENUMS(FormatType)
+    public:
+
+        /**
+        * The menu we like to display.
+        */
+        enum ViewType {
+            Combined = 0, ///< Standard Menu
+            Favorites, ///< Favorites Menu
+            Applications, ///< Applications Menu
+            Computer, ///< Computer Menu
+            RecentlyUsed, ///< Recently Used Menu
+            Leave ///< Leave Menu
+        };
+
+        /**
+        * How the text of the menuitems got formatted.
+        */
+        enum FormatType {
+            Name = 0, ///< Name only
+            Description, ///< Description only
+            NameDescription, ///< Name Description
+            DescriptionName, ///< Description (Name)
+            NameDashDescription ///< Name - Description
+        };
+
+        /**
+        * Constructor.
+        *
+        * \param parent The parent QObject.
+        * \param args The optional list of arguments.
+        */
+        MenuLauncherApplet(QObject *parent, const QVariantList &args);
+
+        /**
+        * Destructor.
+        */
+        virtual ~MenuLauncherApplet();
+
+        /**
+         * This method is called once the applet is loaded and added to a Corona.
+         **/
+        void init();
+
+        /**
+         * Call krunner
+         **/
+        void runCommand();
+
+        /**
+         * Called when any of the geometry constraints have been updated.
+         *
+         * @param constraints the type of constraints that were updated
+         */
+        void constraintsEvent(Plasma::Constraints constraints);
+
+        /**
+         * Returns a list of context-related QAction instances.
+         */
+        virtual QList<QAction*> contextualActions();
+
+public Q_SLOTS:
+        /**
+         * Switch the menu style from the traditional aka classic KDE3 like
+         * KMenu to the new Kickoff menu.
+         */
+        void switchMenuStyle();
+
+        void startMenuEditor();
+
+
+protected:
+        /**
+         * Create a configuration dialog.
+         */
+        void createConfigurationInterface(KConfigDialog *parent);
+
+private Q_SLOTS:
+        void configAccepted();
+        void toggleMenu(bool pressed);
+        void toggleMenu();
+        void actionTriggered(QAction *action);
+
+private:
+        class Private;
+        Private * const d;
+};
+
+K_EXPORT_PLASMA_APPLET(menulauncher, MenuLauncherApplet)
+
+#endif
diff -u -N -r k/plasma/applets/kickoff/mandrivaapplet/menuview.cpp km/plasma/applets/kickoff/mandrivaapplet/menuview.cpp
--- k/plasma/applets/kickoff/mandrivaapplet/menuview.cpp	1969-12-31 21:00:00.000000000 -0300
+++ km/plasma/applets/kickoff/mandrivaapplet/menuview.cpp	2008-08-29 12:51:29.000000000 -0300
@@ -0,0 +1,400 @@
+/*
+    Copyright 2007 Robert Knight <robertknight@gmail.com>
+    Copyright 2008 Sebastian Sauer <mail@dipe.org>
+
+    This library 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 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., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+// Own
+#include "menuview.h"
+
+// Qt
+#include <QtCore/QAbstractItemModel>
+#include <QtCore/QStack>
+#include <QtGui/QApplication>
+#include <QtGui/QMouseEvent>
+#include <QPersistentModelIndex>
+
+// KDE
+#include <KDebug>
+#include <KUrl>
+#include <KIconLoader>
+
+// Local
+#include "core/models.h"
+#include "core/itemhandlers.h"
+
+Q_DECLARE_METATYPE(QPersistentModelIndex)
+Q_DECLARE_METATYPE(QAction*)
+
+using namespace Kickoff;
+
+/// @internal d-pointer class
+class MenuView::Private
+{
+public:
+    enum { ActionRole = Qt::UserRole + 52 };
+
+    Private(MenuView *parent) : q(parent) , model(0) , column(0), launcher(new UrlItemLauncher(parent)), formattype(MenuView::DescriptionName) {}
+
+    QAction *createActionForIndex(const QModelIndex& index, QMenu *parent)
+    {
+        QAction *action = 0; 
+
+        if (model->hasChildren(index)) {
+            KMenu *childMenu = new KMenu(parent);
+            childMenu->installEventFilter(q);
+
+            action = childMenu->menuAction();
+
+            if (model->canFetchMore(index)) {
+                model->fetchMore(index);
+            }
+
+            buildBranch(childMenu, index);
+        } else {
+            action = q->createLeafAction(index,parent);
+        }
+
+        q->updateAction(action,index);
+
+        return action;
+    }
+
+    void buildBranch(QMenu *menu, const QModelIndex& parent)
+    {
+        int rowCount = model->rowCount(parent);
+        for (int i = 0; i < rowCount; i++) {
+            QAction *action = createActionForIndex(model->index(i, column, parent), menu);
+            menu->addAction(action);
+        }
+    }
+
+    MenuView * const q;
+    QAbstractItemModel *model;
+    int column;
+    UrlItemLauncher *launcher;
+    MenuView::FormatType formattype;
+    QPoint mousePressPos;
+};
+
+MenuView::MenuView(QWidget *parent)
+    : KMenu(parent)
+    , d(new Private(this))
+{
+    installEventFilter(this);
+}
+
+MenuView::~MenuView()
+{
+    delete d;
+}
+
+QAction *MenuView::createLeafAction(const QModelIndex&,QObject *parent)
+{
+    return new QAction(parent);
+}
+
+void MenuView::updateAction(QAction *action,const QModelIndex& index)
+{
+    Q_ASSERT(d->model);
+
+    QString text = index.data(Qt::DisplayRole).value<QString>().replace("&","&&"); // describing text, e.g. "Spreadsheet" or "Rekall" (right, sometimes the text is also used for the generic app-name)
+    QString name = index.data(Kickoff::SubTitleRole).value<QString>().replace("&","&&"); // the generic name, e.g. "kspread" or "OpenOffice.org Spreadsheet" or just "" (right, it's a mess too)
+    if( action->menu()!=0 ) { // if its an item with sub-menuitems, we probably like to thread them another way...
+        action->setText(text);
+    }
+    else {
+        switch( d->formattype ) {
+            case Name: {
+                if (name.isEmpty()) {
+                    action->setText(text);
+                } else {
+                    action->setText(name);
+                }
+            } break;
+            case Description: {
+                if (name.contains(text,Qt::CaseInsensitive)) {
+                    text = name;
+                }
+                action->setText(text);
+            } break;
+            case NameDescription: // fall through
+            case NameDashDescription: // fall through
+            case DescriptionName: {
+                if (!name.isEmpty()) { // seems we have a program, but some of them don't define a name at all
+                    if (text.contains(name,Qt::CaseInsensitive)) { // sometimes the description contains also the name
+                        action->setText(text);
+                    } else if (name.contains(text,Qt::CaseInsensitive)) { // and sometimes the name also contains the description
+                        action->setText(name);
+                    } else { // seems we have a perfect desktop-file (likely a KDE one, heh) and name+description are clear separated
+                        if (d->formattype == NameDescription) {
+                            action->setText(QString("%1 %2").arg(name).arg(text));
+                        } else if (d->formattype == NameDashDescription) {
+                            action->setText(QString("%1 - %2").arg(name).arg(text));
+                        } else {
+                            action->setText(QString("%1 (%2)").arg(text).arg(name));
+                        }
+                    }
+                }
+                else { // if there is no name, let's just use the describing text
+                    action->setText(text);
+                }
+            } break;
+        }
+    }
+
+    action->setIcon(index.data(Qt::DecorationRole).value<QIcon>());
+
+    // we map modelindex and action together
+    action->setData(qVariantFromValue(QPersistentModelIndex(index)));
+
+    // don't emit the dataChanged-signal cause else we may end in a infinite loop
+    d->model->blockSignals(true);
+    d->model->setData(index, qVariantFromValue(action), Private::ActionRole);
+    d->model->blockSignals(false);
+}
+
+bool MenuView::eventFilter(QObject *watched, QEvent *event)
+{
+    if (event->type() == QEvent::MouseMove) {
+        QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
+        QMenu *watchedMenu = qobject_cast<QMenu*>(watched);
+        const int mousePressDistance = !d->mousePressPos.isNull() ? (mouseEvent->pos() - d->mousePressPos).manhattanLength() : 0;
+
+        if (watchedMenu && mouseEvent->buttons() & Qt::LeftButton
+            && mousePressDistance >= QApplication::startDragDistance()) {
+            QAction *action = watchedMenu->actionAt(mouseEvent->pos());
+            if (!action) {
+                return KMenu::eventFilter(watched, event);
+            }
+
+            QPersistentModelIndex index = action->data().value<QPersistentModelIndex>();
+            if (!index.isValid()) {
+                return KMenu::eventFilter(watched, event);
+            }
+
+            QString urlString = index.data(UrlRole).toString();
+            if (urlString.isNull()) {
+                return KMenu::eventFilter(watched, event);
+            }
+
+            QMimeData *mimeData = new QMimeData();
+            mimeData->setData("text/uri-list", urlString.toAscii());
+            mimeData->setText(mimeData->text());
+            QDrag *drag = new QDrag(this);
+            drag->setMimeData(mimeData);
+
+            QIcon icon = action->icon();
+            drag->setPixmap(icon.pixmap(IconSize(KIconLoader::Desktop)));
+
+            d->mousePressPos = QPoint();
+
+            Qt::DropAction dropAction = drag->exec();
+            Q_UNUSED(dropAction);
+
+            return true;
+        }
+    } else if (event->type() == QEvent::MouseButtonPress) {
+        QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
+        QMenu *watchedMenu = qobject_cast<QMenu*>(watched);
+        if (watchedMenu) {
+            d->mousePressPos = mouseEvent->pos();
+        }
+    } else if (event->type() == QEvent::MouseButtonRelease) {
+        QMenu *watchedMenu = qobject_cast<QMenu*>(watched);
+        if (watchedMenu) {
+            d->mousePressPos = QPoint();
+        }
+    }
+
+    return KMenu::eventFilter(watched, event);
+}
+
+void MenuView::setModel(QAbstractItemModel *model)
+{
+    if (d->model) {
+        d->model->disconnect(this);
+    }
+    d->model = model;
+    clear();
+    if (d->model) {
+        d->buildBranch(this,QModelIndex());
+        connect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int)));
+        connect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)));
+        connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)));
+        connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
+    }
+}
+
+QAbstractItemModel *MenuView::model() const
+{
+    return d->model;
+}
+
+UrlItemLauncher *MenuView::launcher() const
+{
+    return d->launcher;
+}
+
+QModelIndex MenuView::indexForAction(QAction *action) const
+{
+    Q_ASSERT(d->model);
+    Q_ASSERT(action != 0);
+    QPersistentModelIndex index = action->data().value<QPersistentModelIndex>();
+    Q_ASSERT(index.isValid());
+    return index;
+}
+
+QAction *MenuView::actionForIndex(const QModelIndex& index) const
+{
+    Q_ASSERT(d->model);
+
+    if (!index.isValid()) {
+        return this->menuAction();
+    }
+
+    QVariant v = d->model->data(index, Private::ActionRole);
+    Q_ASSERT(v.isValid());
+    QAction* a = v.value<QAction*>();
+    Q_ASSERT(a);
+    return a;
+}
+
+bool MenuView::isValidIndex(const QModelIndex& index) const
+{
+    QVariant v = (d->model && index.isValid()) ? d->model->data(index, Private::ActionRole) : QVariant();
+    return v.isValid() && v.value<QAction*>();
+}
+
+void MenuView::rowsAboutToBeInserted(const QModelIndex& parent,int start,int end)
+{
+    if (!isValidIndex(parent)) {
+        // can happen if the models data is incomplete yet
+        return;
+    }
+
+    Q_ASSERT(d->model);
+
+    QAction *menuAction = actionForIndex(parent);
+    if(!menuAction) return;
+
+    QMenu *menu = menuAction->menu();
+    if(!menu) return;
+
+    QList<QAction*> newActions;
+    for (int row = start; row <= end; row++) {
+        QModelIndex index = d->model->index(row, d->column, parent);
+        if( index.isValid() )
+        {
+            QAction *newAction = d->createActionForIndex(index, menu);
+            newActions << newAction;
+        }
+    }
+
+    if (start < menu->actions().count()) {
+        menu->insertActions(menu->actions()[start],newActions);
+    } else {
+        menu->addActions(newActions);
+    }
+}
+
+void MenuView::rowsAboutToBeRemoved(const QModelIndex& parent,int start,int end)
+{
+    if (!isValidIndex(parent)) {
+        // can happen if the models data is incomplete yet
+        return;
+    }
+
+    Q_ASSERT(d->model);
+
+    QAction *menuAction = actionForIndex(parent);
+    Q_ASSERT(menuAction);
+
+    QMenu *menu = menuAction->menu();
+    //Q_ASSERT(menu);
+
+    if( ! menu )
+       return;
+
+    QList<QAction*> actions = menu->actions();
+    Q_ASSERT(end < actions.count());
+    for (int row = end; row >= start; row--) {
+        menu->removeAction(actions[row]);
+    }
+}
+
+void MenuView::dataChanged(const QModelIndex& topLeft,const QModelIndex& bottomRight)
+{
+    if (!isValidIndex(topLeft.parent())) {
+        // can happen if the models data is incomplete yet
+        return;
+    }
+
+    Q_ASSERT(d->model);
+
+    QAction *menuAction = actionForIndex(topLeft.parent());
+    Q_ASSERT(menuAction);
+
+    QMenu *menu = menuAction->menu();
+    Q_ASSERT(menu);
+    
+    //if( menu->isEmpty() )
+    //    return;
+
+    QList<QAction*> actions = menu->actions();
+    Q_ASSERT(bottomRight.row() < actions.count());
+
+    for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
+        updateAction(actions[row], d->model->index(row,d->column,topLeft.parent()));
+    }
+}
+
+void MenuView::modelReset()
+{
+    // force clearance of the menu and rebuild from scratch
+    setModel(d->model);
+}
+
+void MenuView::setColumn(int column)
+{
+    d->column = column;
+    modelReset();
+}
+
+int MenuView::column() const
+{
+    return d->column;
+}
+
+MenuView::FormatType MenuView::formatType() const
+{
+    return d->formattype;
+}
+
+void MenuView::setFormatType(MenuView::FormatType formattype)
+{
+    d->formattype = formattype;
+}
+
+void MenuView::actionTriggered(QAction *action)
+{
+    Q_ASSERT(d->model);
+    QModelIndex index = indexForAction(action);
+    Q_ASSERT(index.isValid());
+    d->launcher->openItem(index);
+}
+
+#include "menuview.moc"
diff -u -N -r k/plasma/applets/kickoff/mandrivaapplet/menuview.h km/plasma/applets/kickoff/mandrivaapplet/menuview.h
--- k/plasma/applets/kickoff/mandrivaapplet/menuview.h	1969-12-31 21:00:00.000000000 -0300
+++ km/plasma/applets/kickoff/mandrivaapplet/menuview.h	2008-08-29 12:51:59.000000000 -0300
@@ -0,0 +1,156 @@
+/*
+    Copyright 2007 Robert Knight <robertknight@gmail.com>
+    Copyright 2008 Sebastian Sauer <mail@dipe.org>
+
+    This library 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 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., 51 Franklin Street, Fifth Floor,
+    Boston, MA 02110-1301, USA.
+*/
+
+#ifndef MENUVIEW_H
+#define MENUVIEW_H
+
+// Qt
+#include <QModelIndex>
+
+// KDE
+#include <KMenu>
+
+class QAbstractItemModel;
+
+namespace Kickoff
+{
+
+class UrlItemLauncher;
+
+/**
+ * A view for a QAbstractItemModel which displays the model (set with setModel())
+ * as a hierarchical menu.
+ *
+ * When the menu is executed and an item is triggered, the model index associated with the 
+ * chosen item can be found by calling indexForAction() with the triggered action.  The action
+ * associated with a particular model index can be found using actionForIndex().
+ *
+ * MenuView creates actions for parts of the model on demand as the user explores the menu.
+ * The type of action created for leaf items in the tree can be changed by re-implementing
+ * createLeafAction().  When a new action is created or if the corresponding model
+ * index's data changes, updateAction() is called to set the action's properties.  This
+ * can be reimplemented in sub-classes to change the appearance of the actions.
+ */ 
+class MenuView : public KMenu 
+{
+Q_OBJECT
+public:
+
+    /** Constructs a new menu with the specified @p parent */
+    MenuView(QWidget *parent = 0);
+    /** Destructor */
+    virtual ~MenuView();
+
+    /** Sets the model displayed by this menu. */
+    void setModel(QAbstractItemModel *model);
+    /** Returns the model displayed by this menu. */
+    QAbstractItemModel *model() const;
+
+    /** Returns the UrlItemLauncher used to handle launching of urls. */
+    UrlItemLauncher *launcher() const;
+
+    /** Maps an action in the menu to its corresponding index in model() */
+    QModelIndex indexForAction(QAction *action) const;
+
+    /** 
+     * Maps an index in the model to its corresponding action in the menu. 
+     * If @p index is invalid then menuAction() will be returned.  If @p index
+     * is in a part of the tree which the user has not yet explored then 0 will
+     * be returned because the menu hierarchy is constructed on-demand as the user
+     * explores the menu. 
+     */
+    QAction *actionForIndex(const QModelIndex& index) const;
+
+    /**
+     * Returns true if the passed \p index is a valid QModelIndex and does
+     * represent a QAction. This method is equal to the actionForIndex() method
+     * above except allowing to explicit ask if the QModelIndex is valid and
+     * to indicate that way, that it may the case that the QModelIndex went
+     * out of scope already.
+     */
+    bool isValidIndex(const QModelIndex& index) const;
+
+    /** Sets the column from the model which is used to construct the actions in the menu. */
+    void setColumn(int column);
+    /** Returns the column from the model which is used to construct the actions in the menu. */
+    int column() const;
+
+    /** The format type enumeration. */
+    enum FormatType {
+        Name = 0, ///< Name only
+        Description, ///< Description only
+        NameDescription, ///< Name (Description)
+        DescriptionName, ///< Description (Name)
+        NameDashDescription ///< Name - Description
+    };
+    /** \return the format type. */
+    FormatType formatType() const;
+    /** Set the format type. */
+    void setFormatType(FormatType formattype);
+
+protected:
+
+    /**
+    * Creates a new action to represent a leaf index in the tree.  A leaf index
+    * is one which does not have children.  The default implementation creates a new
+    * QAction with no properties set.  updateAction() is immediately called on the
+    * return action to set its text and icon.
+    *
+    * @param index The index in the model for which an action should be created
+    * @param parent The object which should be set as the parent of the new action
+    */
+    virtual QAction *createLeafAction(const QModelIndex& index,QObject *parent);
+
+    /** 
+     * Sets the text, icon and other properties of @p action using the data 
+     * associated with @p index in the model().  This is called whenever the data for
+     * a range of indexes in the tree is altered. 
+     *
+     * The default implementation sets the action's text to the index's Qt::DisplayRole data
+     * and the action's icon to the index's Qt::DecorationRole data.
+     */
+    virtual void updateAction(QAction *action, const QModelIndex& index);
+
+    // reimplemented
+    virtual bool eventFilter(QObject * watched, QEvent *event);
+
+public Q_SLOTS:
+    // an item in the menu got triggered
+    void actionTriggered(QAction* action);
+
+private Q_SLOTS:
+    // new items are about to be inserted into the model
+    void rowsAboutToBeInserted(const QModelIndex& parent,int start,int end);
+    // existing items are about to be removed from the model
+    void rowsAboutToBeRemoved(const QModelIndex& parent,int start,int end);
+    // data within an item of the model change
+    void dataChanged(const QModelIndex& topLeft,const QModelIndex& bottomRight);
+    // the model did reset itself and all items are invalid
+    void modelReset();
+
+private:
+    class Private;
+    Private * const d;
+};
+
+}
+
+#endif // MENUVIEW_H
+
diff -u -N -r k/plasma/applets/kickoff/mandrivaapplet/plasma-applet-mandrivalauncher.desktop km/plasma/applets/kickoff/mandrivaapplet/plasma-applet-mandrivalauncher.desktop
--- k/plasma/applets/kickoff/mandrivaapplet/plasma-applet-mandrivalauncher.desktop	1969-12-31 21:00:00.000000000 -0300
+++ km/plasma/applets/kickoff/mandrivaapplet/plasma-applet-mandrivalauncher.desktop	2008-08-19 11:38:34.000000000 -0300
@@ -0,0 +1,16 @@
+[Desktop Entry]
+Name=Mandriva Launcher Menu
+Comment=Mandriva menu based on simpleapplet
+Icon=start-here-kde
+Type=Service
+X-KDE-ServiceTypes=Plasma/Applet
+X-KDE-Library=plasma_applet_mandrivalauncher
+X-KDE-PluginInfo-Author=Helio Castro
+X-KDE-PluginInfo-Email=helio@mandriva.com
+X-KDE-PluginInfo-Name=mandrivalauncher
+X-KDE-PluginInfo-Version=0.1
+X-KDE-PluginInfo-Website=http://www.mandriva.com
+X-KDE-PluginInfo-Category=Application Launchers
+X-KDE-PluginInfo-Depends=
+X-KDE-PluginInfo-License=GPL
+X-KDE-PluginInfo-EnabledByDefault=false