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