aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRenato Araujo Oliveira Filho <renato.filho@canonical.com>2013-01-03 18:38:23 -0300
committerRenato Araujo Oliveira Filho <renato.filho@canonical.com>2013-01-03 18:38:23 -0300
commit2d050cddb8a4aa3ada8f956e071efed9d53898fd (patch)
tree090bce4be2b8a8b4b56f37d0588c8e505a309c2f
parentc34c5af8a6990f0af95d8dce59991868bfdc62d3 (diff)
downloadqmenumodel-2d050cddb8a4aa3ada8f956e071efed9d53898fd.tar.gz
qmenumodel-2d050cddb8a4aa3ada8f956e071efed9d53898fd.tar.bz2
qmenumodel-2d050cddb8a4aa3ada8f956e071efed9d53898fd.zip
Implmeneted QMenuModel as tree model.
-rw-r--r--libqmenumodel/src/CMakeLists.txt1
-rw-r--r--libqmenumodel/src/menunode.cpp223
-rw-r--r--libqmenumodel/src/menunode.h70
-rw-r--r--libqmenumodel/src/qdbusmenumodel.cpp5
-rw-r--r--libqmenumodel/src/qmenumodel.cpp379
-rw-r--r--libqmenumodel/src/qmenumodel.h64
-rw-r--r--tests/client/CMakeLists.txt8
-rw-r--r--tests/client/treetest.cpp114
-rw-r--r--tests/client/treeviewtest.cpp88
9 files changed, 680 insertions, 272 deletions
diff --git a/libqmenumodel/src/CMakeLists.txt b/libqmenumodel/src/CMakeLists.txt
index fe7d433..1fb89df 100644
--- a/libqmenumodel/src/CMakeLists.txt
+++ b/libqmenumodel/src/CMakeLists.txt
@@ -3,6 +3,7 @@ project(src)
set(QMENUMODEL_SRC
converter.cpp
dbus-enums.h
+ menunode.cpp
qmenumodel.cpp
qdbusobject.cpp
qdbusmenumodel.cpp
diff --git a/libqmenumodel/src/menunode.cpp b/libqmenumodel/src/menunode.cpp
new file mode 100644
index 0000000..bfdd28a
--- /dev/null
+++ b/libqmenumodel/src/menunode.cpp
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Renato Araujo Oliveira Filho <renato@canonical.com>
+ */
+
+#include "menunode.h"
+
+#include <QMetaMethod>
+#include <QDebug>
+
+MenuNode::MenuNode(const QString &linkType, GMenuModel *model, MenuNode *parent, int pos, QObject *listener)
+ : m_model(model),
+ m_parent(parent),
+ m_signalChangedId(0),
+ m_linkType(linkType)
+{
+ g_object_ref(model);
+ if (m_parent) {
+ m_parent->insertChild(this, pos);
+ }
+
+ m_size = g_menu_model_get_n_items(model);
+ for(int i=0; i < m_size; i++) {
+ MenuNode::create(model, i, this, listener);
+ }
+
+ connect(listener);
+}
+
+MenuNode::~MenuNode()
+{
+ disconnect();
+ Q_FOREACH(MenuNode *child, m_children) {
+ delete child;
+ }
+ m_children.clear();
+ if (m_model) {
+ g_object_unref(m_model);
+ }
+}
+
+void MenuNode::connect(QObject *listener)
+{
+ m_listener = listener;
+ if (m_model && (m_signalChangedId == 0)) {
+ m_signalChangedId = g_signal_connect(m_model,
+ "items-changed",
+ G_CALLBACK(MenuNode::onItemsChanged),
+ this);
+ }
+}
+
+void MenuNode::disconnect()
+{
+ if (m_signalChangedId != 0) {
+ g_signal_handler_disconnect(m_model, m_signalChangedId);
+ }
+}
+
+int MenuNode::position() const
+{
+ if (m_parent) {
+ return m_parent->childPosition(this);
+ } else {
+ return 0;
+ }
+}
+
+MenuNode *MenuNode::parent() const
+{
+ return m_parent;
+}
+
+GMenuModel *MenuNode::model() const
+{
+ return m_model;
+}
+
+QString MenuNode::linkType() const
+{
+ return m_linkType;
+}
+
+MenuNode *MenuNode::child(int pos) const
+{
+ if (m_children.contains(pos)) {
+ return m_children.value(pos);
+ }
+ return 0;
+}
+
+int MenuNode::childPosition(GMenuModel *item) const
+{
+ QMap<int, MenuNode*>::const_iterator i = m_children.constBegin();
+ while (i != m_children.constEnd()) {
+ if (i.value()->m_model == item) {
+ return i.key();
+ }
+ ++i;
+ }
+ return 0;
+}
+
+int MenuNode::childPosition(const MenuNode *item) const
+{
+ return childPosition(item->m_model);
+}
+
+int MenuNode::size() const
+{
+ return m_size;
+}
+
+int MenuNode::depth() const
+{
+ int depth = 0;
+ const MenuNode *child = this;
+ while(child->parent()) {
+ depth++;
+ child = child->parent();
+ }
+ return depth;
+}
+
+void MenuNode::change(int start, int added, int removed)
+{
+ if (added > 0) {
+ for (int i=(m_size - 1 + added), iMin=start; i >= iMin; i--) {
+ if (m_children.contains(i)) {
+ m_children.insert(i + added, m_children.take(i));
+ }
+ }
+ }
+
+ if (removed > 0) {
+ int removedEnd = start + removed;
+ for (int i=start, iMax=m_size; i < iMax; i++) {
+ if (i <= removedEnd) {
+ delete m_children.take(i);
+ } else if (m_children.contains(i)) {
+ m_children.insert(i - removed, m_children.take(i));
+ }
+ }
+ }
+}
+
+void MenuNode::insertChild(MenuNode *child, int pos)
+{
+ if (m_children.contains(pos)) {
+ qWarning() << "Section conflic: parent" << this << "child" << child << "pos" << pos;
+ return;
+ }
+
+ child->m_parent = this;
+ m_children.insert(pos, child);
+}
+
+
+MenuNode *MenuNode::find(GMenuModel *item)
+{
+ if (m_model == item) {
+ return this;
+ }
+
+ Q_FOREACH(MenuNode *child, m_children) {
+ MenuNode *found = child->find(item);
+ if (found) {
+ return found;
+ }
+ }
+ return 0;
+}
+
+MenuNode *MenuNode::create(GMenuModel *model, int pos, MenuNode *parent, QObject *listener)
+{
+ QString linkType(G_MENU_LINK_SUBMENU);
+ GMenuModel *link = g_menu_model_get_item_link(model, pos, G_MENU_LINK_SUBMENU);
+ if (link == NULL) {
+ linkType = G_MENU_LINK_SECTION;
+ link = g_menu_model_get_item_link(model, pos, G_MENU_LINK_SECTION);
+ }
+
+ if (link) {
+ return new MenuNode(linkType, link, parent, pos, listener);
+ }
+ return 0;
+}
+
+void MenuNode::onItemsChanged(GMenuModel *model, gint position, gint removed, gint added, gpointer data)
+{
+ MenuNode *self = reinterpret_cast<MenuNode*>(data);
+ if (self->m_listener) {
+ const QMetaObject *mobj = self->m_listener->metaObject();
+ int slotIndex = mobj->indexOfSlot(QMetaObject::normalizedSignature("onItemsChanged(MenuNode*, int, int, int)"));
+ if (slotIndex > -1) {
+ QMetaMethod slot = mobj->method(slotIndex);
+ qDebug() << "invoke" << model << "pos" << position << removed << added;
+ slot.invoke(self->m_listener,
+ Q_ARG(MenuNode*, self),
+ Q_ARG(int, position),
+ Q_ARG(int, removed),
+ Q_ARG(int, added));
+ } else {
+ qWarning() << "Slot 'onItemsChanged(MenuNode*, int, int, int)' not found in" << self->m_listener;
+ }
+ } else {
+ qDebug() << "No listener" << position << removed << added;
+ }
+}
diff --git a/libqmenumodel/src/menunode.h b/libqmenumodel/src/menunode.h
new file mode 100644
index 0000000..d7a893c
--- /dev/null
+++ b/libqmenumodel/src/menunode.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Renato Araujo Oliveira Filho <renato@canonical.com>
+ */
+
+#ifndef MENULI_H
+#define MENUNODE_H
+
+#include <QObject>
+#include <QPointer>
+#include <QMap>
+#include <QVariant>
+
+extern "C" {
+#include <gio/gio.h>
+}
+
+class MenuNode
+{
+public:
+ MenuNode(const QString &linkType, GMenuModel *model, MenuNode *parent, int pos, QObject *listener);
+ ~MenuNode();
+
+ int position() const;
+ MenuNode *parent() const;
+ GMenuModel *model() const;
+ QString linkType() const;
+
+ void connect(QObject *listener);
+ void disconnect();
+ int size() const;
+ MenuNode *child(int pos) const;
+
+ void insertChild(MenuNode *child, int pos);
+ int childPosition(GMenuModel *item) const;
+ int childPosition(const MenuNode *item) const;
+
+ int depth() const;
+ void change(int start, int added, int removed);
+ MenuNode *find(GMenuModel *item);
+
+ static MenuNode *create(GMenuModel *model, int pos, MenuNode *parent=0, QObject *listener=0);
+
+private:
+ GMenuModel *m_model;
+ QMap<int, MenuNode*> m_children;
+ MenuNode* m_parent;
+ int m_size;
+ QObject *m_listener;
+ gulong m_signalChangedId;
+ QString m_linkType;
+
+ static void onItemsChanged(GMenuModel *model, gint position, gint removed, gint added, gpointer data);
+};
+
+#endif
diff --git a/libqmenumodel/src/qdbusmenumodel.cpp b/libqmenumodel/src/qdbusmenumodel.cpp
index bd678fe..284b08b 100644
--- a/libqmenumodel/src/qdbusmenumodel.cpp
+++ b/libqmenumodel/src/qdbusmenumodel.cpp
@@ -48,13 +48,12 @@ extern "C" {
}
\endcode
*/
-
-/*! \internal */
QDBusMenuModel::QDBusMenuModel(QObject *parent)
- :QMenuModel(0, parent)
+ : QMenuModel(0, parent)
{
}
+
/*! \internal */
QDBusMenuModel::~QDBusMenuModel()
{
diff --git a/libqmenumodel/src/qmenumodel.cpp b/libqmenumodel/src/qmenumodel.cpp
index 5127cc2..4f0769c 100644
--- a/libqmenumodel/src/qmenumodel.cpp
+++ b/libqmenumodel/src/qmenumodel.cpp
@@ -23,7 +23,11 @@ extern "C" {
}
#include "qmenumodel.h"
+#include "menunode.h"
#include "converter.h"
+#include <QDebug>
+#include <QCoreApplication>
+#include <QThread>
/*!
\qmltype QMenuModel
@@ -33,71 +37,25 @@ extern "C" {
This is a abstracted class used by \l QDBusMenuModel.
*/
-
/*! \internal */
QMenuModel::QMenuModel(GMenuModel *other, QObject *parent)
- : QAbstractListModel(parent),
- m_menuModel(0),
- m_signalChangedId(0),
- m_rowCount(0),
- m_currentOperationPosition(0),
- m_currentOperationAdded(0),
- m_currentOperationRemoved(0)
+ : QAbstractItemModel(parent),
+ m_root(0)
{
- m_cache = new QHash<int, QMenuModel*>;
setMenuModel(other);
-
- connect(this, SIGNAL(rowsInserted(const QModelIndex &, int, int)), SIGNAL(countChanged()));
- connect(this, SIGNAL(rowsRemoved(const QModelIndex &, int, int)), SIGNAL(countChanged()));
- connect(this, SIGNAL(modelReset()), SIGNAL(countChanged()));
}
/*! \internal */
QMenuModel::~QMenuModel()
{
- clearModel(true);
- delete m_cache;
-}
-
-/*!
- \qmlmethod QDBusMenuModel::get(int)
-
- Returns the item at index in the model. This allows the item data to be accessed from JavaScript:
-
- \b Note: methods should only be called after the Component has completed.
-*/
-
-QVariantMap QMenuModel::get(int row) const
-{
- QVariantMap result;
-
- QModelIndex index = this->index(row);
- if (index.isValid()) {
- QMap<int, QVariant> data = itemData(index);
- const QHash<int, QByteArray> roleNames = this->roleNames();
- Q_FOREACH(int i, roleNames.keys()) {
- result.insert(roleNames[i], data[i]);
- }
- }
- return result;
-}
-
-/*!
- \qmlmethod QDBusMenuModel::count()
-
- The number of data entries in the model.
-
- \b Note: methods should only be called after the Component has completed.
-*/
-int QMenuModel::count() const
-{
- return m_rowCount;
+ clearModel();
}
/*! \internal */
void QMenuModel::setMenuModel(GMenuModel *other)
{
- if (m_menuModel == other) {
+ //qDebug() << "SetModel" << m_root << other;
+ if ((m_root != 0) && (m_root->model() == other)) {
return;
}
@@ -105,46 +63,21 @@ void QMenuModel::setMenuModel(GMenuModel *other)
clearModel();
- m_menuModel = other;
-
- if (m_menuModel) {
- g_object_ref(m_menuModel);
- m_rowCount = g_menu_model_get_n_items(m_menuModel);
- m_signalChangedId = g_signal_connect(m_menuModel,
- "items-changed",
- G_CALLBACK(QMenuModel::onItemsChanged),
- this);
- } else {
- m_rowCount = 0;
+ if (other) {
+ m_root = new MenuNode("", other, 0, 0, this);
+ //qDebug() << "new size:" << m_root << m_root->size();
}
endResetModel();
}
/*! \internal */
-GMenuModel *QMenuModel::menuModel() const
+void QMenuModel::clearModel()
{
- return m_menuModel;
-}
-
-/*! \internal */
-void QMenuModel::clearModel(bool destructor)
-{
- if (m_menuModel) {
- g_signal_handler_disconnect(m_menuModel, m_signalChangedId);
- m_signalChangedId = 0;
- g_object_unref(m_menuModel);
- m_menuModel = NULL;
+ if (m_root) {
+ delete m_root;
+ m_root = NULL;
}
-
- Q_FOREACH(QMenuModel* child, *m_cache) {
- // avoid emit signals during the object destruction this can crash qml
- if (!destructor) {
- child->setMenuModel(NULL);
- }
- child->deleteLater();
- }
- m_cache->clear();
}
/*! \internal */
@@ -154,91 +87,115 @@ QHash<int, QByteArray> QMenuModel::roleNames() const
if (roles.isEmpty()) {
roles[Action] = "action";
roles[Label] = "label";
- roles[LinkSection] = "linkSection";
- roles[LinkSubMenu] = "linkSubMenu";
roles[Extra] = "extra";
+ roles[Depth] = "depth";
+ roles[hasSection] = "hasSection";
+ roles[hasSubMenu] = "hasSubMenu";
}
return roles;
}
/*! \internal */
-QVariant QMenuModel::data(const QModelIndex &index, int role) const
+QModelIndex QMenuModel::index(int row, int column, const QModelIndex &parent) const
{
- QVariant attribute;
-
- // Return a empty variant if the remove operation is in progress
- if ((m_currentOperationRemoved > 0) &&
- (index.row() >= m_currentOperationPosition) &&
- (index.row() < (m_currentOperationPosition + m_currentOperationRemoved))) {
- return attribute;
+ MenuNode *node = nodeFromIndex(parent);
+ //qDebug() << "get index: root" << m_root << "node:" << node << "row" << row << "parent is valid:" << parent.isValid() << "parent row:" << parent.row();
+ if (node == 0) {
+ return QModelIndex();
}
- int rowCountValue = rowCount() + (m_currentOperationAdded - m_currentOperationRemoved);
- int row = rowIndex(index);
-
- if ((row >= 0) && (row < rowCountValue)) {
- if (m_menuModel) {
- switch (role) {
- case Action:
- attribute = getStringAttribute(row, G_MENU_ATTRIBUTE_ACTION);
- break;
- case Label:
- attribute = getStringAttribute(row, G_MENU_ATTRIBUTE_LABEL);
- break;
- case LinkSection:
- attribute = getLink(row, G_MENU_LINK_SECTION);
- break;
- case LinkSubMenu:
- attribute = getLink(row, G_MENU_LINK_SUBMENU);
- break;
- case Extra:
- attribute = getExtraProperties(row);
- break;
- default:
- break;
- }
+ if (parent.isValid()) {
+ MenuNode *child = node->child(parent.row());
+ if (child) {
+ node = child;
}
}
- return attribute;
+ return createIndex(row, column, node);
}
/*! \internal */
QModelIndex QMenuModel::parent(const QModelIndex &index) const
{
+ if (index.isValid() && index.internalPointer()) {
+ MenuNode *node = nodeFromIndex(index);
+ if (node->parent()) {
+ return createIndex(node->position(), 0, node->parent());
+ }
+ }
+
return QModelIndex();
}
/*! \internal */
-int QMenuModel::rowCount(const QModelIndex &) const
+QVariant QMenuModel::data(const QModelIndex &index, int role) const
{
- return m_rowCount;
-}
+ QVariant attribute;
+ if (!index.isValid()) {
+ return attribute;
+ }
-/*! \internal */
-int QMenuModel::rowIndex(const QModelIndex &index) const
-{
+ MenuNode *node = nodeFromIndex(index);
+ int rowCountValue = node->size();
int row = index.row();
- /*
- if ((m_currentOperationAdded > 0) && (row >= m_currentOperationPosition)) {
- row += m_currentOperationAdded;
- } else if ((m_currentOperationRemoved > 0) && (row >= (row >= m_currentOperationPosition))) {
+ if (node && (row >= 0) && (row < rowCountValue)) {
+ switch (role) {
+ case Action:
+ attribute = getStringAttribute(node, row, G_MENU_ATTRIBUTE_ACTION);
+ break;
+ case Qt::DisplayRole:
+ case Label:
+ attribute = getStringAttribute(node, row, G_MENU_ATTRIBUTE_LABEL);
+ break;
+ case Extra:
+ attribute = getExtraProperties(node, row);
+ break;
+ case hasSection:
+ attribute = QVariant(hasLink(node, row, G_MENU_LINK_SECTION));
+ break;
+ case hasSubMenu:
+ attribute = QVariant(hasLink(node, row, G_MENU_LINK_SUBMENU));
+ break;
+ case Depth:
+ attribute = QVariant(node->depth());
+ break;
+ default:
+ break;
+ }
}
- */
- if (row >= m_currentOperationPosition) {
- row += (m_currentOperationAdded - m_currentOperationRemoved);
+ return attribute;
+}
+
+/*! \internal */
+int QMenuModel::rowCount(const QModelIndex &index) const
+{
+ if (index.isValid()) {
+ MenuNode *node = nodeFromIndex(index);
+ if (node) {
+ MenuNode *child = node->child(index.row());
+ if (child) {
+ return child->size();
+ }
+ }
+ return 0;
}
- return row;
+ return m_root->size();
}
+/*! \internal */
+int QMenuModel::columnCount(const QModelIndex &) const
+{
+ return 1;
+}
/*! \internal */
-QVariant QMenuModel::getStringAttribute(int row,
+QVariant QMenuModel::getStringAttribute(MenuNode *node,
+ int row,
const QString &attribute) const
{
QVariant result;
gchar* value = NULL;
- g_menu_model_get_item_attribute(m_menuModel,
+ g_menu_model_get_item_attribute(node->model(),
row,
attribute.toUtf8().data(),
"s", &value);
@@ -250,44 +207,9 @@ QVariant QMenuModel::getStringAttribute(int row,
}
/*! \internal */
-QVariant QMenuModel::getLink(int row,
- const QString &linkName) const
+QVariant QMenuModel::getExtraProperties(MenuNode *node, int row) const
{
- GMenuModel *link = g_menu_model_get_item_link(m_menuModel,
- row,
- linkName.toUtf8().data());
- if (link) {
- QMenuModel* child = 0;
- if (m_cache->contains(row)) {
- child = m_cache->value(row);
- if (child->menuModel() != link) {
- child->setMenuModel(link);
- }
- }
- if (child == 0) {
- child = new QMenuModel(link);
- m_cache->insert(row, child);
- }
- g_object_unref(link);
- return QVariant::fromValue<QObject*>(child);
- }
- return QVariant();
-}
-
-/*! \internal */
-QString QMenuModel::parseExtraPropertyName(const QString &name) const
-{
- QString newName(name);
- if (name.startsWith("x-")) {
- newName = name.mid(2);
- }
- return newName.replace("-", "_");
-}
-
-/*! \internal */
-QVariant QMenuModel::getExtraProperties(int row) const
-{
- GMenuAttributeIter *iter = g_menu_model_iterate_item_attributes(m_menuModel, row);
+ GMenuAttributeIter *iter = g_menu_model_iterate_item_attributes(node->model(), row);
if (iter == NULL) {
return QVariant();
}
@@ -306,72 +228,73 @@ QVariant QMenuModel::getExtraProperties(int row) const
}
/*! \internal */
-QHash<int, QMenuModel*> QMenuModel::cache() const
+void QMenuModel::onItemsChanged(MenuNode *node,
+ int position,
+ int removed,
+ int added)
{
- return *m_cache;
+ qDebug() << "+[onItemsChanged]" << this << "pos" << position << "removed" << removed << "added" << added << "thread" << QThread::currentThread();
+
+ QModelIndex index = indexFromNode(node);
+ if (removed > 0) {
+ beginRemoveRows(index, position, position + removed - 1);
+
+ node->change(position, added, removed);
+
+ endRemoveRows();
+ }
+
+ if (added > 0) {
+ beginInsertRows(index, position, position + added - 1);
+
+ node->change(position, added, removed);
+ for (int i = position; i < (position + added); i++) {
+ MenuNode::create(node->model(), i, node, this);
+ }
+
+ endInsertRows();
+ }
+
+ //qDebug() << "-[onItemsChanged]" << self << "pos" << position << "removed" << removed << "added" << added << "thread" << QThread::currentThread();
}
/*! \internal */
-void QMenuModel::onItemsChanged(GMenuModel *model,
- gint position,
- gint removed,
- gint added,
- gpointer data)
+QModelIndex QMenuModel::indexFromNode(MenuNode *node) const
{
- QMenuModel *self = reinterpret_cast<QMenuModel*>(data);
- QHash<int, QMenuModel*>* cache = self->m_cache;
-
- self->m_currentOperationPosition = position;
- self->m_currentOperationAdded = added;
- self->m_currentOperationRemoved = removed;
+ return createIndex(node->position(), 0, node);
+}
- int prevcount = g_menu_model_get_n_items(model) + removed - added;
- if (removed > 0) {
- // help function to proxy model
- for(int i=position, iMax=(position + removed - 1); i <= iMax; i++) {
- if (cache->contains(i)) {
- Q_EMIT self->aboutToRemoveLink(cache->value(i), i);
- }
- }
+/*! \internal */
+MenuNode *QMenuModel::nodeFromIndex(const QModelIndex &index) const
+{
+ MenuNode *node = 0;
- // We need clear the removed items before start remove it,
- // because we do not have information about the previous data, and QML
- // will try to retrieve it during the signal 'rowsAboutToBeRemoved'
- QModelIndex start = self->index(position);
- QModelIndex end = self->index(position + removed -1);
- Q_EMIT self->dataChanged(start, end);
-
- self->beginRemoveRows(QModelIndex(), position, position + removed - 1);
- self->m_currentOperationPosition = self->m_currentOperationAdded = self->m_currentOperationRemoved = 0;
- self->m_rowCount -= removed;
- // Remove invalidated menus from the cache
- for (int i = position, iMax = position + removed; i < iMax; ++i) {
- if (cache->contains(i)) {
- QMenuModel *cached = cache->take(i);
- cached->setMenuModel(NULL);
- cached->deleteLater();
- }
- }
- // Update the indexes of other cached menus to account for the removals
- for (int i = position + removed; i < prevcount; ++i) {
- if (cache->contains(i)) {
- cache->insert(i - removed, cache->take(i));
- }
- }
- self->endRemoveRows();
+ if (index.isValid()) {
+ node = reinterpret_cast<MenuNode*>(index.internalPointer());
+ } else {
+ node = m_root;
}
- if (added > 0) {
- self->beginInsertRows(QModelIndex(), position, position + added - 1);
- self->m_currentOperationPosition = self->m_currentOperationAdded = self->m_currentOperationRemoved = 0;
- // Update the indexes of cached menus to account for the insertions
- for (int i = prevcount - removed - 1; i >= position; --i) {
- if (cache->contains(i)) {
- cache->insert(i + added, cache->take(i));
- }
- }
- self->m_rowCount += added;
- self->endInsertRows();
+ return node;
+}
+
+/*! \internal */
+QString QMenuModel::parseExtraPropertyName(const QString &name) const
+{
+ QString newName(name);
+ if (name.startsWith("x-")) {
+ newName = name.mid(2);
}
+ return newName.replace("-", "_");
}
+GMenuModel *QMenuModel::menuModel() const
+{
+ return m_root->model();
+}
+
+bool QMenuModel::hasLink(MenuNode *node, int row, const QString &linkType) const
+{
+ MenuNode *child = node->child(row);
+ return (child && (child->linkType() == linkType));
+}
diff --git a/libqmenumodel/src/qmenumodel.h b/libqmenumodel/src/qmenumodel.h
index d83a35c..8887c81 100644
--- a/libqmenumodel/src/qmenumodel.h
+++ b/libqmenumodel/src/qmenumodel.h
@@ -17,73 +17,61 @@
* Renato Araujo Oliveira Filho <renato@canonical.com>
*/
-#ifndef QMENUMODEL_H
-#define QMENUMODEL_H
+#ifndef QMENUTREEMODEL_H
+#define QMENUTREEMODEL_H
-#include <QAbstractListModel>
+#include <QAbstractItemModel>
-typedef int gint;
-typedef unsigned int guint;
-typedef void* gpointer;
+class MenuNode;
typedef struct _GMenuModel GMenuModel;
-typedef struct _GObject GObject;
-class QMenuModel : public QAbstractListModel
+class QMenuModel : public QAbstractItemModel
{
Q_OBJECT
- Q_PROPERTY(int count READ count NOTIFY countChanged)
public:
enum MenuRoles {
- Action = 0,
+ Action = Qt::DisplayRole + 1,
Label,
- LinkSection,
- LinkSubMenu,
- Extra
+ Extra,
+ Depth,
+ hasSection,
+ hasSubMenu
};
~QMenuModel();
- Q_INVOKABLE QVariantMap get(int row) const;
-
/* QAbstractItemModel */
- QHash<int, QByteArray> roleNames() const;
- QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
- QModelIndex parent (const QModelIndex &index) const;
int rowCount(const QModelIndex &parent = QModelIndex()) const;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const;
+ QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const;
+ QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const;
+ QModelIndex parent(const QModelIndex &index) const;
+ QHash<int, QByteArray> roleNames() const;
Q_SIGNALS:
void countChanged();
- void aboutToRemoveLink(QMenuModel *link, int row);
+
+public Q_SLOTS:
+ void onItemsChanged(MenuNode *node, int position, int removed, int added);
protected:
QMenuModel(GMenuModel *other=0, QObject *parent=0);
void setMenuModel(GMenuModel *model);
GMenuModel *menuModel() const;
- // helper getter intended for use in tests only
- QHash<int, QMenuModel*> cache() const;
-
private:
- QHash<int, QMenuModel*>* m_cache;
- GMenuModel *m_menuModel;
- guint m_signalChangedId;
- guint m_rowCount;
+ MenuNode *m_root;
- //control variables
- int m_currentOperationPosition;
- int m_currentOperationAdded;
- int m_currentOperationRemoved;
+ MenuNode* nodeFromIndex(const QModelIndex &index) const;
+ QModelIndex indexFromNode(MenuNode *node) const;
- QVariant getStringAttribute(int row, const QString &attribute) const;
- QVariant getLink(int row, const QString &linkName) const;
- QVariant getExtraProperties(int row) const;
- QString parseExtraPropertyName(const QString &name) const;
- void clearModel(bool destructor=false);
- int count() const;
- int rowIndex(const QModelIndex &index) const;
+ QVariant getStringAttribute(MenuNode *node, int row, const QString &attribute) const;
+ QVariant getExtraProperties(MenuNode *node, int row) const;
+ bool hasLink(MenuNode *node, int row, const QString &linkType) const;
- static void onItemsChanged(GMenuModel *model, gint position, gint removed, gint added, gpointer data);
+ QString parseExtraPropertyName(const QString &name) const;
+ void clearModel();
};
#endif
diff --git a/tests/client/CMakeLists.txt b/tests/client/CMakeLists.txt
index 54faf34..62f7ccb 100644
--- a/tests/client/CMakeLists.txt
+++ b/tests/client/CMakeLists.txt
@@ -27,7 +27,7 @@ endmacro(declare_test testname)
macro(declare_simple_test testname)
add_executable(${testname} ${testname}.cpp)
- qt5_use_modules(${testname} Core Test)
+ qt5_use_modules(${testname} Core Test Widgets)
target_link_libraries(${testname}
qmenumodel
${GLIB_LDFLAGS}
@@ -54,12 +54,14 @@ endif()
declare_test(servicetest)
declare_test(menuchangestest)
-declare_test(modeltest)
+#declare_test(modeltest)
declare_test(actiongrouptest)
declare_test(qmltest)
declare_simple_test(convertertest)
-declare_simple_test(cachetest)
+#declare_simple_test(cachetest)
declare_simple_test(modelsignalstest)
+declare_simple_test(treetest)
+declare_simple_test(treeviewtest)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qmlfiles.h.in
${CMAKE_CURRENT_BINARY_DIR}/qmlfiles.h)
diff --git a/tests/client/treetest.cpp b/tests/client/treetest.cpp
new file mode 100644
index 0000000..3fce236
--- /dev/null
+++ b/tests/client/treetest.cpp
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Renato Araujo Oliveira Filho <renato@canonical.com>
+ * Olivier Tilloy <olivier.tilloy@canonical.com>
+ */
+
+#include "qmenumodel.h"
+
+extern "C" {
+#include <gio/gio.h>
+}
+
+#include <QtTest>
+
+
+class TestModel : public QMenuModel
+{
+ Q_OBJECT
+public:
+ TestModel() : QMenuModel(0)
+ {
+ GMenu *menu5 = g_menu_new();
+ g_menu_append(menu5, "menu6", NULL);
+ g_menu_append(menu5, "menu7", NULL);
+
+ GMenu *menu3 = g_menu_new();
+ g_menu_append(menu3, "menu4", NULL);
+ g_menu_append_section(menu3, "menu5", G_MENU_MODEL(menu5));
+ g_menu_append(menu3, "menu8", NULL);
+
+ GMenu *menu = g_menu_new();
+ g_menu_append(menu, "menu0", NULL);
+ g_menu_append(menu, "menu1", NULL);
+ g_menu_append(menu, "menu2", NULL);
+ g_menu_append_section(menu, "menu3", G_MENU_MODEL(menu3));
+
+ setMenuModel(G_MENU_MODEL(menu));
+
+ m_menus << menu << menu3 << menu5;
+ }
+
+private:
+ QList<GMenu*> m_menus;
+};
+
+class TreeTest : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void initTestCase()
+ {
+ g_type_init();
+ }
+
+ void testMenuBuild()
+ {
+ TestModel menu;
+ QCOMPARE(menu.rowCount(), 4);
+ QModelIndex row0 = menu.index(0);
+ QVERIFY(row0.isValid());
+ QCOMPARE(menu.rowCount(row0), 0);
+
+ QModelIndex row3 = menu.index(3);
+ QVERIFY(row3.isValid());
+ QCOMPARE(menu.rowCount(row3), 3);
+ QCOMPARE(menu.data(row3, QMenuModel::Label).toString(), QString("menu3"));
+
+ QModelIndex row4 = row3.child(0, 0);
+ QVERIFY(row4.isValid());
+ QCOMPARE(menu.rowCount(row4), 0);
+ QCOMPARE(menu.data(row4, QMenuModel::Label).toString(), QString("menu4"));
+
+ QModelIndex row5 = row3.child(1, 0);
+ QVERIFY(row5.isValid());
+ QCOMPARE(menu.rowCount(row5), 2);
+ QCOMPARE(menu.data(row5, QMenuModel::Label).toString(), QString("menu5"));
+
+ QModelIndex row6 = row5.child(0, 0);
+ QVERIFY(row6.isValid());
+ QCOMPARE(menu.rowCount(row6), 0);
+ QCOMPARE(menu.data(row6, QMenuModel::Label).toString(), QString("menu6"));
+
+ QModelIndex row7 = row5.child(1, 0);
+ QVERIFY(row7.isValid());
+ QCOMPARE(menu.rowCount(row7), 0);
+ QCOMPARE(menu.data(row7, QMenuModel::Label).toString(), QString("menu7"));
+
+ QModelIndex parent_6 = menu.parent(row6);
+ QVERIFY(parent_6.isValid());
+ QCOMPARE(menu.rowCount(parent_6), 2);
+ QCOMPARE(menu.data(parent_6, QMenuModel::Label).toString(), QString("menu5"));
+ }
+
+};
+
+QTEST_MAIN(TreeTest)
+
+#include "treetest.moc"
+
diff --git a/tests/client/treeviewtest.cpp b/tests/client/treeviewtest.cpp
new file mode 100644
index 0000000..527ebd3
--- /dev/null
+++ b/tests/client/treeviewtest.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2012 Canonical Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Authors:
+ * Renato Araujo Oliveira Filho <renato@canonical.com>
+ * Olivier Tilloy <olivier.tilloy@canonical.com>
+ */
+
+#include "qmenumodel.h"
+
+extern "C" {
+#include <gio/gio.h>
+}
+
+#include <QtTest>
+#include <QTreeView>
+#include <QFileSystemModel>
+
+
+class TestModel : public QMenuModel
+{
+ Q_OBJECT
+public:
+ TestModel() : QMenuModel(0)
+ {
+ GMenu *menu5 = g_menu_new();
+ g_menu_append(menu5, "menu6", NULL);
+ g_menu_append(menu5, "menu7", NULL);
+
+ GMenu *menu3 = g_menu_new();
+ g_menu_append(menu3, "menu4", NULL);
+ g_menu_append_section(menu3, "menu5", G_MENU_MODEL(menu5));
+ g_menu_append(menu3, "menu8", NULL);
+
+ GMenu *menu = g_menu_new();
+ g_menu_append(menu, "menu0", NULL);
+ g_menu_append(menu, "menu1", NULL);
+ g_menu_append(menu, "menu2", NULL);
+ g_menu_append_section(menu, "menu3", G_MENU_MODEL(menu3));
+
+ setMenuModel(G_MENU_MODEL(menu));
+
+ m_menus << menu << menu3 << menu5;
+ }
+
+private:
+ QList<GMenu*> m_menus;
+};
+
+class TreeViewTest : public QObject
+{
+ Q_OBJECT
+
+private Q_SLOTS:
+ void initTestCase()
+ {
+ g_type_init();
+ }
+
+ void testView()
+ {
+ TestModel menu;
+ //QFileSystemModel menu; // = new QFileSystemModel;
+ //menu.setRootPath(QDir::currentPath());
+ QTreeView *tree = new QTreeView;
+ tree->setModel(&menu);
+ tree->show();
+ QApplication::exec();
+ }
+
+};
+
+QTEST_MAIN(TreeViewTest)
+
+#include "treeviewtest.moc"
+