[prev in list] [next in list] [prev in thread] [next in thread]
List: kde-commits
Subject: KDE/kdebase/workspace/plasma/applets/kickoff/simpleapplet
From: Sebastian Sauer <mail () dipe ! org>
Date: 2008-08-01 1:15:39
Message-ID: 1217553339.362955.21355.nullmailer () svn ! kde ! org
[Download RAW message or body]
SVN commit 840393 by sebsauer:
* finally found the reasons for crash-bug #167688 and fixed them
* implement functionality to react on changes done to the model
* don't use the position to map the actions with the modelindexes together but use \
QPersistentModelIndex
I plan to backport the whole changeset to 4.1.1 cause the fixes are really needed but \
like to continue testing it before some more days.
M +65 -78 menuview.cpp
M +14 -11 menuview.h
M +4 -7 simpleapplet.cpp
M +0 -1 simpleapplet.h
--- trunk/KDE/kdebase/workspace/plasma/applets/kickoff/simpleapplet/menuview.cpp \
#840392:840393 @@ -25,6 +25,7 @@
#include <QtCore/QStack>
#include <QtGui/QApplication>
#include <QtGui/QMouseEvent>
+#include <QPersistentModelIndex>
// KDE
#include <KDebug>
@@ -35,11 +36,17 @@
#include "core/models.h"
#include "core/itemhandlers.h"
+Q_DECLARE_METATYPE(QPersistentModelIndex)
+Q_DECLARE_METATYPE(QAction*)
+
using namespace Kickoff;
+/// @internal d-pointer class
class MenuView::Private
{
public:
+ enum { ActionRole = Qt::UserRole + 52 };
+
Private(MenuView *parent) : q(parent) , model(0) , column(0), launcher(new \
UrlItemLauncher(parent)), formattype(MenuView::DescriptionName) {}
QAction *createActionForIndex(const QModelIndex& index, QMenu *parent)
@@ -94,7 +101,7 @@
QAction *MenuView::createLeafAction(const QModelIndex&,QObject *parent)
{
- return new QAction(parent);
+ return new QAction(parent);
}
void MenuView::updateAction(QAction *action,const QModelIndex& index)
@@ -123,10 +130,13 @@
case NameDescription: // fall through
case DescriptionName: {
if( ! name.isEmpty() ) { // seems we have a program, but some of \
them don't define a name at all
- if( name.contains(text,Qt::CaseInsensitive) ) {
+ if( text.contains(name,Qt::CaseInsensitive) ) { // sometimes the \
description contains also the name + action->setText(text);
+ }
+ if( name.contains(text,Qt::CaseInsensitive) ) { // and sometimes \
the name also contains the description action->setText(name);
}
- else {
+ else { // seems we have a perfect desktop-file (likely a KDE \
one, heh) and name+description are clear separated if( d->formattype == \
NameDescription ) {
action->setText(QString("%1 %2").arg(name).arg(text));
}
@@ -142,8 +152,15 @@
}
}
- action->setData(index.data(UrlRole));
action->setIcon(index.data(Qt::DecorationRole).value<QIcon>());
+
+ // we map modelindex and action together
+ action->setData(qVariantFromValue(QPersistentModelIndex(index)));
+
+ // don't emit the dataChanged-signal cause else we may end in a infinite loop
+ d->model->blockSignals(true);
+ d->model->setData(index, qVariantFromValue(action), Private::ActionRole);
+ d->model->blockSignals(false);
}
@@ -158,19 +175,22 @@
if (watchedMenu && mouseEvent->buttons() & Qt::LeftButton
&& mousePressDistance >= QApplication::startDragDistance()) {
QAction *action = watchedMenu->actionAt(mouseEvent->pos());
-
if (!action) {
return KMenu::eventFilter(watched, event);
}
- QMimeData *mimeData = new QMimeData();
- QString urlString = action->data().toString();
- mimeData->setData("text/uri-list", urlString.toAscii());
+ QPersistentModelIndex index = \
action->data().value<QPersistentModelIndex>(); + if (!index.isValid()) {
+ return KMenu::eventFilter(watched, event);
+ }
+ QString urlString = index.data(UrlRole).toString();
if (urlString.isNull()) {
return KMenu::eventFilter(watched, event);
}
+ QMimeData *mimeData = new QMimeData();
+ mimeData->setData("text/uri-list", urlString.toAscii());
mimeData->setText(mimeData->text());
QDrag *drag = new QDrag(this);
drag->setMimeData(mimeData);
@@ -181,6 +201,7 @@
d->mousePressPos = QPoint();
Qt::DropAction dropAction = drag->exec();
+ Q_UNUSED(dropAction);
return true;
}
@@ -206,6 +227,10 @@
clear();
if (d->model) {
d->buildBranch(this,QModelIndex());
+ connect(d->model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)), this, \
SLOT(rowsAboutToBeInserted(QModelIndex,int,int))); + connect(d->model, \
SIGNAL(rowsAboutToBeRemoved(QModelIndex,int,int)), this, \
SLOT(rowsAboutToBeRemoved(QModelIndex,int,int))); + connect(d->model, \
SIGNAL(dataChanged(QModelIndex,QModelIndex)), this, \
SLOT(dataChanged(QModelIndex,QModelIndex))); + connect(d->model, \
SIGNAL(modelReset()), this, SLOT(modelReset())); }
}
@@ -223,40 +248,8 @@
{
Q_ASSERT(d->model);
Q_ASSERT(action != 0);
-
- QStack<int> rows;
-
- // find the menu containing the action. for leaf actions this is the
- // action's parent widget. for actions that are sub-menus this is the
- // action's parent widget's parent.
- QWidget *parentWidget = action->parentWidget();
- if (action->menu() && parentWidget != this) {
- parentWidget = parentWidget->parentWidget();
- }
-
- // navigate up the menu hierarchy to find out the position of each
- // action on the path to the specified action
- QMenu *menu = qobject_cast<QMenu*>(parentWidget);
- while (menu) {
- int row = menu->actions().indexOf(action);
- if( row < 0 )
- return QModelIndex();
- rows.push(row);
-
- if (menu == this) {
- break;
- }
- action = menu->menuAction();
- menu = qobject_cast<QMenu*>(menu->parentWidget());
- }
-
- // navigate down the model using the row information from the QMenu traversal
- // to get the index for the specified action
- QModelIndex index;
- while (!rows.isEmpty()) {
- index = d->model->index(rows.pop(),d->column,index);
- }
-
+ QPersistentModelIndex index = action->data().value<QPersistentModelIndex>();
+ Q_ASSERT(index.isValid());
return index;
}
@@ -268,64 +261,53 @@
return this->menuAction();
}
- // navigate up the model to get the rows of each index along the path
- // to the specified index
- QStack<int> rows;
- QModelIndex parent = index.parent();
- while (parent.isValid()) {
- rows.push(parent.row());
- parent = parent.parent();
- }
-
- // navigate down the menu using the row information from the model
- // traversal to find the action for the specified index
- const QMenu *menu = this;
- while (!rows.isEmpty()) {
- if (menu->actions().isEmpty()) {
- // if we reach an empty menu along the way this means that the index
- // is in part of the tree for which the menu hierarchy has not been \
constructed
- // because the user hasn't browsed there yet
- return 0;
- }
-
- menu = menu->actions()[rows.pop()]->menu();
- }
- return menu->actions()[index.row()];
+ QVariant v = d->model->data(index, Private::ActionRole);
+ Q_ASSERT(v.isValid());
+ QAction* a = v.value<QAction*>();
+ Q_ASSERT(a);
+ return a;
}
-void MenuView::rowsInserted(const QModelIndex& parent,int start,int end)
+void MenuView::rowsAboutToBeInserted(const QModelIndex& parent,int start,int end)
{
+ kDebug();
QAction *menuAction = actionForIndex(parent);
if (!menuAction) {
return;
}
- QMenu *menu = menuAction->menu();
+ QMenu *menu = menuAction->menu();
Q_ASSERT(menu);
QList<QAction*> newActions;
for (int row = start; row <= end; row++) {
- QAction *newAction = \
d->createActionForIndex(d->model->index(row,d->column,parent),menu); + \
QModelIndex index = d->model->index(row, d->column, parent); + QAction \
*newAction = d->createActionForIndex(index, menu); newActions << newAction;
}
- Q_ASSERT(menu->actions().count() > start);
- insertActions(menu->actions()[start],newActions);
+ if(start < menu->actions().count()) {
+ menu->insertActions(menu->actions()[start],newActions);
+ } else {
+ menu->addActions(newActions);
+ }
}
-void MenuView::rowsRemoved(const QModelIndex& parent,int start,int end)
+void MenuView::rowsAboutToBeRemoved(const QModelIndex& parent,int start,int end)
{
QAction *menuAction = actionForIndex(parent);
if (!menuAction) {
return;
}
- QMenu *menu = menuAction->menu();
+ QMenu *menu = menuAction->menu();
Q_ASSERT(menu);
QList<QAction*> actions = menu->actions();
- for (int row = start; row <= end; row++) {
- menu->removeAction(actions[row]);
+ Q_ASSERT(end < actions.count());
+ for (int row = end; row >= start; row--) {
+ QAction* a = actions[row];
+ delete a;
}
}
@@ -335,9 +317,13 @@
if (!menuAction) {
return;
}
+
QMenu *menu = menuAction->menu();
+ Q_ASSERT(menu);
QList<QAction*> actions = menu->actions();
+ Q_ASSERT(bottomRight.row() < actions.count());
+
for (int row=topLeft.row(); row <= bottomRight.row(); row++) {
updateAction(actions[row],d->model->index(row,d->column,topLeft.parent()));
}
@@ -345,10 +331,10 @@
void MenuView::modelReset()
{
- // force clearance of the menu
- setModel(0);
- // rebuild the menu from scratch
- setModel(d->model);
+ // force clearance of the menu and rebuild from scratch
+ QAbstractItemModel *m = d->model;
+ setModel(0);
+ setModel(m);
}
void MenuView::fillSubMenu()
@@ -392,6 +378,7 @@
void MenuView::actionTriggered(QAction *action)
{
QModelIndex index = indexForAction(action);
+ Q_ASSERT(index.isValid());
if (index.isValid()) {
d->launcher->openItem(index);
}
--- trunk/KDE/kdebase/workspace/plasma/applets/kickoff/simpleapplet/menuview.h \
#840392:840393 @@ -93,16 +93,18 @@
void setFormatType(FormatType formattype);
protected:
- /**
- * Creates a new action to represent a leaf index in the tree. A leaf index
- * is one which does not have children. The default implementation creates a \
new
- * QAction with no properties set. updateAction() is immediately called on the
- * return action to set its text and icon.
- *
- * @param index The index in the model for which an action should be created
- * @param parent The object which should be set as the parent of the new action
- */
+
+ /**
+ * Creates a new action to represent a leaf index in the tree. A leaf index
+ * is one which does not have children. The default implementation creates a new
+ * QAction with no properties set. updateAction() is immediately called on the
+ * return action to set its text and icon.
+ *
+ * @param index The index in the model for which an action should be created
+ * @param parent The object which should be set as the parent of the new action
+ */
virtual QAction *createLeafAction(const QModelIndex& index,QObject *parent);
+
/**
* Sets the text, icon and other properties of @p action using the data
* associated with @p index in the model(). This is called whenever the data \
for @@ -121,10 +123,11 @@
void actionTriggered(QAction* action);
private Q_SLOTS:
- void rowsInserted(const QModelIndex& parent,int start,int end);
- void rowsRemoved(const QModelIndex& parent,int start,int end);
+ void rowsAboutToBeInserted(const QModelIndex& parent,int start,int end);
+ void rowsAboutToBeRemoved(const QModelIndex& parent,int start,int end);
void dataChanged(const QModelIndex& topLeft,const QModelIndex& bottomRight);
void modelReset();
+
// performs on-demand filling of sub-menus in the tree
void fillSubMenu();
--- trunk/KDE/kdebase/workspace/plasma/applets/kickoff/simpleapplet/simpleapplet.cpp \
#840392:840393 @@ -51,10 +51,11 @@
#include "core/leavemodel.h"
#include "core/urlitemlauncher.h"
+/// @internal d-pointer class
class MenuLauncherApplet::Private
{
public:
- Kickoff::MenuView *menuview;
+ QPointer<Kickoff::MenuView> menuview;
Plasma::Icon *icon;
QPointer<Kickoff::UrlItemLauncher> launcher;
@@ -117,6 +118,7 @@
menuview->addAction(action);
}
}
+ connect(view->model(), SIGNAL(modelReset()), menuview, \
SLOT(deleteLater())); }
QString viewIcon() {
@@ -325,7 +327,7 @@
d->menuview = new Kickoff::MenuView();
connect(d->menuview, SIGNAL(triggered(QAction*)), this, \
SLOT(actionTriggered(QAction*)));
connect(d->menuview, SIGNAL(aboutToHide()), d->icon, SLOT(setUnpressed()));
- connect(d->menuview, SIGNAL(destroyed(QObject*)), this, \
SLOT(menuDestroyed())); + connect(d->menuview, SIGNAL(aboutToHide()), \
d->menuview, SLOT(deleteLater()));
switch( d->viewtype ) {
case Combined: {
@@ -394,11 +396,6 @@
}
}
-void MenuLauncherApplet::menuDestroyed()
-{
- d->menuview = 0;
-}
-
QList<QAction*> MenuLauncherApplet::contextualActions()
{
return d->actions;
--- trunk/KDE/kdebase/workspace/plasma/applets/kickoff/simpleapplet/simpleapplet.h \
#840392:840393 @@ -108,7 +108,6 @@
void toggleMenu(bool pressed);
void toggleMenu();
void actionTriggered(QAction *action);
- void menuDestroyed();
private:
class Private;
[prev in list] [next in list] [prev in thread] [next in thread]
Configure |
About |
News |
Add a list |
Sponsored by KoreLogic