• Skip to content
  • Skip to link menu
KDE 4.1 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

Applets

menuview.cpp

Go to the documentation of this file.
00001 /*
00002     Copyright 2007 Robert Knight <robertknight@gmail.com>
00003     Copyright 2008 Sebastian Sauer <mail@dipe.org>
00004 
00005     This library is free software; you can redistribute it and/or
00006     modify it under the terms of the GNU Library General Public
00007     License as published by the Free Software Foundation; either
00008     version 2 of the License, or (at your option) any later version.
00009 
00010     This library is distributed in the hope that it will be useful,
00011     but WITHOUT ANY WARRANTY; without even the implied warranty of
00012     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013     Library General Public License for more details.
00014 
00015     You should have received a copy of the GNU Library General Public License
00016     along with this library; see the file COPYING.LIB.  If not, write to
00017     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018     Boston, MA 02110-1301, USA.
00019 */
00020 
00021 // Own
00022 #include "menuview.h"
00023 
00024 // Qt
00025 #include <QtCore/QAbstractItemModel>
00026 #include <QtCore/QStack>
00027 #include <QtGui/QApplication>
00028 #include <QtGui/QMouseEvent>
00029 #include <QPersistentModelIndex>
00030 
00031 // KDE
00032 #include <KDebug>
00033 #include <KUrl>
00034 #include <KIconLoader>
00035 
00036 // Local
00037 #include "core/models.h"
00038 #include "core/itemhandlers.h"
00039 
00040 Q_DECLARE_METATYPE(QPersistentModelIndex)
00041 Q_DECLARE_METATYPE(QAction*)
00042 
00043 using namespace Kickoff;
00044 
00046 class MenuView::Private
00047 {
00048 public:
00049     enum { ActionRole = Qt::UserRole + 52 };
00050 
00051     Private(MenuView *parent) : q(parent) , model(0) , column(0), launcher(new UrlItemLauncher(parent)), formattype(MenuView::DescriptionName) {}
00052 
00053     QAction *createActionForIndex(const QModelIndex& index, QMenu *parent)
00054     {
00055         Q_ASSERT(index.isValid());
00056 
00057         QAction *action = 0; 
00058 
00059         if (model->hasChildren(index)) {
00060             KMenu *childMenu = new KMenu(parent);
00061             childMenu->installEventFilter(q);
00062 
00063             QObject::connect(childMenu, SIGNAL(aboutToShow()), q, SLOT(fillSubMenu()));
00064             action = childMenu->menuAction();
00065         } else {
00066             action = q->createLeafAction(index,parent);
00067         }
00068 
00069         q->updateAction(action,index);
00070 
00071         return action;
00072     }
00073 
00074     void buildBranch(QMenu *menu, const QModelIndex& parent)
00075     {
00076         int rowCount = model->rowCount(parent);
00077         for (int i = 0; i < rowCount; i++) {
00078             QAction *action = createActionForIndex(model->index(i, column, parent), menu);
00079             menu->addAction(action);
00080         }
00081     }
00082 
00083     MenuView * const q;
00084     QAbstractItemModel *model;
00085     int column;
00086     UrlItemLauncher *launcher;
00087     MenuView::FormatType formattype;
00088     QPoint mousePressPos;
00089 };
00090 
00091 MenuView::MenuView(QWidget *parent)
00092     : KMenu(parent)
00093     , d(new Private(this))
00094 {
00095     installEventFilter(this);
00096 }
00097 
00098 MenuView::~MenuView()
00099 {
00100     delete d;
00101 }
00102 
00103 QAction *MenuView::createLeafAction(const QModelIndex&,QObject *parent)
00104 {
00105     return new QAction(parent);
00106 }
00107 
00108 void MenuView::updateAction(QAction *action,const QModelIndex& index)
00109 {
00110     Q_ASSERT(d->model);
00111 
00112     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)
00113     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)
00114     if( action->menu()!=0 ) { // if its an item with sub-menuitems, we probably like to thread them another way...
00115         action->setText(text);
00116     }
00117     else {
00118         switch( d->formattype ) {
00119             case Name: {
00120                 if (name.isEmpty()) {
00121                     action->setText(text);
00122                 } else {
00123                     action->setText(name);
00124                 }
00125             } break;
00126             case Description: {
00127                 if (name.contains(text,Qt::CaseInsensitive)) {
00128                     text = name;
00129                 }
00130                 action->setText(text);
00131             } break;
00132             case NameDescription: // fall through
00133             case DescriptionName: {
00134                 if (!name.isEmpty()) { // seems we have a program, but some of them don't define a name at all
00135                     if (text.contains(name,Qt::CaseInsensitive)) { // sometimes the description contains also the name
00136                         action->setText(text);
00137                     } else if (name.contains(text,Qt::CaseInsensitive)) { // and sometimes the name also contains the description
00138                         action->setText(name);
00139                     } else { // seems we have a perfect desktop-file (likely a KDE one, heh) and name+description are clear separated
00140                         if (d->formattype == NameDescription) {
00141                             action->setText(QString("%1 %2").arg(name).arg(text));
00142                         } else {
00143                             action->setText(QString("%1 (%2)").arg(text).arg(name));
00144                         }
00145                     }
00146                 }
00147                 else { // if there is no name, let's just use the describing text
00148                     action->setText(text);
00149                 }
00150             } break;
00151         }
00152     }
00153 
00154     action->setIcon(index.data(Qt::DecorationRole).value<QIcon>());
00155 
00156     // we map modelindex and action together
00157     action->setData(qVariantFromValue(QPersistentModelIndex(index)));
00158 
00159     // don't emit the dataChanged-signal cause else we may end in a infinite loop
00160     d->model->blockSignals(true);
00161     d->model->setData(index, qVariantFromValue(action), Private::ActionRole);
00162     d->model->blockSignals(false);
00163 }
00164 
00165 bool MenuView::eventFilter(QObject *watched, QEvent *event)
00166 {
00167     if (event->type() == QEvent::MouseMove) {
00168         QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
00169         QMenu *watchedMenu = qobject_cast<QMenu*>(watched);
00170         const int mousePressDistance = !d->mousePressPos.isNull() ? (mouseEvent->pos() - d->mousePressPos).manhattanLength() : 0;
00171 
00172         if (watchedMenu && mouseEvent->buttons() & Qt::LeftButton
00173             && mousePressDistance >= QApplication::startDragDistance()) {
00174             QAction *action = watchedMenu->actionAt(mouseEvent->pos());
00175             if (!action) {
00176                 return KMenu::eventFilter(watched, event);
00177             }
00178 
00179             QPersistentModelIndex index = action->data().value<QPersistentModelIndex>();
00180             if (!index.isValid()) {
00181                 return KMenu::eventFilter(watched, event);
00182             }
00183 
00184             QString urlString = index.data(UrlRole).toString();
00185             if (urlString.isNull()) {
00186                 return KMenu::eventFilter(watched, event);
00187             }
00188 
00189             QMimeData *mimeData = new QMimeData();
00190             mimeData->setData("text/uri-list", urlString.toAscii());
00191             mimeData->setText(mimeData->text());
00192             QDrag *drag = new QDrag(this);
00193             drag->setMimeData(mimeData);
00194 
00195             QIcon icon = action->icon();
00196             drag->setPixmap(icon.pixmap(IconSize(KIconLoader::Desktop)));
00197 
00198             d->mousePressPos = QPoint();
00199 
00200             Qt::DropAction dropAction = drag->exec();
00201             Q_UNUSED(dropAction);
00202 
00203             return true;
00204         }
00205     } else if (event->type() == QEvent::MouseButtonPress) {
00206         QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
00207         QMenu *watchedMenu = qobject_cast<QMenu*>(watched);
00208         if (watchedMenu) {
00209             d->mousePressPos = mouseEvent->pos();
00210         }
00211     } else if (event->type() == QEvent::MouseButtonRelease) {
00212         QMenu *watchedMenu = qobject_cast<QMenu*>(watched);
00213         if (watchedMenu) {
00214             d->mousePressPos = QPoint();
00215         }
00216     }
00217 
00218     return KMenu::eventFilter(watched, event);
00219 }
00220 
00221 void MenuView::setModel(QAbstractItemModel *model)
00222 {
00223     if (d->model) {
00224         d->model->disconnect(this);
00225     }
00226     d->model = model;
00227     clear();
00228     if (d->model) {
00229         d->buildBranch(this,QModelIndex());
00230         connect(this, SIGNAL(aboutToShow()), this, SLOT(aboutToShow()));
00231     }
00232 }
00233 
00234 void MenuView::aboutToShow()
00235 {
00236     // not needed to be called more then once
00237     disconnect(this, SIGNAL(aboutToShow()), this, SLOT(aboutToShow()));
00238     // we need to delay reaction to following signals till all the setup-work is done else
00239     // it may the case we got called to early at a time where the models data is incomplete.
00240     connect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, SLOT(rowsAboutToBeInserted(QModelIndex,int,int)));
00241     connect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, SLOT(rowsAboutToBeRemoved(QModelIndex,int,int)));
00242     connect(d->model, SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, SLOT(dataChanged(QModelIndex,QModelIndex)));
00243     connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
00244 }
00245 
00246 QAbstractItemModel *MenuView::model() const
00247 {
00248     return d->model;
00249 }
00250 
00251 UrlItemLauncher *MenuView::launcher() const
00252 {
00253     return d->launcher;
00254 }
00255 
00256 QModelIndex MenuView::indexForAction(QAction *action) const
00257 {
00258     Q_ASSERT(d->model);
00259     Q_ASSERT(action != 0);
00260     QPersistentModelIndex index = action->data().value<QPersistentModelIndex>();
00261     Q_ASSERT(index.isValid());
00262     return index;
00263 }
00264 
00265 QAction *MenuView::actionForIndex(const QModelIndex& index) const
00266 {
00267     Q_ASSERT(d->model);
00268 
00269     if (!index.isValid()) {
00270         return this->menuAction();
00271     }
00272 
00273     QVariant v = d->model->data(index, Private::ActionRole);
00274     Q_ASSERT(v.isValid());
00275     QAction* a = v.value<QAction*>();
00276     Q_ASSERT(a);
00277     return a;
00278 }
00279 
00280 void MenuView::rowsAboutToBeInserted(const QModelIndex& parent,int start,int end)
00281 {
00282     Q_ASSERT(d->model);
00283 
00284     QAction *menuAction = actionForIndex(parent);
00285     Q_ASSERT(menuAction);
00286 
00287     QMenu *menu = menuAction->menu();
00288     Q_ASSERT(menu);
00289 
00290     QList<QAction*> newActions;
00291     for (int row = start; row <= end; row++) {
00292         QModelIndex index = d->model->index(row, d->column, parent);
00293         QAction *newAction = d->createActionForIndex(index, menu);
00294         newActions << newAction;
00295     }
00296 
00297     if (start < menu->actions().count()) {
00298         menu->insertActions(menu->actions()[start],newActions);
00299     } else {
00300         menu->addActions(newActions);
00301     }
00302 }
00303 
00304 void MenuView::rowsAboutToBeRemoved(const QModelIndex& parent,int start,int end)
00305 {
00306     QAction *menuAction = actionForIndex(parent);
00307     Q_ASSERT(menuAction);
00308 
00309     QMenu *menu = menuAction->menu();
00310     Q_ASSERT(menu);
00311 
00312     QList<QAction*> actions = menu->actions();
00313     Q_ASSERT(end < actions.count());
00314     for (int row = end; row >= start; row--) {
00315         menu->removeAction(actions[row]);
00316     }
00317 }
00318 
00319 void MenuView::dataChanged(const QModelIndex& topLeft,const QModelIndex& bottomRight)
00320 {
00321     Q_ASSERT(d->model);
00322 
00323     QAction *menuAction = actionForIndex(topLeft.parent());
00324     Q_ASSERT(menuAction);
00325 
00326     QMenu *menu = menuAction->menu();
00327     Q_ASSERT(menu);
00328 
00329     QList<QAction*> actions = menu->actions();
00330     Q_ASSERT(bottomRight.row() < actions.count());
00331 
00332     for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
00333         updateAction(actions[row], d->model->index(row,d->column,topLeft.parent()));
00334     }
00335 }
00336 
00337 void MenuView::modelReset()
00338 {
00339     // force clearance of the menu and rebuild from scratch
00340     setModel(d->model);
00341 }
00342 
00343 void MenuView::fillSubMenu()
00344 {
00345     Q_ASSERT(d->model);
00346 
00347     QMenu *subMenu = qobject_cast<QMenu*>(sender());
00348     Q_ASSERT(subMenu);
00349     Q_ASSERT(subMenu->isEmpty());
00350 
00351     disconnect(subMenu, SIGNAL(aboutToShow()), this, SLOT(fillSubMenu()));
00352 
00353     QModelIndex menuIndex = indexForAction(subMenu->menuAction());
00354     Q_ASSERT(menuIndex.isValid());
00355 
00356     if (d->model->canFetchMore(menuIndex)) {
00357         d->model->fetchMore(menuIndex);
00358     }
00359 
00360     d->buildBranch(subMenu, menuIndex);
00361 }
00362 
00363 void MenuView::setColumn(int column)
00364 {
00365     d->column = column;
00366     modelReset();
00367 }
00368 
00369 int MenuView::column() const
00370 {
00371     return d->column;
00372 }
00373 
00374 MenuView::FormatType MenuView::formatType() const
00375 {
00376     return d->formattype;
00377 }
00378 
00379 void MenuView::setFormatType(MenuView::FormatType formattype)
00380 {
00381     d->formattype = formattype;
00382 }
00383 
00384 void MenuView::actionTriggered(QAction *action)
00385 {
00386     Q_ASSERT(d->model);
00387     QModelIndex index = indexForAction(action);
00388     Q_ASSERT(index.isValid());
00389     d->launcher->openItem(index);
00390 }
00391 
00392 #include "menuview.moc"

Applets

Skip menu "Applets"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members

API Reference

Skip menu "API Reference"
  • KWin
  •   KWin Libraries
  • Libraries
  •   libkworkspace
  •   libplasma
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.4
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal