00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "menuview.h"
00023
00024
00025 #include <QtCore/QAbstractItemModel>
00026 #include <QtCore/QStack>
00027 #include <QtGui/QApplication>
00028 #include <QtGui/QMouseEvent>
00029 #include <QPersistentModelIndex>
00030
00031
00032 #include <KDebug>
00033 #include <KUrl>
00034 #include <KIconLoader>
00035
00036
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("&","&&");
00113 QString name = index.data(Kickoff::SubTitleRole).value<QString>().replace("&","&&");
00114 if( action->menu()!=0 ) {
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:
00133 case DescriptionName: {
00134 if (!name.isEmpty()) {
00135 if (text.contains(name,Qt::CaseInsensitive)) {
00136 action->setText(text);
00137 } else if (name.contains(text,Qt::CaseInsensitive)) {
00138 action->setText(name);
00139 } else {
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 {
00148 action->setText(text);
00149 }
00150 } break;
00151 }
00152 }
00153
00154 action->setIcon(index.data(Qt::DecorationRole).value<QIcon>());
00155
00156
00157 action->setData(qVariantFromValue(QPersistentModelIndex(index)));
00158
00159
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
00237 disconnect(this, SIGNAL(aboutToShow()), this, SLOT(aboutToShow()));
00238
00239
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
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"