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.
24 #include <QtCore/QAbstractItemModel>
25 #include <QtCore/QStack>
31 #include "core/models.h"
32 #include "core/itemhandlers.h"
34 using namespace Kickoff
;
36 class MenuView::Private
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());
47 if (model
->hasChildren(index
)) {
48 QMenu
*childMenu
= new QMenu(parent
);
49 QObject::connect(childMenu
,SIGNAL(aboutToShow()),q
,SLOT(fillSubMenu()));
50 action
= childMenu
->menuAction();
52 action
= q
->createLeafAction(index
,parent
);
55 q
->updateAction(action
,index
);
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
);
70 QAbstractItemModel
*model
;
72 UrlItemLauncher
*launcher
;
73 MenuView::FormatType formattype
;
76 MenuView::MenuView(QWidget
*parent
)
78 , d(new Private(this))
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("&","&&"));
100 switch( d
->formattype
) {
102 if( name
.isEmpty() ) {
103 action
->setText(text
.replace("&","&&"));
106 action
->setText(name
.replace("&","&&"));
110 if( name
.contains(text
,Qt::CaseInsensitive
) ) {
113 action
->setText(text
.replace("&","&&"));
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("&","&&"));
122 if( d
->formattype
== NameDescription
) {
123 action
->setText(QString("%1 %2").arg(name
).arg(text
).replace("&","&&"));
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("&","&&"));
136 action
->setIcon(index
.data(Qt::DecorationRole
).value
<QIcon
>());
139 void MenuView::setModel(QAbstractItemModel
*model
)
144 d
->buildBranch(this,QModelIndex());
148 QAbstractItemModel
*MenuView::model() const
153 UrlItemLauncher
*MenuView::launcher() const
158 QModelIndex
MenuView::indexForAction(QAction
*action
) const
161 Q_ASSERT(action
!= 0);
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
);
177 int row
= menu
->actions().indexOf(action
);
179 return QModelIndex();
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
192 while (!rows
.isEmpty()) {
193 index
= d
->model
->index(rows
.pop(),d
->column
,index
);
199 QAction
*MenuView::actionForIndex(const QModelIndex
& index
) const
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
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
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
);
238 QMenu
*menu
= menuAction
->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
);
258 QMenu
*menu
= menuAction
->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());
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
286 // rebuild the menu from scratch
290 void MenuView::fillSubMenu()
292 QMenu
*subMenu
= qobject_cast
<QMenu
*>(sender());
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
)
313 int MenuView::column() const
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
);
332 d
->launcher
->openItem(index
);
335 #include "menuview.moc"