• Skip to content
  • Skip to link menu
KDE 4.2 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 <QtCore/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         Q_ASSERT(index.isValid());
00055 
00056         QAction *action = 0;
00057 
00058         if (model->hasChildren(index)) {
00059             KMenu *childMenu = new KMenu(parent);
00060             childMenu->installEventFilter(q);
00061 
00062             action = childMenu->menuAction();
00063 
00064             if (model->canFetchMore(index)) {
00065                 model->fetchMore(index);
00066             }
00067 
00068             buildBranch(childMenu, index);
00069         } else {
00070             action = q->createLeafAction(index, parent);
00071         }
00072 
00073         q->updateAction(action, index);
00074 
00075         return action;
00076     }
00077 
00078     void buildBranch(QMenu *menu, const QModelIndex& parent) {
00079         int rowCount = model->rowCount(parent);
00080         for (int i = 0; i < rowCount; i++) {
00081             QAction *action = createActionForIndex(model->index(i, column, parent), menu);
00082             menu->addAction(action);
00083         }
00084     }
00085 
00086     MenuView * const q;
00087     QAbstractItemModel *model;
00088     int column;
00089     UrlItemLauncher *launcher;
00090     MenuView::FormatType formattype;
00091     QPoint mousePressPos;
00092 };
00093 
00094 MenuView::MenuView(QWidget *parent)
00095         : KMenu(parent)
00096         , d(new Private(this))
00097 {
00098     installEventFilter(this);
00099 }
00100 
00101 MenuView::~MenuView()
00102 {
00103     delete d;
00104 }
00105 
00106 QAction *MenuView::createLeafAction(const QModelIndex&, QObject *parent)
00107 {
00108     return new QAction(parent);
00109 }
00110 
00111 void MenuView::updateAction(QAction *action, const QModelIndex& index)
00112 {
00113     Q_ASSERT(d->model);
00114 
00115     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)
00116     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)
00117     if (action->menu() != 0) { // if its an item with sub-menuitems, we probably like to thread them another way...
00118         action->setText(text);
00119     } else {
00120         switch (d->formattype) {
00121         case Name: {
00122             if (name.isEmpty()) {
00123                 action->setText(text);
00124             } else {
00125                 action->setText(name);
00126             }
00127         }
00128         break;
00129         case Description: {
00130             if (name.contains(text, Qt::CaseInsensitive)) {
00131                 text = name;
00132             }
00133             action->setText(text);
00134         }
00135         break;
00136         case NameDescription: // fall through
00137         case NameDashDescription: // fall through
00138         case DescriptionName: {
00139             if (!name.isEmpty()) { // seems we have a program, but some of them don't define a name at all
00140                 if (text.contains(name, Qt::CaseInsensitive)) { // sometimes the description contains also the name
00141                     action->setText(text);
00142                 } else if (name.contains(text, Qt::CaseInsensitive)) { // and sometimes the name also contains the description
00143                     action->setText(name);
00144                 } else { // seems we have a perfect desktop-file (likely a KDE one, heh) and name+description are clear separated
00145                     if (d->formattype == NameDescription) {
00146                         action->setText(QString("%1 %2").arg(name).arg(text));
00147                     } else if (d->formattype == NameDashDescription) {
00148                         action->setText(QString("%1 - %2").arg(name).arg(text));
00149                     } else {
00150                         action->setText(QString("%1 (%2)").arg(text).arg(name));
00151                     }
00152                 }
00153             } else { // if there is no name, let's just use the describing text
00154                 action->setText(text);
00155             }
00156         }
00157         break;
00158         }
00159     }
00160 
00161     action->setIcon(index.data(Qt::DecorationRole).value<QIcon>());
00162 
00163     // we map modelindex and action together
00164     action->setData(qVariantFromValue(QPersistentModelIndex(index)));
00165 
00166     // don't emit the dataChanged-signal cause else we may end in a infinite loop
00167     d->model->blockSignals(true);
00168     d->model->setData(index, qVariantFromValue(action), Private::ActionRole);
00169     d->model->blockSignals(false);
00170 }
00171 
00172 bool MenuView::eventFilter(QObject *watched, QEvent *event)
00173 {
00174     if (event->type() == QEvent::MouseMove) {
00175         QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
00176         QMenu *watchedMenu = qobject_cast<QMenu*>(watched);
00177         const int mousePressDistance = !d->mousePressPos.isNull() ? (mouseEvent->pos() - d->mousePressPos).manhattanLength() : 0;
00178 
00179         if (watchedMenu && mouseEvent->buttons() & Qt::LeftButton
00180                 && mousePressDistance >= QApplication::startDragDistance()) {
00181             QAction *action = watchedMenu->actionAt(mouseEvent->pos());
00182             if (!action) {
00183                 return KMenu::eventFilter(watched, event);
00184             }
00185 
00186             QPersistentModelIndex index = action->data().value<QPersistentModelIndex>();
00187             if (!index.isValid()) {
00188                 return KMenu::eventFilter(watched, event);
00189             }
00190 
00191             QString urlString = index.data(UrlRole).toString();
00192             if (urlString.isNull()) {
00193                 return KMenu::eventFilter(watched, event);
00194             }
00195 
00196             QMimeData *mimeData = new QMimeData();
00197             mimeData->setData("text/uri-list", urlString.toAscii());
00198             mimeData->setText(mimeData->text());
00199             QDrag *drag = new QDrag(this);
00200             drag->setMimeData(mimeData);
00201 
00202             QIcon icon = action->icon();
00203             drag->setPixmap(icon.pixmap(IconSize(KIconLoader::Desktop)));
00204 
00205             d->mousePressPos = QPoint();
00206 
00207             Qt::DropAction dropAction = drag->exec();
00208             Q_UNUSED(dropAction);
00209 
00210             return true;
00211         }
00212     } else if (event->type() == QEvent::MouseButtonPress) {
00213         QMouseEvent *mouseEvent = static_cast<QMouseEvent*>(event);
00214         QMenu *watchedMenu = qobject_cast<QMenu*>(watched);
00215         if (watchedMenu) {
00216             d->mousePressPos = mouseEvent->pos();
00217         }
00218     } else if (event->type() == QEvent::MouseButtonRelease) {
00219         QMenu *watchedMenu = qobject_cast<QMenu*>(watched);
00220         if (watchedMenu) {
00221             d->mousePressPos = QPoint();
00222         }
00223     }
00224 
00225     return KMenu::eventFilter(watched, event);
00226 }
00227 
00228 void MenuView::setModel(QAbstractItemModel *model)
00229 {
00230     if (d->model) {
00231         d->model->disconnect(this);
00232     }
00233     d->model = model;
00234     clear();
00235     if (d->model) {
00236         d->buildBranch(this, QModelIndex());
00237         connect(d->model, SIGNAL(rowsInserted(QModelIndex, int, int)), this, SLOT(rowsInserted(QModelIndex, int, int)));
00238         connect(d->model, SIGNAL(rowsAboutToBeRemoved(QModelIndex, int, int)), this, SLOT(rowsAboutToBeRemoved(QModelIndex, int, int)));
00239         connect(d->model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this, SLOT(dataChanged(QModelIndex, QModelIndex)));
00240         connect(d->model, SIGNAL(modelReset()), this, SLOT(modelReset()));
00241     }
00242 }
00243 
00244 QAbstractItemModel *MenuView::model() const
00245 {
00246     return d->model;
00247 }
00248 
00249 UrlItemLauncher *MenuView::launcher() const
00250 {
00251     return d->launcher;
00252 }
00253 
00254 QModelIndex MenuView::indexForAction(QAction *action) const
00255 {
00256     Q_ASSERT(d->model);
00257     Q_ASSERT(action != 0);
00258     QPersistentModelIndex index = action->data().value<QPersistentModelIndex>();
00259     Q_ASSERT(index.isValid());
00260     return index;
00261 }
00262 
00263 QAction *MenuView::actionForIndex(const QModelIndex& index) const
00264 {
00265     Q_ASSERT(d->model);
00266     if (!index.isValid()) {
00267         return this->menuAction();
00268     }
00269 
00270     QVariant v = d->model->data(index, Private::ActionRole);
00271     Q_ASSERT(v.isValid());
00272     QAction* a = v.value<QAction*>();
00273     Q_ASSERT(a);
00274     return a;
00275 }
00276 
00277 bool MenuView::isValidIndex(const QModelIndex& index) const
00278 {
00279     QVariant v = (d->model && index.isValid()) ? d->model->data(index, Private::ActionRole) : QVariant();
00280     return v.isValid() && v.value<QAction*>();
00281 }
00282 
00283 void MenuView::rowsInserted(const QModelIndex& parent, int start, int end)
00284 {
00285     if (!isValidIndex(parent)) {
00286         // can happen if the models data is incomplete yet
00287         return;
00288     }
00289 
00290     Q_ASSERT(d->model);
00291 
00292     QAction *menuAction = actionForIndex(parent);
00293     Q_ASSERT(menuAction);
00294 
00295     QMenu *menu = menuAction->menu();
00296     Q_ASSERT(menu);
00297 
00298     QList<QAction*> newActions;
00299     for (int row = start; row <= end; row++) {
00300         QModelIndex index = d->model->index(row, d->column, parent);
00301         QAction *newAction = d->createActionForIndex(index, menu);
00302         newActions << newAction;
00303     }
00304 
00305     if (start < menu->actions().count()) {
00306         menu->insertActions(menu->actions()[start], newActions);
00307     } else {
00308         menu->addActions(newActions);
00309     }
00310 }
00311 
00312 void MenuView::rowsAboutToBeRemoved(const QModelIndex& parent, int start, int end)
00313 {
00314     if (!isValidIndex(parent)) {
00315         // can happen if the models data is incomplete yet
00316         return;
00317     }
00318     
00319     Q_ASSERT(d->model);
00320 
00321     QAction *menuAction = actionForIndex(parent);
00322     Q_ASSERT(menuAction);
00323 
00324     QMenu *menu = menuAction->menu();
00325     Q_ASSERT(menu);
00326 
00327     QList<QAction*> actions = menu->actions();
00328     Q_ASSERT(end < actions.count());
00329     for (int row = end; row >= start; row--) {
00330         menu->removeAction(actions[row]);
00331     }
00332 }
00333 
00334 void MenuView::dataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight)
00335 {
00336     if (!isValidIndex(topLeft.parent())) {
00337         // can happen if the models data is incomplete yet
00338         return;
00339     }
00340 
00341     Q_ASSERT(d->model);
00342 
00343     QAction *menuAction = actionForIndex(topLeft.parent());
00344     Q_ASSERT(menuAction);
00345 
00346     QMenu *menu = menuAction->menu();
00347     Q_ASSERT(menu);
00348 
00349     QList<QAction*> actions = menu->actions();
00350     Q_ASSERT(bottomRight.row() < actions.count());
00351 
00352     for (int row = topLeft.row(); row <= bottomRight.row(); row++) {
00353         updateAction(actions[row], d->model->index(row, d->column, topLeft.parent()));
00354     }
00355 }
00356 
00357 void MenuView::modelReset()
00358 {
00359     // force clearance of the menu and rebuild from scratch
00360     setModel(d->model);
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
  •   libsolidcontrol
  •   libtaskmanager
  • Plasma
  •   Animators
  •   Applets
  •   Engines
  • Solid Modules
Generated for API Reference by doxygen 1.5.7
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