Make a branch to make krunner Good Enough For Aaron™.
[kdebase/uwolfer.git] / workspace / plasma / applets / kickoff / simpleapplet / menuview.cpp
blobfd400c3223c58fd0b0b026f2f5316cc6c5a10cd9
1 /*
2 Copyright 2007 Robert Knight <robertknight@gmail.com>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 // Own
21 #include "menuview.h"
23 // Qt
24 #include <QtCore/QAbstractItemModel>
25 #include <QtCore/QStack>
27 // KDE
28 #include <KUrl>
30 // Local
31 #include "core/models.h"
32 #include "core/itemhandlers.h"
34 using namespace Kickoff;
36 class MenuView::Private
38 public:
39 Private(MenuView *parent) : q(parent) , model(0) , column(0), launcher(new UrlItemLauncher(parent)), formattype(MenuView::DescriptionName) {}
41 QAction *createActionForIndex(const QModelIndex& index,QWidget *parent)
43 Q_ASSERT(index.isValid());
45 QAction *action = 0;
47 if (model->hasChildren(index)) {
48 QMenu *childMenu = new QMenu(parent);
49 QObject::connect(childMenu,SIGNAL(aboutToShow()),q,SLOT(fillSubMenu()));
50 action = childMenu->menuAction();
51 } else {
52 action = q->createLeafAction(index,parent);
55 q->updateAction(action,index);
57 return action;
60 void buildBranch(QMenu *menu,const QModelIndex& parent)
62 int rowCount = model->rowCount(parent);
63 for (int i=0;i<rowCount;i++) {
64 QAction *action = createActionForIndex(model->index(i,column,parent),menu);
65 menu->addAction(action);
69 MenuView * const q;
70 QAbstractItemModel *model;
71 int column;
72 UrlItemLauncher *launcher;
73 MenuView::FormatType formattype;
76 MenuView::MenuView(QWidget *parent)
77 : KMenu(parent)
78 , d(new Private(this))
82 MenuView::~MenuView()
84 delete d;
87 QAction *MenuView::createLeafAction(const QModelIndex&,QObject *parent)
89 return new QAction(parent);
92 void MenuView::updateAction(QAction *action,const QModelIndex& index)
94 QString text = index.data(Qt::DisplayRole).value<QString>(); // describing text, e.g. "Spreadsheet" or "Rekall" (right, sometimes the text is also used for the generic app-name)
95 QString name = index.data(Kickoff::SubTitleRole).value<QString>(); // the generic name, e.g. "kspread" or "OpenOffice.org Spreadsheet" or just "" (right, it's a mess too)
96 if( action->menu()!=0 ) { // if its an item with sub-menuitems, we probably like to thread them another way...
97 action->setText(text.replace("&","&&"));
99 else {
100 switch( d->formattype ) {
101 case Name: {
102 if( name.isEmpty() ) {
103 action->setText(text.replace("&","&&"));
105 else {
106 action->setText(name.replace("&","&&"));
108 } break;
109 case Description: {
110 if( name.contains(text,Qt::CaseInsensitive) ) {
111 text = name;
113 action->setText(text.replace("&","&&"));
114 } break;
115 case NameDescription: // fall through
116 case DescriptionName: {
117 if( ! name.isEmpty() ) { // seems we have a program, but some of them dont define a name at all
118 if( name.contains(text,Qt::CaseInsensitive) ) {
119 action->setText(name.replace("&","&&"));
121 else {
122 if( d->formattype == NameDescription ) {
123 action->setText(QString("%1 %2").arg(name).arg(text).replace("&","&&"));
125 else {
126 action->setText(QString("%1 (%2)").arg(text).arg(name).replace("&","&&"));
130 else { // if there is no name, let's just use the describing text
131 action->setText(text.replace("&","&&"));
133 } break;
136 action->setIcon(index.data(Qt::DecorationRole).value<QIcon>());
139 void MenuView::setModel(QAbstractItemModel *model)
141 d->model = model;
142 clear();
143 if (d->model) {
144 d->buildBranch(this,QModelIndex());
148 QAbstractItemModel *MenuView::model() const
150 return d->model;
153 UrlItemLauncher *MenuView::launcher() const
155 return d->launcher;
158 QModelIndex MenuView::indexForAction(QAction *action) const
160 Q_ASSERT(d->model);
161 Q_ASSERT(action != 0);
163 QStack<int> rows;
165 // find the menu containing the action. for leaf actions this is the
166 // action's parent widget. for actions that are sub-menus this is the
167 // action's parent widget's parent.
168 QWidget *parentWidget = action->parentWidget();
169 if (action->menu() != 0) {
170 parentWidget = parentWidget->parentWidget();
173 // navigate up the menu hierarchy to find out the position of each
174 // action on the path to the specified action
175 QMenu *menu = qobject_cast<QMenu*>(parentWidget);
176 while (menu) {
177 int row = menu->actions().indexOf(action);
178 if( row < 0 )
179 return QModelIndex();
180 rows.push(row);
182 if (menu == this) {
183 break;
185 action = menu->menuAction();
186 menu = qobject_cast<QMenu*>(menu->parentWidget());
189 // navigate down the model using the row information from the QMenu traversal
190 // to get the index for the specified action
191 QModelIndex index;
192 while (!rows.isEmpty()) {
193 index = d->model->index(rows.pop(),d->column,index);
196 return index;
199 QAction *MenuView::actionForIndex(const QModelIndex& index) const
201 Q_ASSERT(d->model);
203 if (!index.isValid()) {
204 return this->menuAction();
207 // navigate up the model to get the rows of each index along the path
208 // to the specified index
209 QStack<int> rows;
210 QModelIndex parent = index.parent();
211 while (parent.isValid()) {
212 rows.push(parent.row());
213 parent = parent.parent();
216 // navigate down the menu using the row information from the model
217 // traversal to find the action for the specified index
218 const QMenu *menu = this;
219 while (!rows.isEmpty()) {
220 if (menu->actions().isEmpty()) {
221 // if we reach an empty menu along the way this means that the index
222 // is in part of the tree for which the menu hierarchy has not been constructed
223 // because the user hasn't browsed there yet
224 return 0;
227 menu = menu->actions()[rows.pop()]->menu();
229 return menu->actions()[index.row()];
232 void MenuView::rowsInserted(const QModelIndex& parent,int start,int end)
234 QAction *menuAction = actionForIndex(parent);
235 if (!menuAction) {
236 return;
238 QMenu *menu = menuAction->menu();
240 Q_ASSERT(menu);
242 QList<QAction*> newActions;
243 for (int row = start; row <= end; row++) {
244 QAction *newAction = d->createActionForIndex(d->model->index(row,d->column,parent),menu);
245 newActions << newAction;
248 Q_ASSERT(menu->actions().count() > start);
249 insertActions(menu->actions()[start],newActions);
252 void MenuView::rowsRemoved(const QModelIndex& parent,int start,int end)
254 QAction *menuAction = actionForIndex(parent);
255 if (!menuAction) {
256 return;
258 QMenu *menu = menuAction->menu();
260 Q_ASSERT(menu);
262 QList<QAction*> actions = menu->actions();
263 for (int row = start; row <= end; row++) {
264 menu->removeAction(actions[row]);
268 void MenuView::dataChanged(const QModelIndex& topLeft,const QModelIndex& bottomRight)
270 QAction *menuAction = actionForIndex(topLeft.parent());
271 if (!menuAction) {
272 return;
274 QMenu *menu = menuAction->menu();
276 QList<QAction*> actions = menu->actions();
277 for (int row=topLeft.row(); row <= bottomRight.row(); row++) {
278 updateAction(actions[row],d->model->index(row,d->column,topLeft.parent()));
282 void MenuView::modelReset()
284 // force clearance of the menu
285 setModel(0);
286 // rebuild the menu from scratch
287 setModel(d->model);
290 void MenuView::fillSubMenu()
292 QMenu *subMenu = qobject_cast<QMenu*>(sender());
293 Q_ASSERT(subMenu);
294 Q_ASSERT(subMenu->isEmpty());
296 QModelIndex menuIndex = indexForAction(subMenu->menuAction());
297 Q_ASSERT(menuIndex.isValid());
299 if (d->model->canFetchMore(menuIndex)) {
300 d->model->fetchMore(menuIndex);
302 d->buildBranch(subMenu,menuIndex);
304 disconnect(sender(),0,this,SLOT(fillSubMenu()));
307 void MenuView::setColumn(int column)
309 d->column = column;
310 modelReset();
313 int MenuView::column() const
315 return d->column;
318 MenuView::FormatType MenuView::formatType() const
320 return d->formattype;
323 void MenuView::setFormatType(MenuView::FormatType formattype)
325 d->formattype = formattype;
328 void MenuView::actionTriggered(QAction *action)
330 QModelIndex index = indexForAction(action);
331 if (index.isValid())
332 d->launcher->openItem(index);
335 #include "menuview.moc"