From 2d050cddb8a4aa3ada8f956e071efed9d53898fd Mon Sep 17 00:00:00 2001 From: Renato Araujo Oliveira Filho Date: Thu, 3 Jan 2013 18:38:23 -0300 Subject: Implmeneted QMenuModel as tree model. --- libqmenumodel/src/CMakeLists.txt | 1 + libqmenumodel/src/menunode.cpp | 223 +++++++++++++++++++++ libqmenumodel/src/menunode.h | 70 +++++++ libqmenumodel/src/qdbusmenumodel.cpp | 5 +- libqmenumodel/src/qmenumodel.cpp | 379 ++++++++++++++--------------------- libqmenumodel/src/qmenumodel.h | 64 +++--- tests/client/CMakeLists.txt | 8 +- tests/client/treetest.cpp | 114 +++++++++++ tests/client/treeviewtest.cpp | 88 ++++++++ 9 files changed, 680 insertions(+), 272 deletions(-) create mode 100644 libqmenumodel/src/menunode.cpp create mode 100644 libqmenumodel/src/menunode.h create mode 100644 tests/client/treetest.cpp create mode 100644 tests/client/treeviewtest.cpp 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 . + * + * Authors: + * Renato Araujo Oliveira Filho + */ + +#include "menunode.h" + +#include +#include + +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::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(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 . + * + * Authors: + * Renato Araujo Oliveira Filho + */ + +#ifndef MENULI_H +#define MENUNODE_H + +#include +#include +#include +#include + +extern "C" { +#include +} + +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 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 +#include +#include /*! \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; 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 data = itemData(index); - const QHash 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 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(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 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(data); - QHash* 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(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 */ -#ifndef QMENUMODEL_H -#define QMENUMODEL_H +#ifndef QMENUTREEMODEL_H +#define QMENUTREEMODEL_H -#include +#include -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 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 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 cache() const; - private: - QHash* 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 . + * + * Authors: + * Renato Araujo Oliveira Filho + * Olivier Tilloy + */ + +#include "qmenumodel.h" + +extern "C" { +#include +} + +#include + + +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 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 . + * + * Authors: + * Renato Araujo Oliveira Filho + * Olivier Tilloy + */ + +#include "qmenumodel.h" + +extern "C" { +#include +} + +#include +#include +#include + + +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 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" + -- cgit v1.2.3