aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libqmenumodel/src/qmenumodel.cpp108
-rw-r--r--libqmenumodel/src/qmenumodel.h14
-rw-r--r--tests/client/CMakeLists.txt1
-rw-r--r--tests/client/modelsignalstest.cpp231
-rw-r--r--tests/client/modeltest.cpp10
5 files changed, 322 insertions, 42 deletions
diff --git a/libqmenumodel/src/qmenumodel.cpp b/libqmenumodel/src/qmenumodel.cpp
index 57c61d0..5127cc2 100644
--- a/libqmenumodel/src/qmenumodel.cpp
+++ b/libqmenumodel/src/qmenumodel.cpp
@@ -38,7 +38,11 @@ extern "C" {
QMenuModel::QMenuModel(GMenuModel *other, QObject *parent)
: QAbstractListModel(parent),
m_menuModel(0),
- m_signalChangedId(0)
+ m_signalChangedId(0),
+ m_rowCount(0),
+ m_currentOperationPosition(0),
+ m_currentOperationAdded(0),
+ m_currentOperationRemoved(0)
{
m_cache = new QHash<int, QMenuModel*>;
setMenuModel(other);
@@ -87,7 +91,7 @@ QVariantMap QMenuModel::get(int row) const
*/
int QMenuModel::count() const
{
- return rowCount();
+ return m_rowCount;
}
/*! \internal */
@@ -105,12 +109,13 @@ void QMenuModel::setMenuModel(GMenuModel *other)
if (m_menuModel) {
g_object_ref(m_menuModel);
- // this will trigger the menu load
- (void) g_menu_model_get_n_items(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;
}
endResetModel();
@@ -160,25 +165,34 @@ QHash<int, QByteArray> QMenuModel::roleNames() const
QVariant QMenuModel::data(const QModelIndex &index, int role) const
{
QVariant attribute;
- int rowCountValue = rowCount();
- if ((rowCountValue > 0) && (index.row() >= 0) && (index.row() < rowCountValue)) {
+ // 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;
+ }
+
+ 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(index, G_MENU_ATTRIBUTE_ACTION);
+ attribute = getStringAttribute(row, G_MENU_ATTRIBUTE_ACTION);
break;
case Label:
- attribute = getStringAttribute(index, G_MENU_ATTRIBUTE_LABEL);
+ attribute = getStringAttribute(row, G_MENU_ATTRIBUTE_LABEL);
break;
case LinkSection:
- attribute = getLink(index, G_MENU_LINK_SECTION);
+ attribute = getLink(row, G_MENU_LINK_SECTION);
break;
case LinkSubMenu:
- attribute = getLink(index, G_MENU_LINK_SUBMENU);
+ attribute = getLink(row, G_MENU_LINK_SUBMENU);
break;
case Extra:
- attribute = getExtraProperties(index);
+ attribute = getExtraProperties(row);
break;
default:
break;
@@ -197,20 +211,35 @@ QModelIndex QMenuModel::parent(const QModelIndex &index) const
/*! \internal */
int QMenuModel::rowCount(const QModelIndex &) const
{
- if (m_menuModel) {
- return g_menu_model_get_n_items(m_menuModel);
+ return m_rowCount;
+}
+
+/*! \internal */
+int QMenuModel::rowIndex(const QModelIndex &index) const
+{
+ int row = index.row();
+ /*
+ if ((m_currentOperationAdded > 0) && (row >= m_currentOperationPosition)) {
+ row += m_currentOperationAdded;
+ } else if ((m_currentOperationRemoved > 0) && (row >= (row >= m_currentOperationPosition))) {
+
}
- return 0;
+ */
+ if (row >= m_currentOperationPosition) {
+ row += (m_currentOperationAdded - m_currentOperationRemoved);
+ }
+ return row;
}
+
/*! \internal */
-QVariant QMenuModel::getStringAttribute(const QModelIndex &index,
+QVariant QMenuModel::getStringAttribute(int row,
const QString &attribute) const
{
QVariant result;
gchar* value = NULL;
g_menu_model_get_item_attribute(m_menuModel,
- index.row(),
+ row,
attribute.toUtf8().data(),
"s", &value);
if (value) {
@@ -221,24 +250,23 @@ QVariant QMenuModel::getStringAttribute(const QModelIndex &index,
}
/*! \internal */
-QVariant QMenuModel::getLink(const QModelIndex &index,
+QVariant QMenuModel::getLink(int row,
const QString &linkName) const
{
GMenuModel *link = g_menu_model_get_item_link(m_menuModel,
- index.row(),
+ row,
linkName.toUtf8().data());
if (link) {
QMenuModel* child = 0;
- int key = index.row();
- if (m_cache->contains(key)) {
- QMenuModel* cached = m_cache->value(key);
- if (cached->menuModel() == link) {
- child = cached;
+ 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(key, child);
+ m_cache->insert(row, child);
}
g_object_unref(link);
return QVariant::fromValue<QObject*>(child);
@@ -257,9 +285,9 @@ QString QMenuModel::parseExtraPropertyName(const QString &name) const
}
/*! \internal */
-QVariant QMenuModel::getExtraProperties(const QModelIndex &index) const
+QVariant QMenuModel::getExtraProperties(int row) const
{
- GMenuAttributeIter *iter = g_menu_model_iterate_item_attributes(m_menuModel, index.row());
+ GMenuAttributeIter *iter = g_menu_model_iterate_item_attributes(m_menuModel, row);
if (iter == NULL) {
return QVariant();
}
@@ -293,15 +321,35 @@ void QMenuModel::onItemsChanged(GMenuModel *model,
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;
+
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);
+ }
+ }
+
+ // 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 *model = cache->take(i);
- model->setMenuModel(NULL);
- model->deleteLater();
+ QMenuModel *cached = cache->take(i);
+ cached->setMenuModel(NULL);
+ cached->deleteLater();
}
}
// Update the indexes of other cached menus to account for the removals
@@ -315,12 +363,14 @@ void QMenuModel::onItemsChanged(GMenuModel *model,
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();
}
}
diff --git a/libqmenumodel/src/qmenumodel.h b/libqmenumodel/src/qmenumodel.h
index f186b5f..d83a35c 100644
--- a/libqmenumodel/src/qmenumodel.h
+++ b/libqmenumodel/src/qmenumodel.h
@@ -54,6 +54,7 @@ public:
Q_SIGNALS:
void countChanged();
+ void aboutToRemoveLink(QMenuModel *link, int row);
protected:
QMenuModel(GMenuModel *other=0, QObject *parent=0);
@@ -67,13 +68,20 @@ private:
QHash<int, QMenuModel*>* m_cache;
GMenuModel *m_menuModel;
guint m_signalChangedId;
+ guint m_rowCount;
- QVariant getStringAttribute(const QModelIndex &index, const QString &attribute) const;
- QVariant getLink(const QModelIndex &index, const QString &linkName) const;
- QVariant getExtraProperties(const QModelIndex &index) const;
+ //control variables
+ int m_currentOperationPosition;
+ int m_currentOperationAdded;
+ int m_currentOperationRemoved;
+
+ 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;
static void onItemsChanged(GMenuModel *model, gint position, gint removed, gint added, gpointer data);
};
diff --git a/tests/client/CMakeLists.txt b/tests/client/CMakeLists.txt
index 0fcac9e..54faf34 100644
--- a/tests/client/CMakeLists.txt
+++ b/tests/client/CMakeLists.txt
@@ -59,6 +59,7 @@ declare_test(actiongrouptest)
declare_test(qmltest)
declare_simple_test(convertertest)
declare_simple_test(cachetest)
+declare_simple_test(modelsignalstest)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qmlfiles.h.in
${CMAKE_CURRENT_BINARY_DIR}/qmlfiles.h)
diff --git a/tests/client/modelsignalstest.cpp b/tests/client/modelsignalstest.cpp
new file mode 100644
index 0000000..40a76c6
--- /dev/null
+++ b/tests/client/modelsignalstest.cpp
@@ -0,0 +1,231 @@
+/*
+ * 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 "qmenumodel.h"
+
+#include <QObject>
+#include <QtTest>
+#include <QDebug>
+
+extern "C" {
+#include <gio/gio.h>
+}
+
+
+class MenuModelTestClass : public QMenuModel
+{
+ Q_OBJECT
+public:
+ MenuModelTestClass()
+ : QMenuModel(G_MENU_MODEL(g_menu_new())), m_step(0)
+ {
+ }
+
+ void loadModel()
+ {
+ GMenu *root = G_MENU(menuModel());
+
+ // STEP 0
+ m_step = 0;
+ GMenu *section = g_menu_new();
+ g_menu_append(section, "msg1", NULL);
+ g_menu_append_section(root, "section1", G_MENU_MODEL(section));
+
+ // STEP 1
+ m_step++;
+ GMenu *section2 = g_menu_new();
+ g_menu_append_section(root, "section2", G_MENU_MODEL(section2));
+
+ // STEP 2
+ m_step++;
+ g_menu_append(root, "item1", NULL);
+
+ // STEP 3
+ m_step++;
+ g_menu_insert(root, 1, "item2", NULL);
+ }
+
+ void clear()
+ {
+ GMenu *root = G_MENU(menuModel());
+
+ // STEP 0
+ m_step = 0;
+ g_menu_remove(root, 0);
+
+ // STEP 1
+ m_step++;
+ g_menu_remove(root, 2);
+
+ // STEP 2
+ m_step++;
+ g_menu_remove(root, 0);
+
+ // STEP 3
+ m_step++;
+ g_menu_remove(root, 0);
+ }
+
+public Q_SLOTS:
+ void checkModelStateBeforeInsert(const QModelIndex &parent, int start, int end)
+ {
+ if (m_step == 0) {
+ QCOMPARE(rowCount(), 0);
+ QVERIFY(data(index(1), QMenuModel::Label).isNull());
+ } else if (m_step == 1) {
+ QCOMPARE(rowCount(), 1);
+ QCOMPARE(data(index(0), QMenuModel::Label).toString(), QString("section1"));
+ QVERIFY(data(index(1), QMenuModel::Label).isNull());
+ } else if (m_step == 2) {
+ QCOMPARE(rowCount(), 2);
+ QCOMPARE(data(index(0), QMenuModel::Label).toString(), QString("section1"));
+ QCOMPARE(data(index(1), QMenuModel::Label).toString(), QString("section2"));
+ QVERIFY(data(index(2), QMenuModel::Label).isNull());
+ } else if (m_step == 3) {
+ QCOMPARE(rowCount(), 3);
+ QCOMPARE(data(index(0), QMenuModel::Label).toString(), QString("section1"));
+ QCOMPARE(data(index(1), QMenuModel::Label).toString(), QString("section2"));
+ QCOMPARE(data(index(2), QMenuModel::Label).toString(), QString("item1"));
+ QVERIFY(data(index(3), QMenuModel::Label).isNull());
+ }
+ }
+
+ void checkModelStateAfterInsert(const QModelIndex &parent, int start, int end)
+ {
+ if (m_step == 0) {
+ QCOMPARE(rowCount(), 1);
+ QCOMPARE(data(index(0), QMenuModel::Label).toString(), QString("section1"));
+ QVERIFY(data(index(1), QMenuModel::Label).isNull());
+ } else if (m_step == 1) {
+ QCOMPARE(rowCount(), 2);
+ QCOMPARE(data(index(0), QMenuModel::Label).toString(), QString("section1"));
+ QCOMPARE(data(index(1), QMenuModel::Label).toString(), QString("section2"));
+ QVERIFY(data(index(2), QMenuModel::Label).isNull());
+ } else if (m_step == 2) {
+ QCOMPARE(rowCount(), 3);
+ QCOMPARE(data(index(0), QMenuModel::Label).toString(), QString("section1"));
+ QCOMPARE(data(index(1), QMenuModel::Label).toString(), QString("section2"));
+ QCOMPARE(data(index(2), QMenuModel::Label).toString(), QString("item1"));
+ QVERIFY(data(index(3), QMenuModel::Label).isNull());
+ } else if (m_step == 3) {
+ QCOMPARE(rowCount(), 4);
+ QCOMPARE(data(index(0), QMenuModel::Label).toString(), QString("section1"));
+ QCOMPARE(data(index(1), QMenuModel::Label).toString(), QString("item2"));
+ QCOMPARE(data(index(2), QMenuModel::Label).toString(), QString("section2"));
+ QCOMPARE(data(index(3), QMenuModel::Label).toString(), QString("item1"));
+ QVERIFY(data(index(4), QMenuModel::Label).isNull());
+ }
+ }
+
+ void checkModelStateBeforeRemove(const QModelIndex &parent, int start, int end)
+ {
+ if (m_step == 0) {
+ QCOMPARE(rowCount(), 4);
+ QVERIFY(data(index(0), QMenuModel::Label).isNull());
+ QCOMPARE(data(index(1), QMenuModel::Label).toString(), QString("item2"));
+ QCOMPARE(data(index(2), QMenuModel::Label).toString(), QString("section2"));
+ QCOMPARE(data(index(3), QMenuModel::Label).toString(), QString("item1"));
+ QVERIFY(data(index(4), QMenuModel::Label).isNull());
+ } else if (m_step == 1) {
+ QCOMPARE(rowCount(), 3);
+ QCOMPARE(data(index(0), QMenuModel::Label).toString(), QString("item2"));
+ QCOMPARE(data(index(1), QMenuModel::Label).toString(), QString("section2"));
+ QVERIFY(data(index(3), QMenuModel::Label).isNull());
+ QVERIFY(data(index(4), QMenuModel::Label).isNull());
+ } else if (m_step == 2) {
+ QCOMPARE(rowCount(), 2);
+ QVERIFY(data(index(0), QMenuModel::Label).isNull());
+ QCOMPARE(data(index(1), QMenuModel::Label).toString(), QString("section2"));
+ QVERIFY(data(index(2), QMenuModel::Label).isNull());
+ } else if (m_step == 3) {
+ QCOMPARE(rowCount(), 1);
+ QVERIFY(data(index(0), QMenuModel::Label).isNull());
+ QVERIFY(data(index(1), QMenuModel::Label).isNull());
+ }
+ }
+
+ void checkModelStateAfterRemove(const QModelIndex &parent, int start, int end)
+ {
+ if (m_step == 0) {
+ QCOMPARE(rowCount(), 3);
+ QCOMPARE(data(index(0), QMenuModel::Label).toString(), QString("item2"));
+ QCOMPARE(data(index(1), QMenuModel::Label).toString(), QString("section2"));
+ QCOMPARE(data(index(2), QMenuModel::Label).toString(), QString("item1"));
+ QVERIFY(data(index(3), QMenuModel::Label).isNull());
+ } else if (m_step == 1) {
+ QCOMPARE(rowCount(), 2);
+ QCOMPARE(data(index(0), QMenuModel::Label).toString(), QString("item2"));
+ QCOMPARE(data(index(1), QMenuModel::Label).toString(), QString("section2"));
+ QVERIFY(data(index(3), QMenuModel::Label).isNull());
+ } else if (m_step == 2) {
+ QCOMPARE(rowCount(), 1);
+ QCOMPARE(data(index(0), QMenuModel::Label).toString(), QString("section2"));
+ QVERIFY(data(index(1), QMenuModel::Label).isNull());
+ } else if (m_step == 3) {
+ QCOMPARE(rowCount(), 0);
+ QVERIFY(data(index(0), QMenuModel::Label).isNull());
+ }
+ }
+
+private:
+ int m_step;
+};
+
+
+class ModelSignalsTest : public QObject
+{
+ Q_OBJECT
+private Q_SLOTS:
+ void initTestCase()
+ {
+ g_type_init();
+ }
+
+ /*
+ * Test if the model state still correct before and after insert a new row
+ */
+ void testSignalInsertRows()
+ {
+ MenuModelTestClass model;
+ QObject::connect(&model, SIGNAL(rowsAboutToBeInserted(QModelIndex,int,int)),
+ &model, SLOT(checkModelStateBeforeInsert(QModelIndex,int,int)));
+ QObject::connect(&model, SIGNAL(rowsInserted(QModelIndex,int,int)),
+ &model, SLOT(checkModelStateAfterInsert(QModelIndex,int,int)));
+ model.loadModel();
+ }
+
+ /*
+ * Test if the model state still correct before and after remove a row
+ */
+ void testSignalRemoveRows()
+ {
+ MenuModelTestClass model;
+ model.loadModel();
+
+ QObject::connect(&model, SIGNAL(rowsAboutToBeMoved(QModelIndex,int,int,QModelIndex,int)),
+ &model, SLOT(checkModelStateBeforeRemove(QModelIndex,int,int)));
+ QObject::connect(&model, SIGNAL(rowsRemoved(QModelIndex,int,int)),
+ &model, SLOT(checkModelStateAfterRemove(QModelIndex,int,int)));
+ model.clear();
+ }
+};
+
+QTEST_MAIN(ModelSignalsTest)
+
+#include "modelsignalstest.moc"
diff --git a/tests/client/modeltest.cpp b/tests/client/modeltest.cpp
index 3366177..017f859 100644
--- a/tests/client/modeltest.cpp
+++ b/tests/client/modeltest.cpp
@@ -29,15 +29,6 @@ extern "C" {
#include <gio/gio.h>
}
-class TestMenuModel : public QMenuModel
-{
-public:
- TestMenuModel(GMenuModel *other, QObject *parent=0)
- : QMenuModel(other, parent)
- {
- }
-};
-
class ModelTest : public QObject
{
Q_OBJECT
@@ -296,7 +287,6 @@ private Q_SLOTS:
QVariantMap extra = data["extra"].toMap();
QCOMPARE(extra.size(), 13);
QCOMPARE(extra["boolean"].toBool(), true);
-
}
};