From 6bacc0d5db885a72202cb7f80e505642a36052d4 Mon Sep 17 00:00:00 2001 From: Renato Araujo Oliveira Filho Date: Tue, 11 Sep 2012 17:57:08 -0300 Subject: Splitted qmenumodel plugin in two libraries to make it testable. Create unit test for qmenumodel library. --- CMakeLists.txt | 14 +- cmake/lcov.cmake | 68 ++++++++++ examples/simple.py | 8 +- src/CMakeLists.txt | 1 + src/QMenuModel/CMakeLists.txt | 13 +- src/QMenuModel/qdbusactiongroup.cpp | 193 -------------------------- src/QMenuModel/qdbusactiongroup.h | 63 --------- src/QMenuModel/qdbusmenumodel.cpp | 64 --------- src/QMenuModel/qdbusmenumodel.h | 45 ------ src/QMenuModel/qdbusobject.cpp | 121 ----------------- src/QMenuModel/qdbusobject.h | 65 --------- src/QMenuModel/qmenumodel.cpp | 156 --------------------- src/QMenuModel/qmenumodel.h | 44 ------ src/common/CMakeLists.txt | 42 ++++++ src/common/qdbusactiongroup.cpp | 193 ++++++++++++++++++++++++++ src/common/qdbusactiongroup.h | 63 +++++++++ src/common/qdbusmenumodel.cpp | 66 +++++++++ src/common/qdbusmenumodel.h | 47 +++++++ src/common/qdbusobject.cpp | 127 +++++++++++++++++ src/common/qdbusobject.h | 65 +++++++++ src/common/qmenumodel.cpp | 241 +++++++++++++++++++++++++++++++++ src/common/qmenumodel.h | 45 ++++++ tests/CMakeLists.txt | 2 + tests/client/CMakeLists.txt | 46 +++++++ tests/client/menuchangestest.cpp | 65 +++++++++ tests/client/modeltest.cpp | 185 +++++++++++++++++++++++++ tests/client/script_menuchangestest.py | 14 ++ tests/client/script_modeltest.py | 31 +++++ tests/client/script_servicetest.py | 12 ++ tests/client/servicetest.cpp | 101 ++++++++++++++ tests/script/CMakeLists.txt | 34 +++++ tests/script/dbusmenuscript.cpp | 75 ++++++++++ tests/script/dbusmenuscript.h | 35 +++++ tests/script/menuscript.py | 172 +++++++++++++++++++++++ 34 files changed, 1755 insertions(+), 761 deletions(-) create mode 100644 cmake/lcov.cmake delete mode 100644 src/QMenuModel/qdbusactiongroup.cpp delete mode 100644 src/QMenuModel/qdbusactiongroup.h delete mode 100644 src/QMenuModel/qdbusmenumodel.cpp delete mode 100644 src/QMenuModel/qdbusmenumodel.h delete mode 100644 src/QMenuModel/qdbusobject.cpp delete mode 100644 src/QMenuModel/qdbusobject.h delete mode 100644 src/QMenuModel/qmenumodel.cpp delete mode 100644 src/QMenuModel/qmenumodel.h create mode 100644 src/common/CMakeLists.txt create mode 100644 src/common/qdbusactiongroup.cpp create mode 100644 src/common/qdbusactiongroup.h create mode 100644 src/common/qdbusmenumodel.cpp create mode 100644 src/common/qdbusmenumodel.h create mode 100644 src/common/qdbusobject.cpp create mode 100644 src/common/qdbusobject.h create mode 100644 src/common/qmenumodel.cpp create mode 100644 src/common/qmenumodel.h create mode 100644 tests/CMakeLists.txt create mode 100644 tests/client/CMakeLists.txt create mode 100644 tests/client/menuchangestest.cpp create mode 100644 tests/client/modeltest.cpp create mode 100755 tests/client/script_menuchangestest.py create mode 100755 tests/client/script_modeltest.py create mode 100755 tests/client/script_servicetest.py create mode 100644 tests/client/servicetest.cpp create mode 100644 tests/script/CMakeLists.txt create mode 100644 tests/script/dbusmenuscript.cpp create mode 100644 tests/script/dbusmenuscript.h create mode 100644 tests/script/menuscript.py diff --git a/CMakeLists.txt b/CMakeLists.txt index d8dad33..65e6fa8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,6 +6,7 @@ find_package(Qt4 REQUIRED) include(FindPkgConfig) pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32) +pkg_check_modules(GIO REQUIRED gio-2.0>=2.32) add_definitions(-DQT_NO_KEYWORDS) find_program(DBUS_RUNNER dbus-test-runner) @@ -14,7 +15,10 @@ find_program(DBUS_RUNNER dbus-test-runner) OPTION(BUILD_WITH_COVERAGE "Build with coverage analysis support" OFF) if(BUILD_WITH_COVERAGE) message(STATUS "Using coverage flags") - SET(COVERAGE_COMMAND "/usr/bin/gcov") + find_program(COVERAGE_COMMAND gcov) + if(NOT COVERAGE_COMMAND) + message(FATAL_ERROR "gcov command not found") + endif() SET(CMAKE_C_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") SET(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage -lgcov") @@ -24,6 +28,14 @@ endif() add_subdirectory(src) add_subdirectory(examples) +# Tests Tools +if(NOT DBUS_RUNNER) + message(STATUS "dbus-test-runner not found tests disabled.") +else() + enable_testing() + add_subdirectory(tests) +endif() + # Tests Tools #if(NOT DBUS_RUNNER) diff --git a/cmake/lcov.cmake b/cmake/lcov.cmake new file mode 100644 index 0000000..5758859 --- /dev/null +++ b/cmake/lcov.cmake @@ -0,0 +1,68 @@ +# - This module creates a new 'lcov' target which generates +# a coverage analysis html output. +# LCOV is a graphical front-end for GCC's coverage testing tool gcov. Please see +# http://ltp.sourceforge.net/coverage/lcov.php +# +# Usage: you must add an option to your CMakeLists.txt to build your application +# with coverage support. Then you need to include this file to the lcov target. +# +# Example: +# IF(BUILD_WITH_COVERAGE) +# SET(CMAKE_C_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") +# SET(CMAKE_CXX_FLAGS "-g -O0 -Wall -fprofile-arcs -ftest-coverage") +# SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fprofile-arcs -ftest-coverage -lgcov") +# include(${CMAKE_SOURCE_DIR}/cmake/lcov.cmake) +# ENDIF(BUILD_WITH_COVERAGE) +#============================================================================= +# Copyright 2010 ascolab GmbH +# +# Distributed under the OSI-approved BSD License (the "License"); +# see accompanying file Copyright.txt for details. +# +# This software is distributed WITHOUT ANY WARRANTY; without even the +# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the License for more information. +#============================================================================= +# (To distributed this file outside of CMake, substitute the full +# License text for the above reference.) + +set(REMOVE_PATTERN + q*.h + *.moc + moc_*.cxx + locale_facets.h + new) + +## lcov target +ADD_CUSTOM_TARGET(lcov) +ADD_CUSTOM_COMMAND(TARGET lcov + COMMAND mkdir -p coverage + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) +ADD_CUSTOM_COMMAND(TARGET lcov + COMMAND lcov --directory . --zerocounters + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) +ADD_CUSTOM_COMMAND(TARGET lcov + COMMAND make test + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) +ADD_CUSTOM_COMMAND(TARGET lcov + COMMAND lcov --directory . --capture --output-file ./coverage/stap_all.info --no-checksum --compat-libtool + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) +ADD_CUSTOM_COMMAND(TARGET lcov + COMMAND lcov --directory . -r ./coverage/stap_all.info ${REMOVE_PATTERN} --output-file ./coverage/stap.info + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) +ADD_CUSTOM_COMMAND(TARGET lcov + COMMAND genhtml -o ./coverage --title "Code Coverage" --legend --show-details --demangle-cpp ./coverage/stap.info + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) +ADD_CUSTOM_COMMAND(TARGET lcov + COMMAND echo "Open ${CMAKE_BINARY_DIR}/coverage/index.html to view the coverage analysis results." + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + ) +ADD_DEPENDENCIES(lcov helloworld) + + diff --git a/examples/simple.py b/examples/simple.py index bc2d37c..4efbb8a 100644 --- a/examples/simple.py +++ b/examples/simple.py @@ -26,10 +26,16 @@ numbers.append ('One', 'one') numbers.append ('Two', 'two') numbers.append ('Three', 'three') +numbers2 = Gio.Menu () +numbers2.append ('One2', 'one2') +numbers2.append ('Two2', 'two2') +numbers2.append ('Three2', 'three2') + + menu = Gio.Menu () menu.append ('Menu item', 'one') menu.append_section ('Numbers', numbers) -menu.append_submenu ('Submenu', numbers) +menu.append_submenu ('Submenu', numbers2) # export the menu and action group on d-bus bus = Gio.bus_get_sync (Gio.BusType.SESSION, None) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index e2c06cb..7215efc 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,3 +1,4 @@ project(src) +add_subdirectory(common) add_subdirectory(QMenuModel) diff --git a/src/QMenuModel/CMakeLists.txt b/src/QMenuModel/CMakeLists.txt index ee3f28c..bf3bb5a 100644 --- a/src/QMenuModel/CMakeLists.txt +++ b/src/QMenuModel/CMakeLists.txt @@ -1,16 +1,11 @@ + +project(qmenumodelqmlplugin) + set(QMENUMODEL_SRC - qmenumodel.cpp - qdbusobject.cpp - qdbusmenumodel.cpp - qdbusactiongroup.cpp plugin.cpp ) set(QMENUMODEL_HEADERS - qmenumodel.h - qdbusobject.h - qdbusmenumodel.h - qdbusactiongroup.h plugin.h ) @@ -25,6 +20,7 @@ add_library(qmenumodel MODULE include_directories( ${CMAKE_CURRENT_SOURCE_DIR} + ${qmenumodelcommon_SOURCE_DIR} ${QT_INCLUDE_DIR} ${QT_QTCORE_INCLUDE_DIR} ${QT_QTGUI_INCLUDE_DIR} @@ -34,6 +30,7 @@ include_directories( ) target_link_libraries(qmenumodel + qmenumodelcommon ${QT_QTCORE_LIBRARY} ${QT_QTGUI_LIBRARY} ${QT_QTDCLARATIVE_LIBRARY} diff --git a/src/QMenuModel/qdbusactiongroup.cpp b/src/QMenuModel/qdbusactiongroup.cpp deleted file mode 100644 index ca430fb..0000000 --- a/src/QMenuModel/qdbusactiongroup.cpp +++ /dev/null @@ -1,193 +0,0 @@ -#include "qdbusactiongroup.h" - -#include - -#include - -QDBusActionGroup::QDBusActionGroup(QObject *parent) - :QObject(parent), - m_actionGroup(NULL) -{ -} - -QDBusActionGroup::~QDBusActionGroup() -{ - clear(); -} - -QAction *QDBusActionGroup::getAction(const QString &actionName) -{ - Q_FOREACH(QAction *act, m_actions) { - if (act->text() == actionName) { - return act; - } - } - - return NULL; -} - -void QDBusActionGroup::serviceVanish(GDBusConnection *) -{ - clear(); -} - -void QDBusActionGroup::serviceAppear(GDBusConnection *connection) -{ - GDBusActionGroup *ag = g_dbus_action_group_get(connection, - busName().toLatin1(), - objectPath().toLatin1()); - setActionGroup(ag); - if (ag == NULL) { - stop(); - } -} - -void QDBusActionGroup::start() -{ - QDbusObject::connect(); -} - -void QDBusActionGroup::stop() -{ - QDbusObject::disconnect(); -} - -void QDBusActionGroup::busTypeChanged(BusType) -{ - busTypeChanged(); -} - -void QDBusActionGroup::busNameChanged(const QString &) -{ - busNameChanged(); -} - -void QDBusActionGroup::objectPathChanged(const QString &objectPath) -{ - objectPathChanged(); -} - -void QDBusActionGroup::statusChanged(ConnectionStatus status) -{ - statusChanged(); -} - -void QDBusActionGroup::setIntBusType(int busType) -{ - if ((busType > None) && (busType < LastBusType)) { - setBusType(static_cast(busType)); - } -} - -void QDBusActionGroup::setActionGroup(GDBusActionGroup *ag) -{ - if (m_actionGroup == reinterpret_cast(ag)) { - return; - } - - if (m_actionGroup) { - g_signal_handler_disconnect(m_actionGroup, m_signalActionAddId); - g_signal_handler_disconnect(m_actionGroup, m_signalActionRemovedId); - m_signalActionAddId = m_signalActionRemovedId = 0; - g_object_unref(m_actionGroup); - } - - m_actionGroup = reinterpret_cast(ag); - - if (m_actionGroup) { - - m_signalActionAddId = g_signal_connect(m_actionGroup, - "action-add", - G_CALLBACK(QDBusActionGroup::onActionAdded), - this); - - m_signalActionRemovedId = g_signal_connect(m_actionGroup, - "action-removed", - G_CALLBACK(QDBusActionGroup::onActionRemoved), - this); - - gchar **actionNames = g_action_group_list_actions(m_actionGroup); - for(int i=0; actionNames[i] != NULL; i++) { - addAction(actionNames[i]); - } - g_strfreev(actionNames); - } -} - -void QDBusActionGroup::addAction(const char *actionName) -{ - QAction *act = new QAction(actionName, this); - - act->setEnabled(g_action_group_get_action_enabled(m_actionGroup, actionName)); - - const GVariantType *stateType = g_action_group_get_action_state_type(m_actionGroup, actionName); - if (stateType == G_VARIANT_TYPE_BOOLEAN) { - act->setCheckable(true); - - GVariant *actState = g_action_group_get_action_state(m_actionGroup, actionName); - if (actState != NULL) { - act->setChecked(g_variant_get_boolean(actState)); - g_variant_unref(actState); - } - } - - // remove any older action with the same name - removeAction(actionName); - - m_actions.insert(act); -} - -void QDBusActionGroup::removeAction(const char *actionName) -{ - Q_FOREACH(QAction *act, m_actions) { - if (act->text() == actionName) { - m_actions.remove(act); - delete act; - break; - } - } -} - -void QDBusActionGroup::updateAction(const char *actionName, GVariant *state) -{ - QAction *action = getAction(actionName); - if ((action != NULL) && (state != NULL)) { - - const GVariantType *stateType = g_variant_get_type(state); - if (stateType == G_VARIANT_TYPE_BOOLEAN) { - action->setChecked(g_variant_get_boolean(state)); - } - } -} - -void QDBusActionGroup::clear() -{ - Q_FOREACH(QAction *act, m_actions) { - delete act; - } - m_actions.clear(); - - if (m_actionGroup != NULL) { - g_object_unref(m_actionGroup); - m_actionGroup = NULL; - } -} - -void QDBusActionGroup::onActionAdded(GDBusActionGroup *, gchar *actionName, gpointer data) -{ - QDBusActionGroup *self = reinterpret_cast(data); - self->addAction(actionName); -} - -void QDBusActionGroup::onActionRemoved(GDBusActionGroup *, gchar *actionName, gpointer data) -{ - QDBusActionGroup *self = reinterpret_cast(data); - self->removeAction(actionName); - -} - -void QDBusActionGroup::onActionStateChanged(GDBusActionGroup *ag, gchar *actionName, GVariant *value, gpointer data) -{ - QDBusActionGroup *self = reinterpret_cast(data); - self->updateAction(actionName, value); -} diff --git a/src/QMenuModel/qdbusactiongroup.h b/src/QMenuModel/qdbusactiongroup.h deleted file mode 100644 index a32b03e..0000000 --- a/src/QMenuModel/qdbusactiongroup.h +++ /dev/null @@ -1,63 +0,0 @@ -#ifndef QDBUSACTIONGROUP_H -#define QDBUSACTIONGROUP_H - -#include "qdbusobject.h" - -#include -#include -#include - -class QDBusActionGroup : public QObject, public QDbusObject -{ - Q_OBJECT - Q_PROPERTY(int busType READ busType WRITE setIntBusType NOTIFY busTypeChanged) - Q_PROPERTY(QString busName READ busName WRITE setBusName NOTIFY busNameChanged) - Q_PROPERTY(QString objectPath READ objectPath WRITE setObjectPath NOTIFY objectPathChanged) - Q_PROPERTY(int status READ status NOTIFY statusChanged) - -public: - QDBusActionGroup(QObject *parent=0); - ~QDBusActionGroup(); - -Q_SIGNALS: - void busTypeChanged(); - void busNameChanged(); - void objectPathChanged(); - void statusChanged(); - -public Q_SLOTS: - void start(); - void stop(); - QAction *getAction(const QString &actionName); - -protected: - virtual void serviceAppear(GDBusConnection *connection); - virtual void serviceVanish(GDBusConnection *connection); - - virtual void busTypeChanged(BusType type); - virtual void busNameChanged(const QString &busNameChanged); - virtual void objectPathChanged(const QString &objectPath); - virtual void statusChanged(ConnectionStatus status); - -private: - GActionGroup *m_actionGroup; - QSet m_actions; - int m_signalActionAddId; - int m_signalActionRemovedId; - - // workaround to support int as bustType - void setIntBusType(int busType); - - void setActionGroup(GDBusActionGroup *ag); - void addAction(const char *actionName); - void removeAction(const char *actionName); - void updateAction(const char *actionName, GVariant *state); - void clear(); - - // glib slots - static void onActionAdded(GDBusActionGroup *ag, gchar *actionName, gpointer data); - static void onActionRemoved(GDBusActionGroup *ag, gchar *actionName, gpointer data); - static void onActionStateChanged(GDBusActionGroup *ag, gchar *actionName, GVariant *value, gpointer data); -}; - -#endif diff --git a/src/QMenuModel/qdbusmenumodel.cpp b/src/QMenuModel/qdbusmenumodel.cpp deleted file mode 100644 index c551464..0000000 --- a/src/QMenuModel/qdbusmenumodel.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "qdbusmenumodel.h" -#include - -QDBusMenuModel::QDBusMenuModel(QObject *parent) - :QMenuModel(0, parent) -{ -} - -QDBusMenuModel::~QDBusMenuModel() -{ -} - -void QDBusMenuModel::start() -{ - QDbusObject::connect(); -} - -void QDBusMenuModel::stop() -{ - QDbusObject::disconnect(); -} - -void QDBusMenuModel::serviceVanish(GDBusConnection *) -{ - setMenuModel(NULL); -} - -void QDBusMenuModel::serviceAppear(GDBusConnection *connection) -{ - GMenuModel *model = reinterpret_cast(g_dbus_menu_model_get(connection, - busName().toLatin1(), - objectPath().toLatin1())); - setMenuModel(model); - if (model == NULL) { - stop(); - } -} - -void QDBusMenuModel::busTypeChanged(BusType) -{ - busTypeChanged(); -} - -void QDBusMenuModel::busNameChanged(const QString &) -{ - busNameChanged(); -} - -void QDBusMenuModel::objectPathChanged(const QString &objectPath) -{ - objectPathChanged(); -} - -void QDBusMenuModel::statusChanged(ConnectionStatus status) -{ - statusChanged(); -} - -void QDBusMenuModel::setIntBusType(int busType) -{ - if ((busType > None) && (busType < LastBusType)) { - setBusType(static_cast(busType)); - } -} diff --git a/src/QMenuModel/qdbusmenumodel.h b/src/QMenuModel/qdbusmenumodel.h deleted file mode 100644 index 975e72d..0000000 --- a/src/QMenuModel/qdbusmenumodel.h +++ /dev/null @@ -1,45 +0,0 @@ -#ifndef QDBUSMENUMODEL_H -#define QDBUSMENUMODEL_H - -#include "qdbusobject.h" -#include "qmenumodel.h" - -#include - -class QDBusMenuModel : public QMenuModel, public QDbusObject -{ - Q_OBJECT - Q_PROPERTY(int busType READ busType WRITE setIntBusType NOTIFY busTypeChanged) - Q_PROPERTY(QString busName READ busName WRITE setBusName NOTIFY busNameChanged) - Q_PROPERTY(QString objectPath READ objectPath WRITE setObjectPath NOTIFY objectPathChanged) - Q_PROPERTY(int status READ status NOTIFY statusChanged) - -public: - QDBusMenuModel(QObject *parent=0); - ~QDBusMenuModel(); - -Q_SIGNALS: - void busTypeChanged(); - void busNameChanged(); - void objectPathChanged(); - void statusChanged(); - -public Q_SLOTS: - void start(); - void stop(); - -protected: - virtual void serviceAppear(GDBusConnection *connection); - virtual void serviceVanish(GDBusConnection *connection); - - virtual void busTypeChanged(BusType type); - virtual void busNameChanged(const QString &busNameChanged); - virtual void objectPathChanged(const QString &objectPath); - virtual void statusChanged(ConnectionStatus status); - -private: - // workaround to support int as bustType - void setIntBusType(int busType); -}; - -#endif diff --git a/src/QMenuModel/qdbusobject.cpp b/src/QMenuModel/qdbusobject.cpp deleted file mode 100644 index 9f11813..0000000 --- a/src/QMenuModel/qdbusobject.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include "qdbusobject.h" - -#include - -QDbusObject::QDbusObject() - :m_watchId(0), - m_busType(None), - m_status(QDbusObject::Disconnected) -{ -} - -QDbusObject::~QDbusObject() -{ - if (m_watchId != 0) { - g_bus_unwatch_name (m_watchId); - m_watchId = 0; - } -} - -QDbusObject::BusType QDbusObject::busType() const -{ - return m_busType; -} - -void QDbusObject::setBusType(QDbusObject::BusType type) -{ - if (m_busType != type) { - if (m_status != QDbusObject::Disconnected) - disconnect(); - m_busType = type; - busTypeChanged(m_busType); - } -} - -QString QDbusObject::busName() const -{ - return m_busName; -} - -void QDbusObject::setBusName(const QString &busName) -{ - if (m_busName != busName) { - if (m_status != QDbusObject::Disconnected) - disconnect(); - m_busName = busName; - busNameChanged(m_busName); - } -} - -QString QDbusObject::objectPath() const -{ - return m_objectPath; -} - -void QDbusObject::setObjectPath(const QString &objectPath) -{ - if (m_objectPath != objectPath) { - if (m_status != QDbusObject::Disconnected) - disconnect(); - m_objectPath = objectPath; - objectPathChanged(m_objectPath); - } -} - -void QDbusObject::setStatus(QDbusObject::ConnectionStatus status) -{ - if (m_status != status) { - m_status = status; - statusChanged(m_status); - } -} - -QDbusObject::ConnectionStatus QDbusObject::status() const -{ - return m_status; -} - -void QDbusObject::connect() -{ - if (m_status != QDbusObject::Disconnected) { - return; - } else if ((m_busType > None) && !m_objectPath.isEmpty() && !m_busName.isEmpty()) { - GBusType type = m_busType == SessionBus ? G_BUS_TYPE_SESSION : G_BUS_TYPE_SYSTEM; - m_watchId = g_bus_watch_name (type, - m_busName.toLatin1(), - G_BUS_NAME_WATCHER_FLAGS_NONE, - QDbusObject::onServiceAppeared, - QDbusObject::onServiceFanished, - this, - NULL); - - setStatus(QDbusObject::Connecting); - } else { - qWarning() << "Invalid dbus connection args"; - } -} - -void QDbusObject::disconnect() -{ - if (m_status != QDbusObject::Disconnected) { - g_bus_unwatch_name (m_watchId); - m_watchId = 0; - setStatus(QDbusObject::Disconnected); - } -} - -void QDbusObject::onServiceAppeared(GDBusConnection *connection, const gchar *, const gchar *, gpointer data) -{ - QDbusObject *self = reinterpret_cast(data); - - self->setStatus(QDbusObject::Connected); - self->serviceAppear(connection); -} - -void QDbusObject::onServiceFanished(GDBusConnection *connection, const gchar *, gpointer data) -{ - QDbusObject *self = reinterpret_cast(data); - - self->setStatus(QDbusObject::Connecting); - self->serviceVanish(connection); -} diff --git a/src/QMenuModel/qdbusobject.h b/src/QMenuModel/qdbusobject.h deleted file mode 100644 index 0b17ae1..0000000 --- a/src/QMenuModel/qdbusobject.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef QDBUSOBJECT_H -#define QDBUSOBJECT_H - -#include - -#include - -class QDbusObject -{ -public: - enum BusType { - None = 0, - SessionBus, - SystemBus, - LastBusType - }; - - enum ConnectionStatus { - Disconnected = 0, - Connecting, - Connected - }; - - QDbusObject(); - ~QDbusObject(); - - BusType busType() const; - void setBusType(BusType type); - - QString busName() const; - void setBusName(const QString &busName); - - QString objectPath() const; - void setObjectPath(const QString &busName); - - ConnectionStatus status() const; - - void connect(); - void disconnect(); - -protected: - virtual void serviceAppear(GDBusConnection *connection) = 0; - virtual void serviceVanish(GDBusConnection *connection) = 0; - - // notify functions - virtual void busTypeChanged(BusType type) = 0; - virtual void busNameChanged(const QString &busNameChanged) = 0; - virtual void objectPathChanged(const QString &objectPath) = 0; - virtual void statusChanged(ConnectionStatus status) = 0; - -private: - guint m_watchId; - BusType m_busType; - QString m_busName; - QString m_objectPath; - ConnectionStatus m_status; - - void setStatus(ConnectionStatus status); - - // glib slots - static void onServiceAppeared(GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer data); - static void onServiceFanished(GDBusConnection *connection, const gchar *name, gpointer data); -}; - -#endif diff --git a/src/QMenuModel/qmenumodel.cpp b/src/QMenuModel/qmenumodel.cpp deleted file mode 100644 index c7ccf56..0000000 --- a/src/QMenuModel/qmenumodel.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include "qmenumodel.h" -#include - -QMenuModel::QMenuModel(GMenuModel *other, QObject *parent) - : QAbstractListModel(parent), - m_menuModel(0), - m_signalChangedId(0) -{ - static QHash rolesNames; - if (rolesNames.empty()) { - rolesNames[Action] = "action"; - rolesNames[Label] = "label"; - rolesNames[LinkSection] = "linkSection"; - rolesNames[LinkSubMenu] = "linkSubMenu"; - } - setRoleNames(rolesNames); - setMenuModel(other); -} - -QMenuModel::~QMenuModel() -{ - setMenuModel(NULL); -} - -void QMenuModel::setMenuModel(GMenuModel *other) -{ - if (m_menuModel == other) { - return; - } - - beginResetModel(); - - if (m_menuModel) { - g_signal_handler_disconnect(m_menuModel, m_signalChangedId); - m_signalChangedId = 0; - g_object_unref(m_menuModel); - } - - m_menuModel = other; - - endResetModel(); - - if (m_menuModel) { - qDebug() << "Menu size:" << g_menu_model_get_n_items(m_menuModel); - m_signalChangedId = g_signal_connect(m_menuModel, - "items-changed", - G_CALLBACK(QMenuModel::onItemsChanged), - this); - } -} - -GMenuModel *QMenuModel::menuModel() const -{ - return m_menuModel; -} - -/* QAbstractItemModel */ -int QMenuModel::columnCount(const QModelIndex &) const -{ - return 1; -} - -QVariant QMenuModel::data(const QModelIndex &index, int role) const -{ - QVariant attribute; - int rowCountValue = rowCount(); - - if ((rowCountValue > 0) && (index.row() >= 0) && (index.row() < rowCountValue)) { - if (m_menuModel) { - switch (role) - { - case Action: - attribute = getStringAttribute(index, G_MENU_ATTRIBUTE_ACTION); - break; - case Label: - attribute = getStringAttribute(index, G_MENU_ATTRIBUTE_LABEL); - break; - case LinkSection: - attribute = getLink(index, G_MENU_LINK_SECTION); - break; - case LinkSubMenu: - attribute = getLink(index, G_MENU_LINK_SUBMENU); - break; - default: - break; - } - } - } - return attribute; -} - -QModelIndex QMenuModel::parent(const QModelIndex &index) const -{ - return QModelIndex(); -} - -int QMenuModel::rowCount(const QModelIndex &) const -{ - if (m_menuModel) { - return g_menu_model_get_n_items(m_menuModel); - } - return 0; -} - -QVariant QMenuModel::getStringAttribute(const QModelIndex &index, - const QString &attribute) const -{ - QVariant result; - gchar* value = NULL; - g_menu_model_get_item_attribute(m_menuModel, - index.row(), - attribute.toLatin1(), - "s", &value); - if (value) { - result = QVariant(QString::fromLatin1(value)); - g_free(value); - } - return result; -} - -QVariant QMenuModel::getLink(const QModelIndex &index, - const QString &linkName) const -{ - GMenuModel *link; - - link = g_menu_model_get_item_link(m_menuModel, - index.row(), - linkName.toLatin1()); - - if (link) { - QMenuModel *other = new QMenuModel(link, const_cast(this)); - return QVariant::fromValue(other); - } - - return QVariant(); -} - -void QMenuModel::onItemsChanged(GMenuModel *, - gint position, - gint removed, - gint added, - gpointer data) -{ - QMenuModel *self = reinterpret_cast(data); - - if (removed > 0) { - self->beginRemoveRows(QModelIndex(), position, position + removed - 1); - self->endRemoveRows(); - } - - if (added > 0) { - self->beginInsertRows(QModelIndex(), position, position + added - 1); - self->endInsertRows(); - } -} - diff --git a/src/QMenuModel/qmenumodel.h b/src/QMenuModel/qmenumodel.h deleted file mode 100644 index 18ad21c..0000000 --- a/src/QMenuModel/qmenumodel.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef QMENUMODEL_H -#define QMENUMODEL_H - -#include -#include - - -class QMenuModel : public QAbstractListModel -{ - Q_OBJECT - -public: - enum MenuRoles { - Action = 0, - Label, - LinkSection, - LinkSubMenu - }; - - ~QMenuModel(); - - /* QAbstractItemModel */ - int columnCount(const QModelIndex &parent = QModelIndex()) const; - QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; - QModelIndex parent (const QModelIndex &index) const; - int rowCount(const QModelIndex &parent = QModelIndex()) const; - -protected: - QMenuModel(GMenuModel *other=0, QObject *parent=0); - void setMenuModel(GMenuModel *model); - GMenuModel *menuModel() const; - -private: - GMenuModel *m_menuModel; - guint m_signalChangedId; - - QVariant getStringAttribute(const QModelIndex &index, const QString &attribute) const; - QVariant getLink(const QModelIndex &index, const QString &linkName) const; - - static void onItemsChanged(GMenuModel *model, gint position, gint removed, gint added, gpointer data); - -}; - -#endif diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt new file mode 100644 index 0000000..1428000 --- /dev/null +++ b/src/common/CMakeLists.txt @@ -0,0 +1,42 @@ +project(qmenumodelcommon) + +set(QMENUMODELCOMMON_SRC + qmenumodel.cpp + qdbusobject.cpp + qdbusmenumodel.cpp + qdbusactiongroup.cpp +) + +set(QMENUMODELCOMMON_HEADERS + qmenumodel.h + qdbusobject.h + qdbusmenumodel.h + qdbusactiongroup.h +) + +qt4_wrap_cpp(QMENUMODELCOMMON_MOC + ${QMENUMODELCOMMON_HEADERS} +) + +add_library(qmenumodelcommon STATIC + ${QMENUMODELCOMMON_SRC} + ${QMENUMODELCOMMON_MOC} +) + +set_target_properties(qmenumodelcommon PROPERTIES COMPILE_FLAGS -fPIC) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${QT_INCLUDE_DIR} + ${QT_QTCORE_INCLUDE_DIR} + ${QT_QTGUI_INCLUDE_DIR} + ${GLIB_INCLUDE_DIRS} + ${GIO_INCLUDE_DIRS} +) + +target_link_libraries(qmenumodelcommon + ${QT_QTCORE_LIBRARY} + ${QT_QTGUI_LIBRARY} + ${GLIB_LDFLAGS} + ${GIO_LDFLAGS} +) diff --git a/src/common/qdbusactiongroup.cpp b/src/common/qdbusactiongroup.cpp new file mode 100644 index 0000000..8475c29 --- /dev/null +++ b/src/common/qdbusactiongroup.cpp @@ -0,0 +1,193 @@ +#include "qdbusactiongroup.h" + +#include + +#include + +QDBusActionGroup::QDBusActionGroup(QObject *parent) + :QObject(parent), + m_actionGroup(NULL) +{ +} + +QDBusActionGroup::~QDBusActionGroup() +{ + clear(); +} + +QAction *QDBusActionGroup::getAction(const QString &actionName) +{ + Q_FOREACH(QAction *act, m_actions) { + if (act->text() == actionName) { + return act; + } + } + + return NULL; +} + +void QDBusActionGroup::serviceVanish(GDBusConnection *) +{ + clear(); +} + +void QDBusActionGroup::serviceAppear(GDBusConnection *connection) +{ + GDBusActionGroup *ag = g_dbus_action_group_get(connection, + busName().toLatin1(), + objectPath().toLatin1()); + setActionGroup(ag); + if (ag == NULL) { + stop(); + } +} + +void QDBusActionGroup::start() +{ + QDBusObject::connect(); +} + +void QDBusActionGroup::stop() +{ + QDBusObject::disconnect(); +} + +void QDBusActionGroup::busTypeChanged(BusType) +{ + busTypeChanged(); +} + +void QDBusActionGroup::busNameChanged(const QString &) +{ + busNameChanged(); +} + +void QDBusActionGroup::objectPathChanged(const QString &objectPath) +{ + objectPathChanged(); +} + +void QDBusActionGroup::statusChanged(ConnectionStatus status) +{ + statusChanged(); +} + +void QDBusActionGroup::setIntBusType(int busType) +{ + if ((busType > None) && (busType < LastBusType)) { + setBusType(static_cast(busType)); + } +} + +void QDBusActionGroup::setActionGroup(GDBusActionGroup *ag) +{ + if (m_actionGroup == reinterpret_cast(ag)) { + return; + } + + if (m_actionGroup) { + g_signal_handler_disconnect(m_actionGroup, m_signalActionAddId); + g_signal_handler_disconnect(m_actionGroup, m_signalActionRemovedId); + m_signalActionAddId = m_signalActionRemovedId = 0; + g_object_unref(m_actionGroup); + } + + m_actionGroup = reinterpret_cast(ag); + + if (m_actionGroup) { + + m_signalActionAddId = g_signal_connect(m_actionGroup, + "action-add", + G_CALLBACK(QDBusActionGroup::onActionAdded), + this); + + m_signalActionRemovedId = g_signal_connect(m_actionGroup, + "action-removed", + G_CALLBACK(QDBusActionGroup::onActionRemoved), + this); + + gchar **actionNames = g_action_group_list_actions(m_actionGroup); + for(int i=0; actionNames[i] != NULL; i++) { + addAction(actionNames[i]); + } + g_strfreev(actionNames); + } +} + +void QDBusActionGroup::addAction(const char *actionName) +{ + QAction *act = new QAction(actionName, this); + + act->setEnabled(g_action_group_get_action_enabled(m_actionGroup, actionName)); + + const GVariantType *stateType = g_action_group_get_action_state_type(m_actionGroup, actionName); + if (stateType == G_VARIANT_TYPE_BOOLEAN) { + act->setCheckable(true); + + GVariant *actState = g_action_group_get_action_state(m_actionGroup, actionName); + if (actState != NULL) { + act->setChecked(g_variant_get_boolean(actState)); + g_variant_unref(actState); + } + } + + // remove any older action with the same name + removeAction(actionName); + + m_actions.insert(act); +} + +void QDBusActionGroup::removeAction(const char *actionName) +{ + Q_FOREACH(QAction *act, m_actions) { + if (act->text() == actionName) { + m_actions.remove(act); + delete act; + break; + } + } +} + +void QDBusActionGroup::updateAction(const char *actionName, GVariant *state) +{ + QAction *action = getAction(actionName); + if ((action != NULL) && (state != NULL)) { + + const GVariantType *stateType = g_variant_get_type(state); + if (stateType == G_VARIANT_TYPE_BOOLEAN) { + action->setChecked(g_variant_get_boolean(state)); + } + } +} + +void QDBusActionGroup::clear() +{ + Q_FOREACH(QAction *act, m_actions) { + delete act; + } + m_actions.clear(); + + if (m_actionGroup != NULL) { + g_object_unref(m_actionGroup); + m_actionGroup = NULL; + } +} + +void QDBusActionGroup::onActionAdded(GDBusActionGroup *, gchar *actionName, gpointer data) +{ + QDBusActionGroup *self = reinterpret_cast(data); + self->addAction(actionName); +} + +void QDBusActionGroup::onActionRemoved(GDBusActionGroup *, gchar *actionName, gpointer data) +{ + QDBusActionGroup *self = reinterpret_cast(data); + self->removeAction(actionName); + +} + +void QDBusActionGroup::onActionStateChanged(GDBusActionGroup *ag, gchar *actionName, GVariant *value, gpointer data) +{ + QDBusActionGroup *self = reinterpret_cast(data); + self->updateAction(actionName, value); +} diff --git a/src/common/qdbusactiongroup.h b/src/common/qdbusactiongroup.h new file mode 100644 index 0000000..fe743b6 --- /dev/null +++ b/src/common/qdbusactiongroup.h @@ -0,0 +1,63 @@ +#ifndef QDBUSACTIONGROUP_H +#define QDBUSACTIONGROUP_H + +#include "qdbusobject.h" + +#include +#include +#include + +class QDBusActionGroup : public QObject, public QDBusObject +{ + Q_OBJECT + Q_PROPERTY(int busType READ busType WRITE setIntBusType NOTIFY busTypeChanged) + Q_PROPERTY(QString busName READ busName WRITE setBusName NOTIFY busNameChanged) + Q_PROPERTY(QString objectPath READ objectPath WRITE setObjectPath NOTIFY objectPathChanged) + Q_PROPERTY(int status READ status NOTIFY statusChanged) + +public: + QDBusActionGroup(QObject *parent=0); + ~QDBusActionGroup(); + +Q_SIGNALS: + void busTypeChanged(); + void busNameChanged(); + void objectPathChanged(); + void statusChanged(); + +public Q_SLOTS: + void start(); + void stop(); + QAction *getAction(const QString &actionName); + +protected: + virtual void serviceAppear(GDBusConnection *connection); + virtual void serviceVanish(GDBusConnection *connection); + + virtual void busTypeChanged(BusType type); + virtual void busNameChanged(const QString &busNameChanged); + virtual void objectPathChanged(const QString &objectPath); + virtual void statusChanged(ConnectionStatus status); + +private: + GActionGroup *m_actionGroup; + QSet m_actions; + int m_signalActionAddId; + int m_signalActionRemovedId; + + // workaround to support int as bustType + void setIntBusType(int busType); + + void setActionGroup(GDBusActionGroup *ag); + void addAction(const char *actionName); + void removeAction(const char *actionName); + void updateAction(const char *actionName, GVariant *state); + void clear(); + + // glib slots + static void onActionAdded(GDBusActionGroup *ag, gchar *actionName, gpointer data); + static void onActionRemoved(GDBusActionGroup *ag, gchar *actionName, gpointer data); + static void onActionStateChanged(GDBusActionGroup *ag, gchar *actionName, GVariant *value, gpointer data); +}; + +#endif diff --git a/src/common/qdbusmenumodel.cpp b/src/common/qdbusmenumodel.cpp new file mode 100644 index 0000000..6e35c85 --- /dev/null +++ b/src/common/qdbusmenumodel.cpp @@ -0,0 +1,66 @@ +#include "qdbusmenumodel.h" +#include + +QDBusMenuModel::QDBusMenuModel(QObject *parent) + :QMenuModel(0, parent) +{ +} + +QDBusMenuModel::~QDBusMenuModel() +{ +} + +void QDBusMenuModel::start() +{ + QDBusObject::connect(); +} + +void QDBusMenuModel::stop() +{ + QDBusObject::disconnect(); +} + +void QDBusMenuModel::serviceVanish(GDBusConnection *) +{ + setMenuModel(NULL); +} + +void QDBusMenuModel::serviceAppear(GDBusConnection *connection) +{ + GMenuModel *model = reinterpret_cast(g_dbus_menu_model_get(connection, + busName().toLatin1(), + objectPath().toLatin1())); + setMenuModel(model); + if (model == NULL) { + stop(); + } +} + +/* +void QDBusMenuModel::busTypeChanged(BusType) +{ + busTypeChanged(); +} + +void QDBusMenuModel::busNameChanged(const QString &) +{ + busNameChanged(); +} + +void QDBusMenuModel::objectPathChanged(const QString &objectPath) +{ + objectPathChanged(); +} + +void QDBusMenuModel::statusChanged(ConnectionStatus status) +{ + statusChanged(); +} +*/ + +void QDBusMenuModel::setIntBusType(int busType) +{ + if ((busType > None) && (busType < LastBusType)) { + setBusType(static_cast(busType)); + } +} diff --git a/src/common/qdbusmenumodel.h b/src/common/qdbusmenumodel.h new file mode 100644 index 0000000..4fc439d --- /dev/null +++ b/src/common/qdbusmenumodel.h @@ -0,0 +1,47 @@ +#ifndef QDBUSMENUMODEL_H +#define QDBUSMENUMODEL_H + +#include "qdbusobject.h" +#include "qmenumodel.h" + +#include + +class QDBusMenuModel : public QMenuModel, public QDBusObject +{ + Q_OBJECT + Q_PROPERTY(int busType READ busType WRITE setIntBusType NOTIFY busTypeChanged) + Q_PROPERTY(QString busName READ busName WRITE setBusName NOTIFY busNameChanged) + Q_PROPERTY(QString objectPath READ objectPath WRITE setObjectPath NOTIFY objectPathChanged) + Q_PROPERTY(int status READ status NOTIFY statusChanged) + +public: + QDBusMenuModel(QObject *parent=0); + ~QDBusMenuModel(); + +Q_SIGNALS: + void busTypeChanged(BusType type); + void busNameChanged(const QString &busNameChanged); + void objectPathChanged(const QString &objectPath); + void statusChanged(QDBusObject::ConnectionStatus status); + +public Q_SLOTS: + void start(); + void stop(); + +protected: + virtual void serviceAppear(GDBusConnection *connection); + virtual void serviceVanish(GDBusConnection *connection); + + /* + virtual void busTypeChanged(BusType type); + virtual void busNameChanged(const QString &busNameChanged); + virtual void objectPathChanged(const QString &objectPath); + virtual void statusChanged(ConnectionStatus status); + */ + +private: + // workaround to support int as bustType + void setIntBusType(int busType); +}; + +#endif diff --git a/src/common/qdbusobject.cpp b/src/common/qdbusobject.cpp new file mode 100644 index 0000000..6dec6d9 --- /dev/null +++ b/src/common/qdbusobject.cpp @@ -0,0 +1,127 @@ +#include "qdbusobject.h" + +#include + + + +QDBusObject::QDBusObject() + :m_watchId(0), + m_busType(None), + m_status(QDBusObject::Disconnected) +{ + qDebug() << "DBUS CREATED"; + qRegisterMetaType("QDBusObject::ConnectionStatus"); +} + +QDBusObject::~QDBusObject() +{ + if (m_watchId != 0) { + g_bus_unwatch_name (m_watchId); + m_watchId = 0; + } +} + +QDBusObject::BusType QDBusObject::busType() const +{ + return m_busType; +} + +void QDBusObject::setBusType(QDBusObject::BusType type) +{ + if (m_busType != type) { + if (m_status != QDBusObject::Disconnected) + disconnect(); + m_busType = type; + busTypeChanged(m_busType); + } +} + +QString QDBusObject::busName() const +{ + return m_busName; +} + +void QDBusObject::setBusName(const QString &busName) +{ + if (m_busName != busName) { + if (m_status != QDBusObject::Disconnected) + disconnect(); + m_busName = busName; + busNameChanged(m_busName); + } +} + +QString QDBusObject::objectPath() const +{ + return m_objectPath; +} + +void QDBusObject::setObjectPath(const QString &objectPath) +{ + if (m_objectPath != objectPath) { + if (m_status != QDBusObject::Disconnected) + disconnect(); + m_objectPath = objectPath; + objectPathChanged(m_objectPath); + } +} + +void QDBusObject::setStatus(QDBusObject::ConnectionStatus status) +{ + if (m_status != status) { + m_status = status; + statusChanged(m_status); + } +} + +QDBusObject::ConnectionStatus QDBusObject::status() const +{ + return m_status; +} + +void QDBusObject::connect() +{ + if (m_status != QDBusObject::Disconnected) { + return; + } else if ((m_busType > None) && !m_objectPath.isEmpty() && !m_busName.isEmpty()) { + GBusType type = m_busType == SessionBus ? G_BUS_TYPE_SESSION : G_BUS_TYPE_SYSTEM; + m_watchId = g_bus_watch_name (type, + m_busName.toLatin1(), + G_BUS_NAME_WATCHER_FLAGS_NONE, + QDBusObject::onServiceAppeared, + QDBusObject::onServiceFanished, + this, + NULL); + + setStatus(QDBusObject::Connecting); + } else { + qWarning() << "Invalid dbus connection args"; + } +} + +void QDBusObject::disconnect() +{ + if (m_status != QDBusObject::Disconnected) { + g_bus_unwatch_name (m_watchId); + m_watchId = 0; + setStatus(QDBusObject::Disconnected); + } +} + +void QDBusObject::onServiceAppeared(GDBusConnection *connection, const gchar *, const gchar *, gpointer data) +{ + QDBusObject *self = reinterpret_cast(data); + qDebug() << "service appear"; + + self->setStatus(QDBusObject::Connected); + self->serviceAppear(connection); +} + +void QDBusObject::onServiceFanished(GDBusConnection *connection, const gchar *, gpointer data) +{ + QDBusObject *self = reinterpret_cast(data); + qDebug() << "service disappear"; + + self->setStatus(QDBusObject::Connecting); + self->serviceVanish(connection); +} diff --git a/src/common/qdbusobject.h b/src/common/qdbusobject.h new file mode 100644 index 0000000..a2dc91c --- /dev/null +++ b/src/common/qdbusobject.h @@ -0,0 +1,65 @@ +#ifndef QDBUSOBJECT_H +#define QDBUSOBJECT_H + +#include + +#include + +class QDBusObject +{ +public: + enum BusType { + None = 0, + SessionBus, + SystemBus, + LastBusType + }; + + enum ConnectionStatus { + Disconnected = 0, + Connecting, + Connected + }; + + QDBusObject(); + ~QDBusObject(); + + BusType busType() const; + void setBusType(BusType type); + + QString busName() const; + void setBusName(const QString &busName); + + QString objectPath() const; + void setObjectPath(const QString &busName); + + ConnectionStatus status() const; + + void connect(); + void disconnect(); + +protected: + virtual void serviceAppear(GDBusConnection *connection) = 0; + virtual void serviceVanish(GDBusConnection *connection) = 0; + + // notify functions + virtual void busTypeChanged(BusType type) = 0; + virtual void busNameChanged(const QString &busNameChanged) = 0; + virtual void objectPathChanged(const QString &objectPath) = 0; + virtual void statusChanged(ConnectionStatus status) = 0; + +private: + guint m_watchId; + BusType m_busType; + QString m_busName; + QString m_objectPath; + ConnectionStatus m_status; + + void setStatus(ConnectionStatus status); + + // glib slots + static void onServiceAppeared(GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer data); + static void onServiceFanished(GDBusConnection *connection, const gchar *name, gpointer data); +}; + +#endif diff --git a/src/common/qmenumodel.cpp b/src/common/qmenumodel.cpp new file mode 100644 index 0000000..6e7483c --- /dev/null +++ b/src/common/qmenumodel.cpp @@ -0,0 +1,241 @@ +#include "qmenumodel.h" + +#include + +QMenuModel::QMenuModel(GMenuModel *other, QObject *parent) + : QAbstractListModel(parent), + m_menuModel(0), + m_signalChangedId(0) +{ + static QHash rolesNames; + if (rolesNames.empty()) { + rolesNames[Action] = "action"; + rolesNames[Label] = "label"; + rolesNames[LinkSection] = "linkSection"; + rolesNames[LinkSubMenu] = "linkSubMenu"; + rolesNames[Extra] = "extra"; + } + setRoleNames(rolesNames); + setMenuModel(other); +} + +QMenuModel::~QMenuModel() +{ + setMenuModel(NULL); +} + +void QMenuModel::setMenuModel(GMenuModel *other) +{ + if (m_menuModel == other) { + return; + } + + beginResetModel(); + + if (m_menuModel) { + g_signal_handler_disconnect(m_menuModel, m_signalChangedId); + m_signalChangedId = 0; + g_object_unref(m_menuModel); + } + + m_menuModel = other; + + if (m_menuModel) { + // this will trigger the menu load + (void) g_menu_model_get_n_items(m_menuModel); + //qDebug() << "Menu size:" << g_menu_model_get_n_items(m_menuModel); + m_signalChangedId = g_signal_connect(m_menuModel, + "items-changed", + G_CALLBACK(QMenuModel::onItemsChanged), + this); + } + + endResetModel(); +} + +GMenuModel *QMenuModel::menuModel() const +{ + return m_menuModel; +} + +/* QAbstractItemModel */ +int QMenuModel::columnCount(const QModelIndex &) const +{ + return 1; +} + +QVariant QMenuModel::data(const QModelIndex &index, int role) const +{ + QVariant attribute; + int rowCountValue = rowCount(); + + if ((rowCountValue > 0) && (index.row() >= 0) && (index.row() < rowCountValue)) { + if (m_menuModel) { + switch (role) { + case Action: + attribute = getStringAttribute(index, G_MENU_ATTRIBUTE_ACTION); + break; + case Label: + attribute = getStringAttribute(index, G_MENU_ATTRIBUTE_LABEL); + break; + case LinkSection: + attribute = getLink(index, G_MENU_LINK_SECTION); + break; + case LinkSubMenu: + attribute = getLink(index, G_MENU_LINK_SUBMENU); + break; + case Extra: + attribute = getExtraProperties(index); + break; + default: + break; + } + } + } + return attribute; +} + +QModelIndex QMenuModel::parent(const QModelIndex &index) const +{ + return QModelIndex(); +} + +int QMenuModel::rowCount(const QModelIndex &) const +{ + if (m_menuModel) { + return g_menu_model_get_n_items(m_menuModel); + } + return 0; +} + +QVariant QMenuModel::getStringAttribute(const QModelIndex &index, + const QString &attribute) const +{ + QVariant result; + gchar* value = NULL; + g_menu_model_get_item_attribute(m_menuModel, + index.row(), + attribute.toLatin1(), + "s", &value); + if (value) { + result = QVariant(QString::fromLatin1(value)); + g_free(value); + } + return result; +} + +QVariant QMenuModel::getLink(const QModelIndex &index, + const QString &linkName) const +{ + GMenuModel *link; + + link = g_menu_model_get_item_link(m_menuModel, + index.row(), + linkName.toLatin1()); + + if (link) { + QMenuModel *other = new QMenuModel(link, const_cast(this)); + return QVariant::fromValue(other); + } + + return QVariant(); +} + +QVariant QMenuModel::getExtraProperties(const QModelIndex &index) const +{ + GMenuAttributeIter *iter = g_menu_model_iterate_item_attributes(m_menuModel, index.row()); + if (iter == NULL) { + return QVariant(); + } + + QObject *extra = new QObject(const_cast(this)); + const gchar *attrName = NULL; + GVariant *value = NULL; + while (g_menu_attribute_iter_get_next (iter, &attrName, &value)) { + qDebug() << "Set property:" << attrName; + if (strncmp("x-", attrName, 2) == 0) { + extra->setProperty(attrName, parseGVariant(value)); + } + } + + return QVariant::fromValue(extra); +} + + +void QMenuModel::onItemsChanged(GMenuModel *, + gint position, + gint removed, + gint added, + gpointer data) +{ + QMenuModel *self = reinterpret_cast(data); + //qDebug() << "Item Changed" << position << removed << added; + + if (removed > 0) { + self->beginRemoveRows(QModelIndex(), position, position + removed - 1); + self->endRemoveRows(); + } + + if (added > 0) { + self->beginInsertRows(QModelIndex(), position, position + added - 1); + self->endInsertRows(); + } +} + +QVariant QMenuModel::parseGVariant(GVariant *value) +{ + QVariant result; + if (value == NULL) { + return result; + } + + const GVariantType *type = g_variant_get_type(value); + if (g_variant_type_equal(type, G_VARIANT_TYPE_BOOLEAN)) { + result.setValue((bool)g_variant_get_boolean(value)); + } else if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTE)) { + result.setValue(g_variant_get_byte(value)); + } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT16)) { + result.setValue(g_variant_get_int16(value)); + } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT16)) { + result.setValue(g_variant_get_uint16(value)); + } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT32)) { + result.setValue(g_variant_get_int32(value)); + } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT32)) { + result.setValue(g_variant_get_uint32(value)); + } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT64)) { + result.setValue(g_variant_get_int64(value)); + } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT64)) { + result.setValue(g_variant_get_uint64(value)); + } else if (g_variant_type_equal(type, G_VARIANT_TYPE_DOUBLE)) { + result.setValue(g_variant_get_double(value)); + } else if (g_variant_type_equal(type, G_VARIANT_TYPE_STRING)) { + gsize size = 0; + const gchar *v = g_variant_get_string(value, &size); + result.setValue(QString::fromLatin1(v, size)); + } else { + qWarning() << "Unsupported GVariant value"; + } + + /* TODO: implement convertions to others types + * G_VARIANT_TYPE_HANDLE + * G_VARIANT_TYPE_OBJECT_PATH + * G_VARIANT_TYPE_SIGNATURE + * G_VARIANT_TYPE_VARIANT + * G_VARIANT_TYPE_ANY + * G_VARIANT_TYPE_BASIC + * G_VARIANT_TYPE_MAYBE + * G_VARIANT_TYPE_ARRAY + * G_VARIANT_TYPE_TUPLE + * G_VARIANT_TYPE_UNIT + * G_VARIANT_TYPE_DICT_ENTRY + * G_VARIANT_TYPE_DICTIONARY + * G_VARIANT_TYPE_STRING_ARRAY + * G_VARIANT_TYPE_BYTESTRING + * G_VARIANT_TYPE_OBJECT_PATH_ARRAY + * G_VARIANT_TYPE_BYTESTRING_ARRAY + * G_VARIANT_TYPE_VARDICT + */ + + return result; +} + diff --git a/src/common/qmenumodel.h b/src/common/qmenumodel.h new file mode 100644 index 0000000..f1e0b61 --- /dev/null +++ b/src/common/qmenumodel.h @@ -0,0 +1,45 @@ +#ifndef QMENUMODEL_H +#define QMENUMODEL_H + +#include +#include + +class QMenuModel : public QAbstractListModel +{ + Q_OBJECT + +public: + enum MenuRoles { + Action = 0, + Label, + LinkSection, + LinkSubMenu, + Extra + }; + + ~QMenuModel(); + + /* QAbstractItemModel */ + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QModelIndex parent (const QModelIndex &index) const; + int rowCount(const QModelIndex &parent = QModelIndex()) const; + +protected: + QMenuModel(GMenuModel *other=0, QObject *parent=0); + void setMenuModel(GMenuModel *model); + GMenuModel *menuModel() const; + +private: + GMenuModel *m_menuModel; + guint m_signalChangedId; + + QVariant getStringAttribute(const QModelIndex &index, const QString &attribute) const; + QVariant getLink(const QModelIndex &index, const QString &linkName) const; + QVariant getExtraProperties(const QModelIndex &index) const; + + static void onItemsChanged(GMenuModel *model, gint position, gint removed, gint added, gpointer data); + static QVariant parseGVariant(GVariant *value); +}; + +#endif diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..96c4bad --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,2 @@ +add_subdirectory(script) +add_subdirectory(client) diff --git a/tests/client/CMakeLists.txt b/tests/client/CMakeLists.txt new file mode 100644 index 0000000..bf77461 --- /dev/null +++ b/tests/client/CMakeLists.txt @@ -0,0 +1,46 @@ +macro(declare_test testname) + set(TEST_MOC_FILE ${CMAKE_CURRENT_BINARY_DIR}/${testname}.moc) + qt4_generate_moc(${testname}.cpp ${TEST_MOC_FILE}) + + add_executable(${testname} ${testname}.cpp ${TEST_MOC_FILE}) + target_link_libraries(${testname} + qmenumodelcommon + dbusmenuscript + ${QT_QTTEST_LIBRARY} + ${QT_QTCORE_LIBRARY} + ${QT_QTGUI_LIBRARY} + ${QT_QTDBUS_LIBRARY} + ${GLIB_LDFLAGS} + ${GIO_LDFLAGS}) + + add_test(${testname} + ${DBUS_RUNNER} + --task ${CMAKE_CURRENT_BINARY_DIR}/${testname} --task-name Client + --task ${CMAKE_CURRENT_SOURCE_DIR}/script_${testname}.py --task-name Server + --ignore-return) + set_tests_properties(${testname} PROPERTIES + TIMEOUT ${CTEST_TESTING_TIMEOUT} + ENVIRONMENT "PYTHONPATH=${TEST_PYTHONPATH}") + +endmacro(declare_test testname) + +include_directories(${qmenumodelcommon_SOURCE_DIR} + ${dbusmenuscript_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} + ${QT_INCLUDE_DIR} + ${QT_QTTEST_INCLUDE_DIR} + ${QT_QTCORE_INCLUDE_DIR} + ${QT_QTGUI_INCLUDE_DIR} + ${QT_QTDBUS_INCLUDE_DIR} + ${GLIB_INCLUDE_DIRS} + ${GIO_INCLUDE_DIRS}) + +add_definitions(-DTEST_SUITE) +set(TEST_PYTHONPATH ${dbusmenuscript_SOURCE_DIR}) +if(NOT CTEST_TESTING_TIMEOUT) + set(CTEST_TESTING_TIMEOUT 60) +endif() + +declare_test(servicetest) +declare_test(menuchangestest) +declare_test(modeltest) diff --git a/tests/client/menuchangestest.cpp b/tests/client/menuchangestest.cpp new file mode 100644 index 0000000..7e6379c --- /dev/null +++ b/tests/client/menuchangestest.cpp @@ -0,0 +1,65 @@ +#include "qdbusmenumodel.h" +#include "dbusmenuscript.h" + +#include +#include +#include +#include + +class MenuChangesTest : public QObject +{ + Q_OBJECT +private: + DBusMenuScript m_script; + QDBusMenuModel m_model; + +private Q_SLOTS: + void initTestCase() + { + Q_ASSERT(m_script.connect()); + } + + void cleanupTestCase() + { + m_script.quit(); + } + + void init() + { + m_model.stop(); + m_model.setBusType(QDBusObject::SessionBus); + m_model.setBusName(MENU_SERVICE_NAME); + m_model.setObjectPath(MENU_OBJECT_PATH); + } + + void cleanup() + { + m_script.unpublishMenu(); + } + + void testMenuItemAppend() + { + m_script.publishMenu(); + m_model.start(); + + // Create first Item + m_script.walk(); + QCOMPARE(m_model.rowCount(), 1); + + // Create second item + m_script.walk(); + QCOMPARE(m_model.rowCount(), 2); + + // Remove item0 + m_script.walk(); + QCOMPARE(m_model.rowCount(), 1); + + // Remove item1 + m_script.walk(); + QCOMPARE(m_model.rowCount(), 0); + } +}; + +QTEST_MAIN(MenuChangesTest) + +#include "menuchangestest.moc" diff --git a/tests/client/modeltest.cpp b/tests/client/modeltest.cpp new file mode 100644 index 0000000..1963327 --- /dev/null +++ b/tests/client/modeltest.cpp @@ -0,0 +1,185 @@ +#include "qdbusmenumodel.h" +#include "dbusmenuscript.h" + +#include +#include +#include +#include + +class ModelTest : public QObject +{ + Q_OBJECT +private: + DBusMenuScript m_script; + QDBusMenuModel m_model; + +private Q_SLOTS: + void initTestCase() + { + Q_ASSERT(m_script.connect()); + } + + void cleanupTestCase() + { + m_script.quit(); + } + + void init() + { + m_model.stop(); + m_model.setBusType(QDBusObject::SessionBus); + m_model.setBusName(MENU_SERVICE_NAME); + m_model.setObjectPath(MENU_OBJECT_PATH); + } + + void cleanup() + { + m_script.unpublishMenu(); + } + + void testColumnCount() + { + QCOMPARE(m_model.columnCount(), 1); + } + + void testParent() + { + QCOMPARE(m_model.parent(QModelIndex()), QModelIndex()); + } + + void testBusTypeProperty() + { + m_model.setProperty("busType", 1); + QCOMPARE(m_model.busType(), QDBusObject::SessionBus); + + m_model.setProperty("busType", 2); + QCOMPARE(m_model.busType(), QDBusObject::SystemBus); + + m_model.setProperty("busType", 0); + QCOMPARE(m_model.busType(), QDBusObject::SystemBus); + + m_model.setProperty("busType", 10); + QCOMPARE(m_model.busType(), QDBusObject::SystemBus); + + } + + void testData() + { + // Make menu available + m_script.publishMenu(); + m_script.run(); + + // start model + m_model.start(); + + // Wait for dbus sync + QTest::qWait(500); + + QCOMPARE(m_model.status(), QDBusObject::Connected); + QCOMPARE(m_model.rowCount(), 4); + + // Label (String) + QVariant label = m_model.data(m_model.index(0, 0), QMenuModel::Label); + QVERIFY(label.isValid()); + QCOMPARE(label.type(), QVariant::String); + QCOMPARE(label.toString(), QString("Menu0")); + + // Action (String) + QVariant action = m_model.data(m_model.index(1, 0), QMenuModel::Action); + QVERIFY(action.isValid()); + QCOMPARE(action.type(), QVariant::String); + QCOMPARE(action.toString(), QString("Menu1Act")); + + // Section (QObject) + QVariant vSection = m_model.data(m_model.index(2, 0), QMenuModel::LinkSection); + QVERIFY(vSection.isValid()); + QMenuModel *section = qobject_cast(vSection.value()); + QVERIFY(section); + QCOMPARE(section->rowCount(), 2); + + // SubMenu (QObject) + QVariant vSubMenu = m_model.data(m_model.index(3, 0), QMenuModel::LinkSubMenu); + QVERIFY(vSubMenu.isValid()); + QMenuModel *submenu = qobject_cast(vSubMenu.value()); + QVERIFY(submenu); + + // Wait for menu load (submenus are loaded async) + QTest::qWait(500); + QCOMPARE(submenu->rowCount(), 2); + } + + void testExtraData() + { + // Make menu available + m_script.publishMenu(); + m_script.run(); + + // start model + m_model.start(); + + // Wait for dbus sync + QTest::qWait(500); + + QCOMPARE(m_model.status(), QDBusObject::Connected); + QCOMPARE(m_model.rowCount(), 4); + + QVariant e = m_model.data(m_model.index(0, 0), QMenuModel::Extra); + QVERIFY(e.isValid()); + QObject *extra = e.value(); + + // Boolean + QVariant v = extra->property("x-boolean"); + QCOMPARE(v.type(), QVariant::Bool); + QCOMPARE(v.toBool(), true); + + // Byte + v = extra->property("x-byte"); + QCOMPARE(v.typeName(), "uchar"); + QCOMPARE(v.value(), (uchar)42); + + // Int16 + v = extra->property("x-int16"); + QCOMPARE(v.typeName(), "short"); + QCOMPARE(v.value(), (short)-42); + + // UInt16 + v = extra->property("x-uint16"); + QCOMPARE(v.typeName(), "ushort"); + QCOMPARE(v.value(), (ushort)42); + + // Int32 + v = extra->property("x-int32"); + QCOMPARE(v.type(), QVariant::Int); + QCOMPARE(v.toInt(), -42); + + // UInt32 + v = extra->property("x-uint32"); + QCOMPARE(v.type(), QVariant::UInt); + QCOMPARE(v.toUInt(), (uint) 42); + + // Int64 + v = extra->property("x-int64"); + QCOMPARE(v.typeName(), "long"); + QCOMPARE(v.value(), (long) -42); + + // UInt64 + v = extra->property("x-uint64"); + QCOMPARE(v.typeName(), "ulong"); + QCOMPARE(v.value(), (ulong) 42); + + // Double + v = extra->property("x-double"); + QCOMPARE(v.type(), QVariant::Double); + QCOMPARE(v.toDouble(), 42.42); + + // String + v = extra->property("x-string"); + QCOMPARE(v.type(), QVariant::String); + QCOMPARE(v.toString(), QString("42")); + } + +}; + +QTEST_MAIN(ModelTest) + +#include "modeltest.moc" diff --git a/tests/client/script_menuchangestest.py b/tests/client/script_menuchangestest.py new file mode 100755 index 0000000..67f2ee0 --- /dev/null +++ b/tests/client/script_menuchangestest.py @@ -0,0 +1,14 @@ +#!/usr/bin/python2.7 + +import time +from menuscript import Script, ActionList, MENU_OBJECT_PATH + +al = ActionList(MENU_OBJECT_PATH) +al.appendItem("Menu0", "Menu0") +al.appendItem("Menu1", "Menu1") +al.removeItem(0) # remove Menu0 +al.removeItem(0) # remove Menu1 + +t = Script.create(al) +t.run() + diff --git a/tests/client/script_modeltest.py b/tests/client/script_modeltest.py new file mode 100755 index 0000000..db4e28b --- /dev/null +++ b/tests/client/script_modeltest.py @@ -0,0 +1,31 @@ +#!/usr/bin/python2.7 + +import time +from gi.repository import GLib +from menuscript import Script, ActionList, MENU_OBJECT_PATH + +al = ActionList(MENU_OBJECT_PATH) +al.appendItem("Menu0", "Menu0Act", None, None, {'x-boolean' : GLib.Variant('b', True), + 'x-byte' : GLib.Variant('y', 42), + 'x-int16' : GLib.Variant('n', -42), + 'x-uint16' : GLib.Variant('q', 42), + 'x-int32' : GLib.Variant('i', -42), + 'x-uint32' : GLib.Variant('u', 42), + 'x-int64' : GLib.Variant('x', -42), + 'x-uint64' : GLib.Variant('t', 42), + 'x-double' : GLib.Variant('d', 42.42), + 'x-string' : GLib.Variant('s', u'42') + }) +al.appendItem("Menu1", "Menu1Act") + +al.appendItem("Menu2", "Menu2Act", "section") +al.appendItem("Menu2.1", "Menu2.1Act", None, "2") +al.appendItem("Menu2.2", "Menu2.2Act", None, "2") + +al.appendItem("Menu3", "Menu3Act", "submenu") +al.appendItem("Menu3.1", "Menu3.1Act", None, "3") +al.appendItem("Menu3.2", "Menu3.2Act", None, "3") + +t = Script.create(al) +t.run() + diff --git a/tests/client/script_servicetest.py b/tests/client/script_servicetest.py new file mode 100755 index 0000000..385256c --- /dev/null +++ b/tests/client/script_servicetest.py @@ -0,0 +1,12 @@ +#!/usr/bin/python2.7 + +import time +from menuscript import Script, ActionList, MENU_OBJECT_PATH + +al = ActionList(MENU_OBJECT_PATH) +al.appendItem("Menu0", "Menu0") +al.appendItem("Menu1", "Menu1") + +t = Script.create(al) +t.run() + diff --git a/tests/client/servicetest.cpp b/tests/client/servicetest.cpp new file mode 100644 index 0000000..3faddd0 --- /dev/null +++ b/tests/client/servicetest.cpp @@ -0,0 +1,101 @@ +#include "qdbusmenumodel.h" +#include "dbusmenuscript.h" + +#include +#include +#include +#include + +class ServiceTest : public QObject +{ + Q_OBJECT +private: + DBusMenuScript m_script; + QDBusMenuModel m_model; + + void setupModel(QDBusMenuModel *model) + { + model->setBusType(QDBusObject::SessionBus); + model->setBusName(MENU_SERVICE_NAME); + model->setObjectPath(MENU_OBJECT_PATH); + } + +private Q_SLOTS: + void initTestCase() + { + Q_ASSERT(m_script.connect()); + } + + void cleanupTestCase() + { + m_script.quit(); + } + + void init() + { + qDebug() << "init>>>>>>>>>>>>>>>>>>>>>"; + m_model.stop(); + m_model.setBusType(QDBusObject::SessionBus); + m_model.setBusName(MENU_SERVICE_NAME); + m_model.setObjectPath(MENU_OBJECT_PATH); + } + + void cleanup() + { + qDebug() << "cleanup>>>>>>>>>>>>>>>>>>"; + m_script.unpublishMenu(); + } + + void testMenuStartStopWithNoService() + { + m_model.start(); + QCOMPARE(m_model.status(), QDBusObject::Connecting); + + m_model.stop(); + QCOMPARE(m_model.status(), QDBusObject::Disconnected); + } + + void testMenuStartStopWithService() + { + // Make menu available + m_script.publishMenu(); + + // start model + m_model.start(); + + // Wait for dbus sync + QTest::qWait(500); + + QCOMPARE(m_model.status(), QDBusObject::Connected); + + // Diconnect model + m_model.stop(); + QCOMPARE(m_model.status(), QDBusObject::Disconnected); + } + + void testMenuServiceAppearAndDissapear() + { + m_model.start(); + QCOMPARE(m_model.status(), QDBusObject::Connecting); + + QSignalSpy spy(&m_model, SIGNAL(statusChanged(QDBusObject::ConnectionStatus))); + + // Make menu available + m_script.publishMenu(); + + // singal changed to connected + QCOMPARE(spy.count(), 1); + QCOMPARE(m_model.status(), QDBusObject::Connected); + + // remove menu service + m_script.unpublishMenu(); + + // signal changed to connecting + QCOMPARE(spy.count(), 2); + QCOMPARE(m_model.status(), QDBusObject::Connecting); + } +}; + +QTEST_MAIN(ServiceTest) + +#include "servicetest.moc" diff --git a/tests/script/CMakeLists.txt b/tests/script/CMakeLists.txt new file mode 100644 index 0000000..11af44e --- /dev/null +++ b/tests/script/CMakeLists.txt @@ -0,0 +1,34 @@ +project(dbusmenuscript) + +set(DBUSMENUSCRIPT_SRC + dbusmenuscript.cpp +) + +set(DBUSMENUSCRIPT_HEADERS + dbusmenuscript.h +) + +qt4_wrap_cpp(DBUSMENUSCRIPT_MOC + ${DBUSMENUSCRIPT_HEADERS} +) + +add_library(dbusmenuscript STATIC + ${DBUSMENUSCRIPT_SRC} + ${DBUSMENUSCRIPT_MOC} +) + +set_target_properties(dbusmenuscript PROPERTIES COMPILE_FLAGS -fPIC) + +include_directories( + ${CMAKE_CURRENT_SOURCE_DIR} + ${QT_INCLUDE_DIR} + ${QT_QTTEST_INCLUDE_DIR} + ${QT_QTCORE_INCLUDE_DIR} + ${QT_QTDBUS_INCLUDE_DIR} +) + +target_link_libraries(dbusmenuscript + ${QT_QTCORE_LIBRARY} + ${QT_QTTEST_LIBRARY} + ${QT_QTDBUS_LIBRARY} +) diff --git a/tests/script/dbusmenuscript.cpp b/tests/script/dbusmenuscript.cpp new file mode 100644 index 0000000..cc332f7 --- /dev/null +++ b/tests/script/dbusmenuscript.cpp @@ -0,0 +1,75 @@ + +#include "dbusmenuscript.h" +#include +#include +#include + +#define WAIT_TIMEOUT 500 + +DBusMenuScript::DBusMenuScript() + :m_script(0) +{ +} + +DBusMenuScript::~DBusMenuScript() +{ + quit(); +} + +bool DBusMenuScript::connect() +{ + QTest::qWait(WAIT_TIMEOUT); + m_script = new QDBusInterface(SCRIPT_SERVICE_NAME, + SCRIPT_OBJECT_PATH, + SCRIPT_INTERFACE_NAME, + QDBusConnection::sessionBus(), 0); + if (m_script && m_script->isValid()) { + return true; + } else { + qWarning() << "DBUS ERROR:" << m_script->lastError().message(); + return false; + } +} + +void DBusMenuScript::publishMenu() +{ + if (m_script) { + m_script->call("publishMenu"); + QTest::qWait(WAIT_TIMEOUT); + } +} + +void DBusMenuScript::unpublishMenu() +{ + if (m_script) { + m_script->call("unpublishMenu"); + QTest::qWait(WAIT_TIMEOUT); + } +} + + +void DBusMenuScript::quit() +{ + if (m_script) { + m_script->call("quit"); + QTest::qWait(WAIT_TIMEOUT); + delete m_script; + m_script = 0; + } +} + +void DBusMenuScript::walk(int steps) +{ + if (m_script) { + m_script->call("walk", steps); + QTest::qWait(WAIT_TIMEOUT); + } +} + +void DBusMenuScript::run() +{ + if (m_script) { + m_script->call("walk", -1); + QTest::qWait(WAIT_TIMEOUT); + } +} diff --git a/tests/script/dbusmenuscript.h b/tests/script/dbusmenuscript.h new file mode 100644 index 0000000..3d0d3c9 --- /dev/null +++ b/tests/script/dbusmenuscript.h @@ -0,0 +1,35 @@ +#ifndef DBUSMENUSCRIPT_H +#define DBUSMENUSCRITP_H + +#include +#include +#include + +#define SCRIPT_SERVICE_NAME "com.canonical.test" +#define SCRIPT_OBJECT_PATH "/com/canonical/test/menuscript" +#define SCRIPT_INTERFACE_NAME "com.canonical.test.menuscript" + +#define MENU_SERVICE_NAME SCRIPT_SERVICE_NAME ".menu" +#define MENU_OBJECT_PATH SCRIPT_OBJECT_PATH "/menu" + + +class DBusMenuScript +{ +public: + DBusMenuScript(); + ~DBusMenuScript(); + + bool connect(); + void quit(); + + void walk(int steps = 1); + void run(); + + void publishMenu(); + void unpublishMenu(); + +private: + QDBusInterface *m_script; +}; + +#endif diff --git a/tests/script/menuscript.py b/tests/script/menuscript.py new file mode 100644 index 0000000..05e226c --- /dev/null +++ b/tests/script/menuscript.py @@ -0,0 +1,172 @@ +import dbus +import dbus.service +from dbus import glib +from dbus.mainloop.glib import DBusGMainLoop +from gi.repository import GObject +from gi.repository import GLib +from gi.repository import Gio + +SERVICE_NAME = "com.canonical.test" +INTERFACE_NAME = "com.canonical.test.menuscript" +OBJECT_PATH = "/com/canonical/test/menuscript" +MENU_SERVICE_NAME= SERVICE_NAME + ".menu" +MENU_OBJECT_PATH = OBJECT_PATH + "/menu" +bus = None + +class Script(dbus.service.Object): + def __init__(self, aList, session, object_path): + dbus.service.Object.__init__(self, session, object_path) + self._list = aList + self._session = session + + def run(self): + self._loop = GObject.MainLoop() + self._loop.run() + + @dbus.service.method(dbus_interface=INTERFACE_NAME, + in_signature='', out_signature='', + sender_keyword='sender') + def publishMenu(self, sender=None): + self._list.start() + + @dbus.service.method(dbus_interface=INTERFACE_NAME, + in_signature='', out_signature='', + sender_keyword='sender') + def unpublishMenu(self, sender=None): + self._list.stop() + + @dbus.service.method(dbus_interface=INTERFACE_NAME, + in_signature='', out_signature='', + sender_keyword='sender') + def quit(self, sender=None): + self.unpublishMenu(); + self._loop.quit() + + @dbus.service.method(dbus_interface=INTERFACE_NAME, + in_signature='i', out_signature='', + sender_keyword='sender') + def walk(self, steps, sender=None): + if steps == -1 or steps > self._list.size(): + steps = self._list.size() + + while(steps > 0): + self._list.walk() + steps -= 1 + + @staticmethod + def create(aList): + global bus + + GObject.threads_init() + glib.threads_init() + + dbus_loop = DBusGMainLoop() + bus = dbus.SessionBus(mainloop=dbus_loop) + bus_name = dbus.service.BusName(SERVICE_NAME, bus=bus) + return Script(aList, bus_name, OBJECT_PATH) + +class Action(object): + def __init__(self, aList, action, **kwargs): + self._list = aList + self._action = action + self._kargs = kwargs + + def setProperties(self, menu, props): + if props: + for key in props: + menu.set_attribute_value(key, props[key]) + + def appendItem(self): + parentId = self._kargs['parentId'] + if parentId and len(parentId): + parent = self._list.getMenu(parentId)[0] + else: + parent = self._list._root + + if self._kargs['link'] == None: + item = Gio.MenuItem.new(self._kargs['label'], self._kargs['actionName']) + self.setProperties(item, self._kargs['properties']) + parent.append_item(item) + elif self._kargs['link'] == 'section': + section = Gio.Menu() + parent.append_section(self._kargs['label'], section) + elif self._kargs['link'] == 'submenu': + submenu = Gio.Menu() + parent.append_submenu(self._kargs['label'], submenu) + + def removeItem(self): + menuId = self._kargs['menuId'] + (menu, mId) = self._list.getMenu(menuId) + if mId != -1: + menu.remove(mId) + else: + print "Remove menu item" + + def run(self): + if self._action == 'append': + self.appendItem() + elif self._action == 'remove': + self.removeItem() + +class ActionList(object): + def __init__(self, objectPath): + self._actions = [] + self._objectPath = objectPath + self._bux = None + self._exportID = None + self._ownNameID = None + self._root = None + + def appendItem(self, label, actionName, link=None, parentId=None, properties=None): + self._actions.append(Action(self, 'append', + parentId=parentId, + label=label, + actionName=actionName, + link=link, + properties=properties)) + + def removeItem(self, menuId): + self._actions.append(Action(self, 'remove', + menuId=menuId)) + + def _findMenu(self, root, ids): + if len(ids) == 0: + return (root, -1) + + currentId = int(ids[0]) + link = root.get_item_link(currentId, 'section') + if link == None: + link = root.get_item_link(currentId, 'submenu') + + if link: + return self._findMenu(link, ids[1:]) + else: + return (root, currentId) + + def getMenu(self, menuId): + return self._findMenu(self._root, str(menuId).split('.')); + + def walk(self): + item = self._actions.pop(0) + item.run() + + def size(self): + return len(self._actions) + + def _exportService(self, connection, name): + self._root = Gio.Menu() + self._bus = connection + self._exportID = connection.export_menu_model(MENU_OBJECT_PATH, self._root) + + def start(self): + self._ownNameID = Gio.bus_own_name(2, MENU_SERVICE_NAME, 0, self._exportService, None, None) + + def stop(self): + if self._exportID: + self._bus.unexport_menu_model(self._exportID) + self._exportID = None + + if self._ownNameID: + Gio.bus_unown_name(self._ownNameID) + self._ownNameID = None + -- cgit v1.2.3