qmenumodel-0.9.1/AUTHORS0000644000000000000000000000262214200511032011613 0ustar Albert Astals Albert Astals Cid Alberto Mardegan Antti Kaijanmäki Bileto Bot Bill Filler Charles Kerr CI Train Bot <${CITRAIN_USER}@canonical.com> CI Train Bot Dalton Durst Florian Boucault Florian Leeber Gustavo Pichorim Boiko Ivan Semkin Lars Uebernickel Luca Weiss Lukáš Tinkl Marco Trevisan (Treviño) Marcus Tomlinson Marius Gripsgard Marius Gripsgard Mathieu Trudel-Lapierre Michael Terry Michał Sawicz Mike Gabriel Mirco Müller Nick Dedekind Nick Dedekind Olivier Tilloy Pete Woods Renato Araujo Oliveira Filho Ricardo Mendoza Robert Tari qmenumodel-0.9.1/.build.yml0000644000000000000000000000703114200511032012442 0ustar ########################################################## # THE FOLLOWING LINES IS USED BY docker-build ########################################################## requires: # Useful URL: https://aur.archlinux.org/cgit/aur.git/tree/PKGBUILD?h=qmenumodel # FIXME: qmenumodel has not been packaged for Archlinux, yet. archlinux: - clang - gcc - git - make - startup-notification - which - cmake - cmake-extras - mate-common # FIXME (Archlinux build requirements) debian: # Useful URL: https://salsa.debian.org/debian-ayatana-team/qmenumodel - autopoint - clang - clang-tools - g++ - cppcheck - git - cmake - cmake-extras - mate-common - libglib2.0-dev - qt5-qmake - qtbase5-dev - qtdeclarative5-dev - python3 - python3-dbus - python3-gi - gir1.2-glib-2.0 - dbus-test-runner - libgl-dev ubuntu: - autopoint - clang - clang-tools - g++ - cppcheck - git - cmake - cmake-extras - mate-common - libglib2.0-dev - qt5-qmake - qtbase5-dev - qtdeclarative5-dev - python3 - python3-dbus - python3-gi - gir1.2-glib-2.0 - dbus-test-runner - libgl-dev variables: - 'CHECKERS=" -enable-checker deadcode.DeadStores -enable-checker alpha.deadcode.UnreachableCode -enable-checker alpha.core.CastSize -enable-checker alpha.core.CastToStruct -enable-checker alpha.core.IdenticalExpr -enable-checker alpha.core.SizeofPtr -enable-checker alpha.security.ArrayBoundV2 -enable-checker alpha.security.MallocOverflow -enable-checker alpha.security.ReturnPtrRange -enable-checker alpha.unix.SimpleStream -enable-checker alpha.unix.cstring.BufferOverlap -enable-checker alpha.unix.cstring.NotNullTerminated -enable-checker alpha.unix.cstring.OutOfBounds -enable-checker alpha.core.FixedAddr -enable-checker security.insecureAPI.strcpy"' build_scripts: - if [ ${DISTRO_NAME} == "debian" ];then - cppcheck --enable=warning,style,performance,portability,information,missingInclude . - fi - - if [ -e ./autogen.sh ]; then - NOCONFIGURE=1 ./autogen.sh - scan-build $CHECKERS ./configure --prefix=/usr --enable-gtk-doc --enable-compile-warnings=maximum - elif [ -e ./CMakeLists.txt ]; then - if [ ${DISTRO_NAME} == "debian" ];then - scan-build $CHECKERS cmake . -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_WERROR=ON -DENABLE_TESTS=ON - else - scan-build $CHECKERS cmake . -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_VERBOSE_MAKEFILE=ON -DENABLE_WERROR=ON - fi - else - exit 1 - fi - - if [ $CPU_COUNT -gt 1 ]; then - if [ ${DISTRO_NAME} == "debian" ];then - scan-build $CHECKERS --keep-cc --use-cc=clang --use-c++=clang++ -o html-report make -j $CPU_COUNT - make clean - fi - scan-build $CHECKERS --keep-cc -o html-report make -j $CPU_COUNT - else - if [ ${DISTRO_NAME} == "debian" ];then - scan-build $CHECKERS --keep-cc --use-cc=clang --use-c++=clang++ -o html-report make - make clean - fi - scan-build $CHECKERS --keep-cc -o html-report make - fi after_scripts: - if [ ${BUILD_TYPE} == "scripts" ];then - XVFB_RUN="$(which xvfb-run || true)" - if [ ${DISTRO_NAME} == "debian" ];then - if [ -e ./autogen.sh ]; then - ${XVFB_RUN} make check - elif [ -e ./CMakeLists.txt ]; then - ${XVFB_RUN} make test - fi - fi - fi qmenumodel-0.9.1/ChangeLog0000644000000000000000000014225014200511032012317 0ustar 2022-02-08 Mike Gabriel * release 0.9.1 (HEAD -> main, tag: 0.9.1) 2022-02-08 Robert Tari * .travis.yml: Run CI builds on Travis CI's Ubuntu focal base system + disable ppc64el builds (5e46aa7) 2022-02-01 Mike Gabriel * Merge branch 'tari01-pr/cleanup-compile-flags' (45da057) 2022-01-13 Robert Tari * .build.yml: Drop extra compilation flags and build with -Werror (f441fab) * CMakeLists.txt: Clean up compilation flags (59283b0) * Fix all build warnings (d23a290) 2022-02-01 Mike Gabriel * Merge branch 'correct-1-0' (7f5119f) 2021-12-23 Dalton Durst * Make the qmltests test imports, at least (03a23b0) * Fix build warning regarding qchar (fbb94cd) * Fix naming of AyatanaMenuModel qml type (0333149) * Fix versioning in examples and tests (02c973a) 2022-02-01 Mike Gabriel * Merge branch 'tari01-pr/ayatana-style-cmake' (cc2c2d9) 2021-12-15 Robert Tari * CMakeLists.txt: Rewrite and enable Ayatana-style options (8e228ca) 2021-12-14 Mike Gabriel * release 0.9.0 (4c27ec6) (tag: 0.9.0) * Merge branch 'tari01-pr/qml-1-0' (0924c28) 2021-12-08 Mike Gabriel * debian/{control,*qmenumodel*}: Rename bin:pkg qmenumodel-qml to qml-module-qmenumodel1 (reflecting the version bump _and_ becoming compliant to Debian's naming policy for QML modules. (efb9bed) 2021-12-02 Robert Tari * debian/qmenumodel-qml.install: Update QMenuModel name (a22b26b) * Bump QMenuModel to 1.0 (8fda779) 2021-12-10 Robert Tari * Fix CMake version and add required test packages (6abf908) 2021-12-08 Mike Gabriel * Merge branch 'tari01-pr/fix-qt5-use-modules-warning' (66af742) 2021-12-07 Robert Tari * Fix qt5_use_modules warning (af5a575) 2021-11-18 Mike Gabriel * Merge branch 'tari01-pr/bump-std-version' (5b84c10) 2021-11-18 Robert Tari * CMakeLists.txt: Stop enforcing C++11 standard. (621d577) 2021-11-18 Mike Gabriel * Merge branch 'tari01-pr/bump-soversion' (a817c0d) 2021-11-18 Robert Tari * debian/*: Update libqmenumodel0 to libqmenumodel1 (6a0f949) * libqmenumodel/src/CMakeLists.txt: Bump the SOVERSION of libqmenumodel (f7f503f) * Merge branch 'sunweaver-pr/amend-version-in-pkgconfig-file' (d9d7b02) 2021-11-18 Mike Gabriel * libqmenumodel/src/qmenumodel.pc.in: Use @PROJECT_VERSION@ for Version: field. (79516ac) 2021-11-17 Mike Gabriel * Merge branch 'tari01-pr/rename-unity-symbols' (62a8b8b) 2021-11-16 Robert Tari * Rename Unity* symbols (f4e75e1) * Rename unity* source files (f803689) 2021-11-17 Mike Gabriel * Merge branch 'tari01-pr/rename-canonical-properties' (8089cd5) 2021-11-15 Robert Tari * Rename com.ubuntu.menu (0d2c880) * examples/exportmenu.py: Rename x-canonical-currentvalue (a4f024e) * examples/unityqmlmenumodel.qml: Rename com.canonical.indicator.sound (f89d653) * libqmenumodel/QMenuModel/plugin.h: Rename com.canonical.qmenumodel (9e464fb) * Rename com.canonical.test* (e728156) 2021-11-13 Robert Tari * Rename com.canonical.unity.slider (7bb1253) 2021-10-24 Mike Gabriel * release 0.8.1 (7f41ec3) (tag: 0.8.1) 2021-09-09 Robert Tari * Merge branch 'sunweaver-pr/use-ayatana-indicators-dev-scripts' (8df381c) 2021-08-29 Mike Gabriel * debian/{control,compat}: Bump to level 10. (dec144a) * debian/rules: Enable unit tests. (55d51db) * debian/{control,compat}: Bump to level 10. (2aec4a4) * debian/rules: Enable unit tests. (47cac22) 2021-08-28 Mike Gabriel * .travis.yml: Use Ayatana Indicators project's dev-scripts repo. (5e12d85) 2021-06-11 Mike Gabriel * debian/control: Ignore missing B-D bin:pkg 'qt5-default'. It has been dropped during the Debian bullseye release cycle. (fb58fcc) 2021-05-17 Mike Gabriel * Merge branch 'z3ntu-glib' (ddcccb4) 2021-04-04 Luca Weiss * Move glib includes outside of extern "C" (a6d79e8) 2021-05-17 Robert Tari * Merge branch 'sunweaver-pr/travis-ci' (792a56d) 2021-05-12 Mike Gabriel * Travis CI: Initial draft for CI builds. (184eb5c) 2020-08-18 Mike Gabriel * release 0.8.0 (095e205) (tag: 0.8.0) 2020-08-15 Marius Gripsgard * Merge branch 'sunweaver-pr/move-over-to-ayatana-indicators' (566e0e1) 2020-08-15 Mike Gabriel * CMakeLists.txt: Introduce shipping PROJECT_VERSION in upstream code (not just in debian/changelog, which gets removed during tarball creation). (2361b98) * Move over qmenumodel into the Ayatana Indicators project, upstream version bump preparation. (8954090) * debian/control: Add myself to Uploaders: field. (617c181) * Merge branch 'ayatana-type' (a05e8b1) 2020-08-15 Marius Gripsgard * Support ayatana type (fa430b5) * Use CoverageReport from cmake-extras (a17ce34) * Correct debian control file (3c9b1ce) * Cleanup bzr (0e71197) * Merge branch 'xenial_-_patches' of https://github.com/z3ntu/qmenumodel (e21000a) 2020-05-18 Luca Weiss * Remove use of the deprecated QModelIndex::child function (b420ba1) * Add missing header (bca9dd3) 2018-07-27 Florian Leeber * Merge pull request #2 from vanyasem/master (30e3dc5) 2018-06-20 Ivan Semkin * Fix build with Qt 5.11_beta3 (dropping qt5_use_modules) (276a033) 2017-12-27 Marius Gripsgard * Build for xenial and bump version (f236743) * Add Jenkinsfile (3463ebe) 2017-04-04 Bileto Bot * Releasing 0.2.12+17.04.20170404-0ubuntu1 (760d505) 2017-04-04 Albert Astals Cid * Add a return true (05133b2) 2017-04-03 Albert Astals Cid * Add a return true (0fd82be) 2017-03-16 Bileto Bot * Releasing 0.2.12+17.04.20170316.1-0ubuntu1 (42ba9cd) 2017-03-16 Albert Astals Cid * Use submenu-enabled for those items that don't have an action (if it exists) (LP: #1670694) (1e6ebcc) 2017-03-16 Bileto Bot * Add UnityMenuModel::aboutToShow ((LP: 1664578) (f0aa8c3) 2017-03-16 Albert Astals Cid * Batch add and removes into the model (e2b335b) * 0 -> G_MAXINT as found by Charles (5c4aae1) * Fix c&p, this ref should be unref (4216eb7) * Review improvements (8c08013) 2017-03-08 Albert Astals Cid * Use submenu-enabled for those items that don't have an action (if it exists) (c4855ff) 2017-03-07 Albert Astals Cid * Increase version because of new api (4e10e84) 2017-03-06 Albert Astals Cid * Call about to show for those items that have a qtubuntu-tag property (7579572) 2017-02-28 Albert Astals Cid * valign (9058e74) * Batch add and removes into the model (87ea781) 2017-01-10 Bileto Bot * Releasing 0.2.11+17.04.20170110.1-0ubuntu1 (2eaa36e) * Added shortcut hasSubmenu and roles to UnityMenuModel; Added actions property to Group (ab9f997) 2016-12-09 Nick Dedekind * added to changelog (a9c7379) * fixed version file (6be5a58) * merged with trunk (69093dc) 2016-11-16 Bileto Bot * Releasing 0.2.10+17.04.20161116-0ubuntu1 (89f2b36) 2016-11-16 Marco Trevisan (Treviño) * converter: cleanup and add support for more variant types (9884a79) 2016-11-16 Florian Boucault * Crossbuilding fixes. (9d914ff) 2016-10-26 Bileto Bot * Releasing 0.2.10+17.04.20161026.2-0ubuntu1 (24f649f) * Add ability to activate or change state of an action by variant string (662f263) 2016-10-26 Marco Trevisan (Treviño) * converterTest: add content conversions back and further from gvariant (352a770) * converter: properly set the QByteArray using ctor, or it will be created twice (210cae9) * converterTest: add conversion to GVariant and back to verify content is correct (e1ee47e) 2016-10-25 Marco Trevisan (Treviño) * converter: return a variant when the schema is a variant (d56d0ba) * converter: verify integer type conversions (eb6e5b5) * converter: free schema type on destruction (fc52f84) * converter: free the strv container (edfe13a) * Merged variant-string-parser into converter-improvements. (64a2113) 2016-10-25 Lukáš Tinkl * converter: use qUtf8Printable function (64ac22d) 2016-10-25 Marco Trevisan (Treviño) * UnityMenuModel: avoid copy on assignment (0d1bfd6) * UnityMenuModel: use conversion with gschema in unity actions (307efb4) * converterTest: simplify metatype definitions (a4c1bb6) * Converter: add support to String Arrays (6d2076c) * Merging with lp:~3v1n0/qmenumodel/variant-string-parser (d25887f) * ConverterTest: add checks for tuples and lists (c314929) 2016-10-24 Marco Trevisan (Treviño) * Add conversion tests for maps and lists to QVariant (3509321) * converterTest: add tests for toQVariantFromVariantString (3bcc60f) * convertertest: add tests for converting to QVariant (abc65a4) * converterTest: QVERIFY comparison return value (7f78a2c) 2016-10-22 Marco Trevisan (Treviño) * remove deprecated g_type_init() calls (3d2c260) * ConverterTest: remove C++11 features for now (3897067) * convertertest: create QGVariantType wrapper to avoid reinterperating a QString (4b495fc) * convertertest: repeat tests using test data (00ddb5f) * ConverterTest: use data in the testSchemaConvert (81e73a2) 2016-10-20 Marco Trevisan (Treviño) * ActionGroupTest: add test for activateByVariantString (6d2a0aa) * ActionGroupTest: split tests depending on they need the helper script or not (bba640d) 2016-10-18 Marco Trevisan (Treviño) * converter: stop looking if w'eve no entryType (901e9dc) * Converter: use builders and c++ facilities for schema conversions (3def677) * Converter: use some c++11 facilities for lists (3bd198e) * ConverterTest: add QVariantList conversion test (d8aa881) * convertertest: add conversion from map to qvariant check (d3899e6) * Converter: use QMetaType to check integer subtypes (36681ba) * Converter: add support for ByteArrayList (c42bc21) * Converter: add bytestring support (e87701e) * ConverterTest: add tests for toQVariant (3edd18d) * convertertest: use better variant map test (894fbb9) * CMakeLists.txt: we can safely use c++11 here (c3b6823) * QStateAction: add ability to activate or change state by variant string (06687a7) * Converter: move parseVariantString here from UnityMenuModel (1adf4a6) 2016-10-17 Marco Trevisan (Treviño) * convertertest: add tests for [U]LongLong converters (c998da0) * converterTest: split tests by type (f52b497) 2016-10-15 Marco Trevisan (Treviño) * UnityMenuModel: avoid initializing an empty QVariant if not needed (33b7533) 2016-10-14 Marco Trevisan (Treviño) * UnityMenuModel: avoid copying the string to a new std one (462b842) * debian/changelog: bump version number (edc354a) * UnityMenuModel add support for activating and changing state with a variant string parameter (1e9cf63) * Converter: add support to LongLong and ULongLong (449ac3d) 2016-10-04 Nick Dedekind * version bump (a618ff5) * better boolean check (388be5b) 2016-07-19 Florian Boucault * Crossbuilding fixes. (c6bba62) 2016-03-29 CI Train Bot * Releasing 0.2.9+16.04.20160329-0ubuntu1 (9efd410) 2016-03-29 Nick Dedekind * Use Qt logging categories Fixes: #1562992 Approved by: Daniel d'Andrada, PS Jenkins bot (f2b1959) 2016-03-29 Michał Sawicz * Sync gcc 5.1 rebuild version Approved by: PS Jenkins bot (2e795a2) 2016-02-23 Nick Dedekind * only log critical (4ad6055) * added missing file (2bf6347) 2016-02-19 Nick Dedekind * use logging categories (2291dec) 2016-01-15 Nick Dedekind * Added roles for submenu & shortcut. Added actions (97b75c9) 2015-12-14 Michał Sawicz * Sync gcc 5.1 rebuild (df7955d) 2015-09-08 Nick Dedekind * accelerators (523db87) 2015-04-30 CI Train Bot * Releasing 0.2.9+15.04.20150430-0ubuntu1 (e6e4bab) 2015-04-30 Michael Terry * Don't leave a dangling GVariant pointer in GtkMenuTrackerItem, which can cause a crash. (3e5d807) * Fix crash from not NULLing a variant that we later might try to unref (1933513) 2015-01-08 CI Train Bot * Releasing 0.2.9+15.04.20150108-0ubuntu1 (19e4949) 2015-01-08 Nick Dedekind * Add support for overriding QDBusActionGroup state parser Fixes: #1385331 Approved by: PS Jenkins bot, Albert Astals Cid (79ef5bc) 2014-12-10 Nick Dedekind * added comment (7dc65fd) 2014-11-11 Nick Dedekind * bump version (f1b1f9a) * Allow override of action state parser (0ca199d) 2014-10-13 CI bot * Releasing 0.2.8+14.10.20141013-0ubuntu1 (f3a203b) 2014-10-13 Nick Dedekind * Disconnect QDBusActionGroup add/remove/stateChange signals when deleting object. Fixes: 1378941 Approved by: Charles Kerr, Michael Sheldon, PS Jenkins bot, Lars Uebernickel (e0ee0a8) 2014-10-10 Nick Dedekind * disconnect on clear (ec1e10b) 2014-08-15 CI bot * Releasing 0.2.8+14.10.20140815-0ubuntu1 (49d7bf9) 2014-08-15 Nick Dedekind * Allow remote URIs to be used from file serialized icons Approved by: Sergio Schvezov, PS Jenkins bot (8f2a49c) 2014-07-29 Nick Dedekind * remove check for native file (eac47df) 2014-07-17 CI bot * Releasing 0.2.8+14.10.20140717-0ubuntu1 (d9a59c0) 2014-07-17 Lars Uebernickel * unitymenumodel: add nameOwner property Fixes: 1308011 Approved by: Mirco Müller, PS Jenkins bot (f7719ce) 2014-07-15 Lars Uebernickel * Update changelog (166406a) 2014-07-11 Lars Uebernickel * unitymenumodel: add nameOwner property (35c2996) * unitymenumodel: notify when the bus name changes (7be80e6) 2014-07-04 CI bot * Releasing 0.2.7+14.10.20140704-0ubuntu1 (018eaeb) 2014-07-04 Nick Dedekind * Unset the UnityMenuActions::model when destroying UnityMenuModel Fixes: 1334203 (2ec6160) 2014-07-04 Charles Kerr * Fix a dangling UnityMenuAction pointer by ensuring it's registered with only one menu at a time. Fixes: 1282282 (d83588e) 2014-07-01 Nick Dedekind * fixed copywrite (4f4fde0) * added missing file (99e2882) 2014-06-30 Nick Dedekind * unset actions model in destructor (6d8d26d) 2014-05-14 CI bot * Releasing 0.2.7+14.10.20140514.1-0ubuntu1 (c7955b4) 2014-05-14 Antti Kaijanmäki * Add standard "has the property actually changed" check to UnityMenuModel::setBusName() (b927a95) 2014-05-08 Antti Kaijanmäki * wrap the changelog entry. (802224f) * try to fix debian/changelog for the ci train.. (20eb438) * manually merge debian/changelog after a manual upload to the archive. (26a791f) 2014-04-02 Antti Kaijanmäki * Add standard "has the property actually changed" check to UnityMenuModel::setBusName() (bc008a3) * revert the last commit. (112eb60) * UnityMenuModel::setName: handle empty strings properly. (5809d6f) 2014-03-16 Charles Kerr * when an action's model changes in UnityMenuAction::setModel(), make sure the action unregisters itself from the previous model. (0b31bc9) 2014-03-05 CI bot * Releasing 0.2.7+14.04.20140305-0ubuntu1 (94aac07) * No change rebuild against Qt 5.2.1. (f8a07da) 2014-02-28 Albert Astals * Fix use of un-initialized memory (788ebe3) * Fix use of un-initialized memory (7b70ef5) 2013-12-13 Automatic PS uploader * Releasing 0.2.7+14.04.20131213-0ubuntu1 (revision 99 from lp:qmenumodel). (24aa85f) * Releasing 0.2.7+14.04.20131213-0ubuntu1, based on r99 (09b0f46) 2013-12-11 Nick Dedekind * Added dataChange to loadExtendedAttributes function. (9530a3d) * Added data change to loadExtAttributes (81b68c9) 2013-11-25 Automatic PS uploader * Releasing 0.2.7+14.04.20131125-0ubuntu1 (revision 97 from lp:qmenumodel). (c40c4c2) * Releasing 0.2.7+14.04.20131125-0ubuntu1, based on r97 (27b7d20) 2013-11-21 Marcus Tomlinson * Fixed #define guard. (12111d2) 2013-11-06 Marcus Tomlinson * Fixed #define guard (7332db6) 2013-10-16 Automatic PS uploader * Releasing 0.2.7+13.10.20131016-0ubuntu1 (revision 95 from lp:qmenumodel). (25e6667) * Releasing 0.2.7+13.10.20131016-0ubuntu1, based on r95 (cf34565) 2013-10-15 Nick Dedekind * Added pointer checks for items/iterators. (lp#1239394). Fixes: https://bugs.launchpad.net/bugs/1239394. (52d2141) 2013-10-14 Nick Dedekind * removed unnecessary pointer checks (c50f57f) * Added iterator/item pointer checks (e7bfaf1) 2013-10-11 Gustavo Pichorim Boiko * Use g_file_get_uri instead of g_file_get_path to make sure the URI's percent encoding is properly kept. (867693a) * Use g_file_get_uri instead of g_file_get_path to make sure the URI's percent encoding is properly kept. (fa2526b) 2013-10-11 Automatic PS uploader * Releasing 0.2.7+13.10.20131011-0ubuntu1 (revision 92 from lp:qmenumodel). (5ac03ca) * Releasing 0.2.7+13.10.20131011-0ubuntu1, based on r92 (e47e13a) 2013-10-01 Lars Uebernickel * QStateAction: try to maintain the type of the action's state (5f66f59) 2013-09-23 Lars Uebernickel * QStateAction: try to maintain the type of the action's state (74a9ba6) 2013-09-20 Automatic PS uploader * Releasing 0.2.7+13.10.20130920-0ubuntu1 (revision 90 from lp:qmenumodel). (23dee12) * Releasing 0.2.7+13.10.20130920-0ubuntu1, based on r90 (61b9ea6) 2013-09-18 Pete Woods * Fix memory leak in unitymenumodel (b6f00b9) 2013-09-17 Pete Woods * Fix sequence memory leak in unitymenumodel (9b9504f) 2013-09-13 Automatic PS uploader * Releasing 0.2.7+13.10.20130913-0ubuntu1 (revision 88 from lp:qmenumodel). (81648d3) * Releasing 0.2.7+13.10.20130913-0ubuntu1, based on r88 (d0d6d56) 2013-09-09 Mathieu Trudel-Lapierre * Add changelog for manual upload of 0.2.7+13.10.20130830-0ubuntu2. (958d147) * upload version 0.2.7+13.10.20130830-0ubuntu2 (71b514b) 2013-09-04 Lars Uebernickel * Use fully namespaced action name in UnityMenuModel::activate and for the action role. (ce35aa4) 2013-09-03 Lars Uebernickel * fullActioName: make sure 'name' is valid for the duration of the function (e01b6b5) * Add UnityMenuAction::index property (9365885) 2013-09-02 Lars Uebernickel * Add UnityMenuModel::getAction (2641328) * UnityMenuModel::activate: use gtk_menu_tracker_item_get_action_name (f58b7c7) * gtk_menu_tracker_item_get_action_name: return full name, including namespace (fa058d5) 2013-08-30 Automatic PS uploader * Releasing 0.2.7+13.10.20130830-0ubuntu1 (revision 85 from lp:qmenumodel). (28a4548) * Releasing 0.2.7+13.10.20130830-0ubuntu1, based on r85 (279b43c) 2013-08-30 Nick Dedekind * Added UnityMenuAction for "out-of-menu" actions. (db40e84) 2013-08-29 Automatic PS uploader * Releasing 0.2.7+13.10.20130829.1-0ubuntu1 (revision 83 from lp:qmenumodel). (751f4fb) * Releasing 0.2.7+13.10.20130829.1-0ubuntu1, based on r83 (f43d10f) 2013-08-29 Lars Uebernickel * Remove theme image provider (9acfda5) 2013-08-29 Nick Dedekind * Let Qt manage the destruction of child ActionStateParsers. (7d013d7) * Let Qt manage deletion of children (2eeecef) 2013-08-28 Automatic PS uploader * Releasing 0.2.7+13.10.20130828.2-0ubuntu1 (revision 80 from lp:qmenumodel). (64ab9df) * Releasing 0.2.7+13.10.20130828.2-0ubuntu1, based on r80 (a601032) 2013-08-27 Lars Uebernickel * Make sure the right qt modules are loaded to make QMenuModel tests pass. Fixes: https://bugs.launchpad.net/bugs/1217335. (4f91a49) 2013-08-27 Automatic PS uploader * Releasing 0.2.7+13.10.20130827.3-0ubuntu1 (revision 78 from lp:qmenumodel). (68e010e) 2013-08-27 Nick Dedekind * merged with trunk (8cc3f9a) * Review comments (ebace67) 2013-08-27 Lars Uebernickel * Make sure the right qt modules are loaded to make QMenuModel tests pass (31bdaf0) 2013-08-27 Automatic PS uploader * Releasing 0.2.7+13.10.20130827.3-0ubuntu1, based on r78 (298bf61) 2013-08-26 Automatic PS uploader * Releasing 0.2.7+13.10.20130826-0ubuntu1 (revision 75 from lp:qmenumodel). (8977be5) 2013-08-26 Nick Dedekind * Adds qt event spawning to direct glib main loop callbacks. Fixes: https://bugs.launchpad.net/bugs/1183065, https://bugs.launchpad.net/bugs/1206991. (aaa8252) * UnityMenuModel::changeState now uses current state parameter type as a base for parameter GVariant conversion. (50563a1) 2013-08-26 Automatic PS uploader * Releasing 0.2.7+13.10.20130826-0ubuntu1, based on r75 (8a3c8c9) 2013-08-26 Nick Dedekind * Added int64 & variant types to UnityMenuModel ext attribute parsing. (346f5fc) 2013-08-22 Nick Dedekind * removed newline (d23d848) * Added copywrite headers (9ed4538) * Using qt event loop to pass UnityMenuAction events (c274656) 2013-08-22 Lars Uebernickel * Remove theme image provider (1439420) 2013-08-22 Nick Dedekind * Added activate/changeState to UnityMenuModel (d912a0a) 2013-08-21 Nick Dedekind * Added UnityMenuAction for out-of-line actions. Action muxer copied to submenus. (72cd124) 2013-08-20 Nick Dedekind * new unitymenumodel attribute types (d83555e) 2013-08-15 Nick Dedekind * Added toGVariantWithSchema for UnityMenuModel::changeState (67394e4) 2013-08-13 Automatic PS uploader * Releasing 0.2.7+13.10.20130813-0ubuntu1 (revision 73 from lp:qmenumodel). (bac9f37) * Releasing 0.2.7+13.10.20130813-0ubuntu1, based on r73 (f26e80d) 2013-08-13 Nick Dedekind * Release version 0.2.7. (1e14a68) * version bump (5111d99) * Removed UnityMenuAction. (a89cb05) 2013-08-12 Automatic PS uploader * Releasing 0.2.6+13.10.20130812-0ubuntu1 (revision 70 from lp:qmenumodel). (2713de8) 2013-08-12 Nick Dedekind * Removed UnityMenuAction. Fixed leaks from unitymenumodel. (3caade3) 2013-08-12 Automatic PS uploader * Releasing 0.2.6+13.10.20130812-0ubuntu1, based on r70 (9d4e931) 2013-08-12 Nick Dedekind * Added UnityMenuModel. (d6085f8) 2013-08-12 Lars Uebernickel * Add isToggled role (94a0809) 2013-08-12 Nick Dedekind * Use qevents to send between direct glib loop events and qt. (6914a54) * use correct qvariant types for sensitive and isSeparator roles (3ded9bb) 2013-08-12 Lars Uebernickel * Expose isCheck and isRadio roles (6bd9b68) 2013-08-09 Nick Dedekind * use correct qvariant types for sensitive and isSeparator roles (6251412) 2013-08-09 Lars Uebernickel * Add parameter to unitymenumodel.action.activate (4e1a260) * Re-add UnityMenuModel::activate() (08a5bb3) 2013-08-09 Nick Dedekind * glib callbacks pass events through qt. (2bbdf4e) * moved events to separate file. (db47d07) * bit of code cleanup (9be2951) 2013-08-08 Nick Dedekind * Glib callbacks send events through qt. (c6cb726) 2013-08-05 Mirco Müller * Fix exporting of boolean extended-attribute in attributeToQVariant() (73223bd) 2013-08-05 Nick Dedekind * Allow changing action state (248ae8d) 2013-08-01 Mirco Müller * Fix exporting of boolean extended-attribute in attributeToQVariant() (c458dff) 2013-07-31 Nick Dedekind * removed debug code (27e7c3b) 2013-07-30 Nick Dedekind * Added menu action updateState (37e6246) * Added action name (56374f1) * merged with parent (559df4b) * Add action role (replacing actionState role) (d1c96de) * merged with trunk (c796dd5) 2013-07-30 Lars Uebernickel * Set model qdata on items instead of the muxer (d47194b) 2013-07-30 Nick Dedekind * Add ActionStateParser (01d57d8) * unitymenumodel: lookup themed icon before turning it into a uri (ed4b694) 2013-07-26 Lars Uebernickel * unitymenumodel: add get() (16b480d) * Expose the state of a menu item's action in model.actionState (d57caa6) 2013-07-25 Lars Uebernickel * Fix crash in UnityMenuModel::menuItemChanged (58afcb8) * examples/unityqmlmenumodel.qml: add license header (57f5b07) * examples/unitymenumodel.qml: update to newest API (192e9c8) * unitymenumodel: expose type and extended attributes to qml (ca81974) 2013-06-25 Lars Uebernickel * unitymenumodel: add support for icons (6aaafd5) 2013-06-24 Lars Uebernickel * Remove unityqmlmenumodel (050958c) * unitymenumodel: allow setting multiple prefixed action groups (3dd6f65) * unitymenumodel: set free func on GSequence (1bc2427) * unitymenumodel: use g_clear_object (7b06205) * unitymenumodel: remove forSubMenu (edad406) * unitymenumodel: move role enum into .cpp (f59e06e) * unitymenumodel: remove redundant priv functions (a74628a) * Move unityqmlmenumodel's properties to unitymenumodel (bf4e73f) 2013-06-07 Automatic PS uploader * Releasing 0.2.6daily13.06.07-0ubuntu1 to ubuntu. (52b1700) * Releasing 0.2.6daily13.06.07-0ubuntu1, based on r68 (a251896) 2013-06-06 Alberto Mardegan * Use invalid index as root index (2c5855c) 2013-06-05 Lars Uebernickel * unitymenumodel: call init in the the constructor that is meant to be used from c++ (45ffd91) * unitymenumodel: add support for submenus (7892fd9) * unitymenumodel: add activate() (e735d95) * Link against the qml module (692926c) 2013-06-04 Lars Uebernickel * unitymenumodel: expose isSeparator (00885c1) * examples/exportmenu.py: use g_bus_own_name to avoid race (2a01b1c) * unitymenumodel: watch the bus name (9f9560d) * Add qml example for UnityMenuModel (b1009a8) * CMakeLists.txt: explicitly specify qml include path (5503064) * Introduce UnityMenuModel and UnityQmlMenuModel (4fc790c) 2013-05-23 Alberto Mardegan * style (8157af2) 2013-05-20 Alberto Mardegan * Use invalid index as root index (c5bcb29) 2013-05-03 Automatic PS uploader * Releasing 0.2.6daily13.05.02-0ubuntu1 to ubuntu. (885710f) 2013-05-02 Automatic PS uploader * Releasing 0.2.6daily13.05.02-0ubuntu1, based on r66 (cfcd397) 2013-04-23 Mathieu Trudel-Lapierre * Don't build on powerpc (qtdeclarative5 isn't available there). (2701b12) * Don't build binary packages on powerpc; qtdeclarative5 isn't available; so explicitly list the architectures we can build on. (47deee0) 2013-04-19 Mathieu Trudel-Lapierre * Bootstrap package. (4a5edf4) * fix revision for bootstap (964f03e) * bootstrap (31ddbce) 2013-03-29 Mathieu Trudel-Lapierre * Packaging review for daily release. (10d7da0) 2013-03-26 Mathieu Trudel-Lapierre * - Add Homepage field. - Extend binary packages descriptions. (43f8630) * Move libgl-dev as an alternate Build-Depends to libgles2-mesa-dev. (3a59b8d) 2013-03-25 Mathieu Trudel-Lapierre * debian/control: - Add Vcs-Browser, add comment to developers. - Update Vcs-Bzr.; debian/rules: - Override dh_install to use --fail-missing.; debian/copyright: should be LGPL-3, not LGPL-3+. (09c56ca) 2013-02-11 Renato Araujo Oliveira Filho * Added missing file (COPYING.LGPL); Updated debian maintaine. Update debian scripts to follow Ubuntu standard;. Fixes: https://bugs.launchpad.net/bugs/1121721. (46f476e) * Added missing key ¨Vcs-Bzr" in control file. (f1d16e8) * Fixed COPYING.LGPL file to use LGPL 3 (0e00948) * Added missing file . (f6d4f68) * Added missing file ".bzr-builddeb/default.conf¨ (eb7de53) * Fixed variable export in rules file. (b0127d3) * Fixed source format. (8deae77) * Added missing file (COPYING.LGPL); Updated debian maintaine. Update debian scripts to follow Ubuntu standard; (0a72006) 2013-01-31 Renato Araujo Oliveira Filho * releasing version 0.2.6. (54bb804) * Updated to qt5 final. (095aed4) * Added missing dependency. (08e25df) * Used qmake to retrieve the import dir used to install QML modules. (e5b89ef) * Fixed extra '/' in the file path. (101c523) * Fixed missing license header. (b9bbb75) * Fixed files missing license header. (5a4ff7a) * releasing version 0.2.6 (35305e3) 2013-01-18 Renato Araujo Oliveira Filho * Updated to qt5 final. (5234489) 2013-01-15 Renato Araujo Oliveira Filho * releasing version 0.2.5. (954e6e6) 2013-01-14 Renato Araujo Oliveira Filho * Fixed changelog. (ba32c60) 2013-01-15 Renato Araujo Oliveira Filho * Fixed "invokeMethod" call to use only the method name instead of full signature. (f72cdbb) 2013-01-14 Renato Araujo Oliveira Filho * releasing version 0.2.5 (9a4f675) * Fixed "invokeMethod" call to use only the method name instead of full signature. (f177b47) * releasing version 0.2.4. (073cbc4) * releasing version 0.2.4 (aa7a055) * Implemented QMenuModel as tree model. Sections and SubMenus are exported as tree. Fixes: https://bugs.launchpad.net/bugs/1090305. (c53b046) * Used 'invokeMethod' to call slot, to make the code simple. (00bd7d4) 2013-01-11 Renato Araujo Oliveira Filho * Merged mainline. (6a56cd8) * Added missing license header. Fixes: https://bugs.launchpad.net/bugs/1097080. (84b4e66) * Added missing license header. (50efc4d) * Merged mainline. (0115b56) * Removed the use of Qt Widgets from tests. (774f7c4) 2013-01-10 Renato Araujo Oliveira Filho * Fixed item removal and insertion. Removed debug messages. Fixed tests to work with new tree model structure. (33147e9) 2013-01-03 Renato Araujo Oliveira Filho * Implmeneted QMenuModel as tree model. (2d050cd) 2012-12-17 Renato Araujo Oliveira Filho * releasing version 0.2.3. (70adf10) * releasing version 0.2.3 (7002fc2) * Try to keep the module unchanged until the function begin[Insert|Remove]Rows is called. Fixes: https://bugs.launchpad.net/bugs/1090305. (df3cd0c) * Added missing test. (c34c5af) 2012-12-16 Renato Araujo Oliveira Filho * Fixed model changes and singal. (64e7281) 2012-12-15 Renato Araujo Oliveira Filho * Removed keywords from VIRTUAL functions. (b601ff7) 2012-12-14 Renato Araujo Oliveira Filho * Only checks for row index in cache. Used Q_EMIT keywork for signals. (96a4d9c) * Avoid change rowCount value outside of BeginModel[Insert|Remove|Reset] functions (9b94a0f) 2012-12-12 Bill Filler * fix release number and formatting. (c7cd7a4) 2012-12-11 Bill Filler * releasing version 0.2.2 (83a2418) 2012-12-11 Renato Araujo Oliveira Filho * Fixed model disconnect function. Fixes: https://bugs.launchpad.net/bugs/1088518. (4b2c268) * Fixed model disconnect function. Set the model null during the disconnection will make sure that the model was cleared and the new call of connect will populate the model again. (edbfcdf) * Avoid emit signal during the model reset, this can crash qml. (0f20cc0) * Avoid emit signal during the model reset, this can crash qml. (d4ce0d4) * Fixed crash during model changes. (b9e5fb6) * Fixed crash during model changes. (0b3bfd9) 2012-12-10 Renato Araujo Oliveira Filho * releasing version 0.2.1. (5c88419) * releasing version 0.2.1 (3f548e8) 2012-12-07 Renato Araujo Oliveira Filho * Implemented conversion for GVariant and Arrays. (afd68a3) * Implemented conversion for GVariant and Arrays. (e8b1e40) 2012-12-04 Olivier Tilloy * releasing version 0.2. (94f07ed) * releasing version 0.2 (cf798a4) * Cache sub-menus. (c544a5f) * Better comment. (ac9450d) * Add myself to the list of authors. (f9ced66) * Add comments to explain non-trivial cache updates. (395a8c6) * Add a test to verify that normal menu items are not cached (only sub-menus are). (02cfba5) * Add a test to verify that the cache is correctly updated after multiple insertions and removals. (e8927d8) * Updated comments. (bf8e579) * Compare the exact cache indexes instead of just comparing the size of the cache. (50d449d) * Simplify test code. (af2608a) * Remove useless include. (4b57869) * Add myself to the list of authors. (517c21c) * Make the cache a pointer, to enforce const correctness. (7b2badf) 2012-12-03 Olivier Tilloy * Simplify the caching mechanism using a a QHash that associates indexes (int) to QMenuModel*. (2f32994) 2012-12-03 Renato Araujo Oliveira Filho * Udated ignored files list. (d39a6e1) * Changed getLink function to not be const. (5c52963) 2012-12-01 Renato Araujo Oliveira Filho * QMenuModel now keeps cache of any link element. (15ac92c) 2012-11-29 Renato Araujo Oliveira Filho * releasing version 0.1.9. (6eb5409) * releasing version 0.1.9 (85c41ee) * Created auxiliary functions. To allows access the model data from JavaScript;. (49810b9) * Used a shorter QObject::connect signature. (b3ac378) 2012-11-28 Renato Araujo Oliveira Filho * Created unit test for get data function. (08b143c) * Optimized get function code. (236259b) * Merged mainline. (98ed17e) 2012-11-23 Renato Araujo Oliveira Filho * releasing version 0.1.8. (da5a043) * releasing version 0.1.8 (8d9b6dc) * Fixed QMenumodel behavior when the GMenuModel is destroyed. (68533d0) * Removed missing function declaration. (bdc349d) * Take care of object ref internally on QMenumodel, instead of QDBusMenuModel. (aa56c7b) * Replaced 'reinterpret_cast' for gobject cast when possible. (af103eb) * Removed QAction dependency from QStateAction. Exported activate function on QStateAction. (7cfd1a4) * Merged mainline. (f68764a) * Fixed QMenumodel behaviour when the GMenuModel is destroyed. (6078bd7) * Create unit test for name property and activate function. (5d755f4) * Added 'TEST_XML_OUTPUT' option on cmake to enable/disalbe test output in xml. OBS: The default value is On. (de8eea6) * Implemented support to tuple conversions. (374e999) * Fixed code style. (2b54bfb) 2012-11-22 Renato Araujo Oliveira Filho * Added docs on new functions. (41f6ce5) * Only unref variant if its is valid. (193ddd3) * Implemented support to tuple conversions. (c377489) * Added 'TEST_XML_OUTPUT' option on cmake to enable/disalbe test output in xml. OBS: The default value is On. (560f14f) * Removed QAction dependecy from QStateAction. Exported activate function on QStateAction. (a745160) * Fixed memory leak after the service disappear. (04391e9) * Avoid find children recursive during the model cleanup. (a8dec79) * Moved model children clear to clearModel function. (f221fee) 2012-11-21 Renato Araujo Oliveira Filho * Fixed memory leak after the service disapear. (af61347) 2012-11-20 Olivier Tilloy * Reimplement roleNames() instead of using setRoleNames(…). (3dec94a) 2012-11-19 Olivier Tilloy * Reimplement roleNames() instead of using setRoleNames(…). (5d33dd5) 2012-11-15 Olivier Tilloy * releasing version 0.1.7. (1430572) 2012-11-14 Olivier Tilloy * releasing version 0.1.7 (368a2e2) 2012-11-13 Renato Araujo Oliveira Filho * Created auxiliary functions. To allows access the model data from JavaScript; (53dfc2a) 2012-11-08 Renato Araujo Oliveira Filho * Updates to get coverage and test results in jenkins. (1915b09) 2012-11-07 Renato Araujo Oliveira Filho * Merged 'lp:~renatofilho/girden/debian-dh'. (67680f3) * Merged mainline. (e6cddf7) * Make the rule 'make check' always enabled. (664f162) * Appended auto generated xml test output files into ignored list. (9dd5954) * Updated README file with the new coverage flag. (6b8fd9e) * Updated debian package to use debhelper instead of cdbs. (fc58dd6) * Merged 'lp:~renatofilho/girden/debian-dh' branch. (987dd97) * Merged mainline. (cba9178) * Updated tests make file to outpu xml results files. (79c1256) 2012-11-06 Renato Araujo Oliveira Filho * Fixed string comparation on cmake files. (21a8952) * Skip tests on qemu to avoid crashes. (dd3e64c) * Removed 'xvfb' from the dependency list. (a086f90) 2012-11-02 Renato Araujo Oliveira Filho * Fixed tests to avoid crashing if dbus session is not available. (28245a5) * Fixed tests code to avoid crash if dbus session is not avaliable. (59dfda6) 2012-11-01 Renato Araujo Oliveira Filho * Uses "QT_QPA_PLATFORM=minimal" to run qt tests. (8432596) 2012-10-31 Olivier Tilloy * releasing version 0.1.6. (2e6ccdb) * releasing version 0.1.6 (cf0b1b6) 2012-10-31 Renato Araujo Oliveira Filho * Rename coverage option name. (50600e1) * Updated dependency list to use python3 packages. (bf5a53e) * Added missing python dependencies. (abd20be) * Merged mainline. (c9c9d58) * Revert last commit. (4aee0f1) * Reverted use of xvfb for tests. (Does not work with opengl) (7f4ba1a) * Removed QtTestGui include from tests. (b4e04e3) * Added xvfb option on cmake to enable run tests without X running. (49797fd) * Added missing opengl dependency. Necessary for examples. (07087af) * Removed show widgets functions to avoid problems in the auto builder. (485cab8) * Added 'dbus-test-runner' ad build dependency. (89f52ae) 2012-10-31 Olivier Tilloy * Migrate all the scripts to python3. (521a582) 2012-10-31 Renato Araujo Oliveira Filho * Updated rules to avoid warnings during the package build. (bed4cab) * Fixed control file to accept any python3 version. (7ee5b38) * Updated debian package to use debhelper instead of cdbs. (d7ae33e) 2012-10-30 Renato Araujo Oliveira Filho * Use the correct function "g_action_group_change_action_state" to change the action state. (e26fff2) * Used utf8 instead of latin1 for strings. (fac3a65) 2012-10-30 Olivier Tilloy * Merge the latest changes from trunk. (0ad7c83) * Migrate all scripts to python3. (209c95e) 2012-10-30 Renato Araujo Oliveira Filho * Replaced gchar <-> QString convertion function, from Latin1 to Utf8. (035b608) * Created function actionActivate on QDbusActionGroup. (fd715d4) * Use the correct function "g_action_group_change_action_state" to change the action state. (2496687) * Created unit test for utf8 strings. (307b793) * Replaced string convertion from Latin1 to Utf8. (ed490f2) 2012-10-25 Renato Araujo Oliveira Filho * Changed the 'g_bus_watch_name' flag to auto start the service;. Approved by PS Jenkins bot, Olivier Tilloy. (4d47b26) 2012-10-24 Renato Araujo Oliveira Filho * Changed the 'g_bus_watch_name' flag to auto start the servic. (3c1b2d2) 2012-10-18 Olivier Tilloy * releasing version 0.1.5 (fef978e) * Add missing changelog entry. (fe42b80) 2012-10-17 Renato Araujo Oliveira Filho * Implemented conversion for QVariantMap; Created unit test for QVariantMap conversion;. Approved by Olivier Tilloy, PS Jenkins bot. (d46ee24) * Used utf8 instead of latin1 on QString conversion; (cdd910c) * Used 'G_VARIANT_TYPE_VARDICT' in GVariantBuilder constructor. (c805b48) 2012-10-16 Renato Araujo Oliveira Filho * Merged mainline. (cc79785) 2012-10-16 Olivier Tilloy * releasing version 0.1.4 (ced307a) 2012-10-16 Renato Araujo Oliveira Filho * Avoid emit model reset during QMenuModel destructor, to avoid crash in QML engine.. Approved by Olivier Tilloy, PS Jenkins bot, jenkins. (340e67d) 2012-10-15 Renato Araujo Oliveira Filho * Created unit test for QVariantMap conversion; (a98d548) * Implemented conversion for QVariantMap; (7f09723) * Merged mainline. (5c56d12) 2012-10-15 Olivier Tilloy * Expose a C++ library, and separate into 3 binary packages: - libqmenumodel0: C++ library - libqmenumodel-dev: development headers for the C++ library - qmenumodel-qml: QML module. Approved by Renato Araujo Oliveira Filho, jenkins. (5999e7a) * Merge the latest changes from trunk. (4366f70) 2012-10-11 Renato Araujo Oliveira Filho * Merged mainline. (e243399) 2012-10-11 Ricardo Mendoza * Rebuild for armhf. Approved by Florian Boucault, jenkins. (46b559b) 2012-10-11 Renato Araujo Oliveira Filho * Added new generated test files into bzrignore. (99229ab) * Created unit test for crash during model destruction. (9bf1472) 2012-10-11 Ricardo Mendoza * Rebuild for armhf (ef7435e) * Bump version to propagate release to the corresponding PPAs. Approved by Florian Boucault. (0f71225) * Bump version for Release (bcd640b) 2012-10-11 Renato Araujo Oliveira Filho * Added comments on qml file used in tests. (f430d60) 2012-10-11 Olivier Tilloy * Updated the TODO list: upon Renato’s suggestion, it would be interesting to add a ProjectConfig.cmake file. (f04b692) * Merge the latest changes from trunk. (6504ccc) 2012-10-10 Renato Araujo Oliveira Filho * Fixed typo in function name. (7c27fad) * Clenup qml file, removed debug messages and unused code. (5dd33f3) * Created a new private function "clearModel" instead of change signature of "setModel" function. (1e659ae) 2012-10-10 Olivier Tilloy * Avoid including C headers (glib/gio) in public headers; use "forward declarations" instead.. Approved by Renato Araujo Oliveira Filho, jenkins. (24faac7) * Revert to using g* types, and forward-declare them. This is more future-proof. (92ee9e3) 2012-10-09 Olivier Tilloy * Reset the navigation history when the connection to the model is lost.. Approved by Renato Araujo Oliveira Filho, jenkins. (f8266c8) 2012-10-09 Renato Araujo Oliveira Filho * Fixed crash during model destruction in QML. (c07f87e) 2012-10-09 Olivier Tilloy * Avoid including C headers (glib/gio) in public headers; use "forward declarations" instead. (bced31c) * Reset the navigation history when the connection to the model is lost. (f5e0285) * Add a changelog entry to explain the split into 3 binary packages. (a3c6400) * Add a C++ example that demonstrates the use of libqmenumodel. (69538ac) * Update TODO list. (35dfc73) * Add a missing Pre-Depends field to the control file. Fixes a lintian error. (b3dc371) * Ensure dh_makeshlibs ignores the QML module. Fixes an error and two warnings in lintian. (b81741d) * Add extended descriptions for all packages. Fixes lintian errors. (3ba2b41) 2012-10-08 Olivier Tilloy * Split the package into 3 packages: libqmenumodel0, libqmenumodel-dev, qmenumodel-qml. (93c81bd) * Ensure the model is set before advertising the status as 'Connected'. (d2ec653) * Use the Q_EMIT keyword to emit signals. (bc7eb61) * Ensure libqmenumodel is linked against libglib and libgio. (07c3252) * Call g_type_init() in the constructor for QDBusObject. This ensures it is always called where needed: clients don’t need to invoke it themselves. (fa5d7c1) * Cosmetics: remove trailing spaces. (a7852ea) * Add a pkgconfig file for developers. (0186c4a) * Install the shared library and dev headers. (1fec6fd) * Make libqmenumodel a shared library. (2ebcae8) 2012-10-05 Olivier Tilloy * Port to Qt5. (a03e7e7) * Tweaks to the structure of the code, and added example code.. Approved by Renato Araujo Oliveira Filho, jenkins. (c3fbab2) * Fixed some deprecation warnings when generating the API documentation. (900b75e) * Fix coverage to ignore all moc files. (2956675) * Use the automoc feature to simplify a lot the build system. (82677d0) * Fix packaging for Qt5. (8c51550) * Port to Qt5. (81dbf09) * Fix source dir for documentation generation. (696fba5) 2012-10-04 Olivier Tilloy * Fix variant conversion on 32-bits platforms. (5fb1863) * Renamed source package to 'qmenumodel', and removed useless build dependencies. (795bf87) * Updated ignore rules. (5292d53) * Updated ignore rules. (c81ecae) * Do not install under "Ubuntu" prefix. (9fab6d5) * Ignore clicks on menu items that do not have a submenu. (957046e) * Updated TODO list. (8e65ebb) * Updated examples/README to reflect name changes. (3fbfb27) * Rename the source directories. (36cebbb) * Add a TODO file. (62d2c4b) * Fixed some #include statements. (1e045bb) * Add instructions. (7a410b5) * Minor doc fixes. (af778cf) * Merged back doc fixes from Renato. (4385dad) 2012-10-04 Renato Araujo Oliveira Filho * Created doc source dir. (7961150) 2012-10-04 Olivier Tilloy * Replace #include statements with forward declarations in converter.h. (e14ab1e) * Remove useless code from the Converter namespace. (ba89bb2) * Add examples. (445945d) * Remove a leftover dependency. (37dd625) * Removed leftover examples infrastructure. (244ec05) * Fix a typo. (c2d5761) * Move the enums into a class that inherits QObject, so they can be exposed and used directly from QML. (36426c5) * Fix signal parameter type, unaccessible in QMl otherwise. (a9416eb) * Fix a typo. (facb9bf) * Remove the implementation of columnCount. According to the documentation (http://doc.qt.digia.com/stable/model-view-programming.html#model-subclassing-reference), « List models do not provide this function because it is already implemented in QAbstractListModel. » (57b1e3f) * Fix a typo. (90bd3a7) * Updated ignore rules. (63c4e47) 2012-10-02 Olivier Tilloy * Add ignore rules. (0ce83ea) 2012-09-14 Renato Araujo Oliveira Filho * Fixed action state initialization. (759356c) * Created function to convert from QVariant to GVariant. Moved QStateAction controler from QACtionGroup. (ebe349e) * Renamed Converter::parseGVariant. (2b9cf16) 2012-09-13 Renato Araujo Oliveira Filho * Appended comments on unit test. Added 'valid' property in QStateAction. (7dec723) * Created class QStateAction to use as GAction proxy. (a03360c) 2012-09-12 Renato Araujo Oliveira Filho * Parse extra properties name to remove 'x-' and replace '-' to '_'. (d2e99cf) * Removed invalid examples. (48e027e) * Created debian directory. (d09eaad) * Created unit test for QDBusActionGroup. (9a62817) * Added QML documentation. (a3aa532) 2012-09-11 Renato Araujo Oliveira Filho * Append the default licence header into source files. (638bcc9) * Splitted qmenumodel plugin in two libraries to make it testable. Create unit test for qmenumodel library. (6bacc0d) 2012-09-06 Renato Araujo Oliveira Filho * Implemented QDBusActionGroup. (31f8103) 2012-09-05 Renato Araujo Oliveira Filho * Initial import. (0dfd3d4) qmenumodel-0.9.1/CMakeLists.txt0000644000000000000000000000427714200511032013313 0ustar project(qmenumodel) cmake_minimum_required(VERSION 3.13) set (PROJECT_VERSION "0.9.1") if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) SET(CMAKE_INSTALL_PREFIX "/usr" CACHE PATH "..." FORCE) endif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) # Options option(ENABLE_TESTS "Enable all tests and checks" OFF) option(ENABLE_COVERAGE "Enable coverage reports (includes enabling all tests and checks)" OFF) option(ENABLE_WERROR "Treat all build warnings as errors" OFF) option(TEST_XML_OUTPUT "Print test results to xml files" OFF) option(GENERATE_DOC "Enable qdoc generation" OFF) if(ENABLE_COVERAGE) set(ENABLE_TESTS ON) set(CMAKE_BUILD_TYPE "Coverage") else() set(CMAKE_BUILD_TYPE "Release") endif() if(ENABLE_TESTS) set(TEST_XML_OUTPUT ON) endif() if(ENABLE_WERROR) add_definitions("-Werror") endif() if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") add_definitions("-Weverything") else() add_definitions("-Wall") endif() # Standard install paths include(GNUInstallDirs) find_package(Qt5Core REQUIRED) find_package(Qt5Qml REQUIRED) find_package(Qt5Gui REQUIRED) include(FindPkgConfig) pkg_check_modules(GLIB REQUIRED glib-2.0>=2.32) pkg_check_modules(GIO REQUIRED gio-2.0>=2.32) set(CMAKE_AUTOMOC ON) set(CMAKE_INCLUDE_CURRENT_DIR ON) add_definitions(-DQT_NO_KEYWORDS) add_subdirectory(libqmenumodel) # Tests if (ENABLE_TESTS) find_program(DBUS_RUNNER dbus-test-runner REQUIRED) enable_testing() if(CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7l") message(STATUS "Current version of qemu crashes during the tests. We will skip it for now.") set(ENABLE_TESTS OFF) set(ENABLE_COVERAGE OFF) set(TEST_XML_OUTPUT OFF) else() add_subdirectory(tests) if (ENABLE_COVERAGE) find_package(CoverageReport) endif() endif() endif() # Doc if(GENERATE_DOC) find_program(QDOC_BIN qdoc REQUIRED) add_subdirectory(doc) endif() # Display config info message(STATUS "Install prefix: ${CMAKE_INSTALL_PREFIX}") message(STATUS "Unit tests: ${ENABLE_TESTS}") message(STATUS "Build with -Werror: ${ENABLE_WERROR}") message(STATUS "Print test results to xml files: ${TEST_XML_OUTPUT}") message(STATUS "QDoc generation: ${GENERATE_DOC}") qmenumodel-0.9.1/COPYING.LGPL0000644000000000000000000001674314200511032012344 0ustar GNU LESSER GENERAL PUBLIC LICENSE Version 3, 29 June 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. This version of the GNU Lesser General Public License incorporates the terms and conditions of version 3 of the GNU General Public License, supplemented by the additional permissions listed below. 0. Additional Definitions. As used herein, "this License" refers to version 3 of the GNU Lesser General Public License, and the "GNU GPL" refers to version 3 of the GNU General Public License. "The Library" refers to a covered work governed by this License, other than an Application or a Combined Work as defined below. An "Application" is any work that makes use of an interface provided by the Library, but which is not otherwise based on the Library. Defining a subclass of a class defined by the Library is deemed a mode of using an interface provided by the Library. A "Combined Work" is a work produced by combining or linking an Application with the Library. The particular version of the Library with which the Combined Work was made is also called the "Linked Version". The "Minimal Corresponding Source" for a Combined Work means the Corresponding Source for the Combined Work, excluding any source code for portions of the Combined Work that, considered in isolation, are based on the Application, and not on the Linked Version. The "Corresponding Application Code" for a Combined Work means the object code and/or source code for the Application, including any data and utility programs needed for reproducing the Combined Work from the Application, but excluding the System Libraries of the Combined Work. 1. Exception to Section 3 of the GNU GPL. You may convey a covered work under sections 3 and 4 of this License without being bound by section 3 of the GNU GPL. 2. Conveying Modified Versions. If you modify a copy of the Library, and, in your modifications, a facility refers to a function or data to be supplied by an Application that uses the facility (other than as an argument passed when the facility is invoked), then you may convey a copy of the modified version: a) under this License, provided that you make a good faith effort to ensure that, in the event an Application does not supply the function or data, the facility still operates, and performs whatever part of its purpose remains meaningful, or b) under the GNU GPL, with none of the additional permissions of this License applicable to that copy. 3. Object Code Incorporating Material from Library Header Files. The object code form of an Application may incorporate material from a header file that is part of the Library. You may convey such object code under terms of your choice, provided that, if the incorporated material is not limited to numerical parameters, data structure layouts and accessors, or small macros, inline functions and templates (ten or fewer lines in length), you do both of the following: a) Give prominent notice with each copy of the object code that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the object code with a copy of the GNU GPL and this license document. 4. Combined Works. You may convey a Combined Work under terms of your choice that, taken together, effectively do not restrict modification of the portions of the Library contained in the Combined Work and reverse engineering for debugging such modifications, if you also do each of the following: a) Give prominent notice with each copy of the Combined Work that the Library is used in it and that the Library and its use are covered by this License. b) Accompany the Combined Work with a copy of the GNU GPL and this license document. c) For a Combined Work that displays copyright notices during execution, include the copyright notice for the Library among these notices, as well as a reference directing the user to the copies of the GNU GPL and this license document. d) Do one of the following: 0) Convey the Minimal Corresponding Source under the terms of this License, and the Corresponding Application Code in a form suitable for, and under terms that permit, the user to recombine or relink the Application with a modified version of the Linked Version to produce a modified Combined Work, in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source. 1) Use a suitable shared library mechanism for linking with the Library. A suitable mechanism is one that (a) uses at run time a copy of the Library already present on the user's computer system, and (b) will operate properly with a modified version of the Library that is interface-compatible with the Linked Version. e) Provide Installation Information, but only if you would otherwise be required to provide such information under section 6 of the GNU GPL, and only to the extent that such information is necessary to install and execute a modified version of the Combined Work produced by recombining or relinking the Application with a modified version of the Linked Version. (If you use option 4d0, the Installation Information must accompany the Minimal Corresponding Source and Corresponding Application Code. If you use option 4d1, you must provide the Installation Information in the manner specified by section 6 of the GNU GPL for conveying Corresponding Source.) 5. Combined Libraries. You may place library facilities that are a work based on the Library side by side in a single library together with other library facilities that are not Applications and are not covered by this License, and convey such a combined library under terms of your choice, if you do both of the following: a) Accompany the combined library with a copy of the same work based on the Library, uncombined with any other library facilities, conveyed under the terms of this License. b) Give prominent notice with the combined library that part of it is a work based on the Library, and explaining where to find the accompanying uncombined form of the same work. 6. Revised Versions of the GNU Lesser General Public License. The Free Software Foundation may publish revised and/or new versions of the GNU Lesser General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Library as you received it specifies that a certain numbered version of the GNU Lesser General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that published version or of any later version published by the Free Software Foundation. If the Library as you received it does not specify a version number of the GNU Lesser General Public License, you may choose any version of the GNU Lesser General Public License ever published by the Free Software Foundation. If the Library as you received it specifies that a proxy can decide whether future versions of the GNU Lesser General Public License shall apply, that proxy's public statement of acceptance of any version is permanent authorization for you to choose that version for the Library. qmenumodel-0.9.1/doc/CMakeLists.txt0000644000000000000000000000064014200511032014046 0ustar set(QMENUMODEL_DOC_FILE ${CMAKE_CURRENT_BINARY_DIR}/qmenumodel.qdocconf) configure_file("${CMAKE_CURRENT_SOURCE_DIR}/qmenumodel.qdocconf.in" ${QMENUMODEL_DOC_FILE} @ONLY) add_custom_target(qdoc) add_custom_command(TARGET qdoc COMMAND ${QDOC_BIN} ${QMENUMODEL_DOC_FILE} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "QDoc running...") qmenumodel-0.9.1/doc/qmenumodel.qdocconf.in0000644000000000000000000000033514200511032015600 0ustar project = QMenuModel QML elements sourcedirs = @src_SOURCE_DIR@ sources.fileextensions = "*.cpp" outputdir = html outputformats = HTML version = 0.1 syntaxhighlighting = true sourceencoding = UTF-8 outputencoding = UTF-8 qmenumodel-0.9.1/examples/ayatanaqmlmenumodel.qml0000644000000000000000000000717414200511032017141 0ustar /* * Copyright 2013 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: Lars Uebernickel */ import QtQuick 2.0 import QMenuModel 1.0 Item { width: 400; height: 500; AyatanaMenuModel { id: menu busName: "org.ayatana.indicator.sound" actions: { "indicator": "/org/ayatana/indicator/sound" } menuObjectPath: "/org/ayatana/indicator/sound/desktop" } ListView { id: listview anchors.fill: parent anchors.margins: 10 spacing: 3 model: menu delegate: Loader { sourceComponent: { if (isSeparator) { return separator; } else if (type == "org.ayatana.indicator.slider") { listview.model.loadExtendedAttributes(index, {'min-icon': 'icon', 'max-icon': 'icon'}); return slider; } else { return menuitem; } } Component { id: separator Rectangle { width: listview.width height: 4 color: "blue" } } Component { id: slider Rectangle { width: listview.width color: "#ddd" height: 40 Row { anchors.fill: parent Image { source: ext.minIcon } Text { text: model.actionState } Image { source: ext.maxIcon } } } } Component { id: menuitem Rectangle { width: listview.width height: 40 color: "#ddd" Row { anchors.fill: parent anchors.margins: 5 Image { source: icon } Text { height: parent.height verticalAlignment: Text.AlignVCenter color: sensitive ? "black" : "#aaa"; text: label } } MouseArea { anchors.fill: parent onClicked: { var submenu = listview.model.submenu(index); if (submenu) listview.model = submenu; else action.activate(); } } } } } } } qmenumodel-0.9.1/examples/CMakeLists.txt0000644000000000000000000000110714200511032015116 0ustar # Standalone CMakeLists.txt to generate the info-menumodel executable. # This is not built as part of libqmenumodel, it has to be built separately # and it assumes libqmenumodel-dev is installed on the system. cmake_minimum_required(VERSION 2.8.9) set(CMAKE_INCLUDE_CURRENT_DIR ON) set(CMAKE_AUTOMOC ON) find_package(Qt5Core REQUIRED) include(FindPkgConfig) pkg_check_modules(QMENUMODEL REQUIRED qmenumodel) add_executable(info-menumodel info-menumodel.cpp) include_directories(${QMENUMODEL_INCLUDE_DIRS}) target_link_libraries(info-menumodel ${QMENUMODEL_LDFLAGS} Qt5::Core) qmenumodel-0.9.1/examples/exportactiongroup.py0000755000000000000000000000501214200511032016526 0ustar #!/usr/bin/env python3 # -*- encoding: utf-8 -*- # # Copyright 2013 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 . # """ This example script exports an action group on the session bus under the name org.ayatana.testactiongroup and at the object path /org/ayatana/testactiongroup. """ import sys from gi.repository import Gio from gi.repository import GLib BUS_NAME = 'org.ayatana.testactiongroup' BUS_OBJECT_PATH = '/org/ayatana/testactiongroup' def action_activated(action, data): name = action.get_name() if action.get_state_type() is None: print('stateless action activated: %s' % name) else: print('stateful action activated: %s (current state: %s)' % (name, action.get_state())) if __name__ == '__main__': bus = Gio.bus_get_sync(Gio.BusType.SESSION, None) # Claim well-known bus name and ensure only one instance of self is running # at any given time. # http://dbus.freedesktop.org/doc/dbus-specification.html#message-bus-names proxy = Gio.DBusProxy.new_sync(bus, 0, None, 'org.freedesktop.DBus', '/org/freedesktop/DBus', 'org.freedesktop.DBus', None) result = proxy.RequestName('(su)', BUS_NAME, 0x4) if result != 1 : print >> sys.stderr, ("Name '%s' is already owned on the session bus." "Aborting.") % BUS_NAME sys.exit(1) group = Gio.SimpleActionGroup() foo = Gio.SimpleAction.new('foo', None) group.insert(foo) bar = Gio.SimpleAction.new_stateful('bar', None, GLib.Variant.new_boolean(False)) group.insert(bar) bleh = Gio.SimpleAction.new_stateful('bleh', None, GLib.Variant.new_string('bleh')) group.insert(bleh) for name in group.list_actions(): action = group.lookup_action(name) action.connect('activate', action_activated) bus.export_action_group(BUS_OBJECT_PATH, group) GLib.MainLoop().run() qmenumodel-0.9.1/examples/exportmenu.py0000755000000000000000000000500114200511032015136 0ustar #!/usr/bin/env python3 # -*- encoding: utf-8 -*- # # Copyright 2013 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 . # """ This example script exports a menu model on the session bus under the name org.ayatana.testmenu and at the object path /org/ayatana/testmenu. The menu model contains items that have attributes with custom values, as well as sub-menus. """ import sys from gi.repository import Gio from gi.repository import GLib BUS_NAME = 'org.ayatana.testmenu' BUS_OBJECT_PATH = '/org/ayatana/testmenu' def bus_acquired(bus, name): menu = Gio.Menu() foo = Gio.MenuItem.new('foo', 'app.foo') foo.set_attribute_value('x-additionaltext', GLib.Variant.new_string('lorem ipsum')) foo.set_attribute_value('x-enabled', GLib.Variant.new_boolean(True)) menu.append_item(foo) bar = Gio.MenuItem.new('bar', 'bar') bar.set_attribute_value('x-defaultvalue', GLib.Variant.new_string('Hello World!')) bar.set_attribute_value('x-ayatana-currentvalue', GLib.Variant.new_string('awesome')) bar.set_attribute_value('x-velocity', GLib.Variant.new_uint64(83374)) menu.append_item(bar) menu.append('bleh', 'app.bleh') submenu = Gio.Menu() submenu.append('submenu A', 'app.suba') submenu2 = Gio.Menu() submenu2.append('submenu2 A', 'app.sub2a') submenu2.append('submenu2 B', 'app.sub2b') submenu2.append('submenu2 C', 'app.sub2c') submenu.append_submenu('submenu submenu', submenu2) submenu.append('submenu C', 'app.subc') menu.append_submenu('submenu', submenu) menu.append('baz', 'app.baz') bus.export_menu_model(BUS_OBJECT_PATH, menu) actions = Gio.SimpleActionGroup.new() actions.add_action(Gio.SimpleAction.new("bar", None)) bus.export_action_group(BUS_OBJECT_PATH, actions) if __name__ == '__main__': Gio.bus_own_name(Gio.BusType.SESSION, BUS_NAME, 0, bus_acquired, None, None) GLib.MainLoop().run() qmenumodel-0.9.1/examples/info-menumodel.cpp0000644000000000000000000000634514200511032016011 0ustar /* * 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: * Olivier Tilloy */ /* * Example executable that links against libqmenumodel to monitor * and print information about a menu model exported on D-Bus. */ // QMenuModel #include "qmenumodel/qdbusmenumodel.h" // stdlib #include // Qt #include #include #define BUS_NAME "org.ayatana.testmenu" #define BUS_OBJECT_PATH "/org/ayatana/testmenu" class MenuModelMonitor : public QDBusMenuModel { Q_OBJECT public: MenuModelMonitor(QObject* parent=0) : QDBusMenuModel(parent) { setProperty("busType", DBusEnums::SessionBus); setProperty("busName", BUS_NAME); setProperty("objectPath", BUS_OBJECT_PATH); QObject::connect(this, SIGNAL(statusChanged(DBusEnums::ConnectionStatus)), SLOT(onStatusChanged(DBusEnums::ConnectionStatus))); QObject::connect(this, SIGNAL(rowsInserted(const QModelIndex&, int, int)), SLOT(onModelChanged())); QObject::connect(this, SIGNAL(rowsRemoved(const QModelIndex&, int, int)), SLOT(onModelChanged())); std::cout << "Monitoring menu model " << property("objectPath").toString().toUtf8().constData() << " on the well-known name " << property("busName").toString().toUtf8().constData() << std::endl; } private Q_SLOTS: void onStatusChanged(DBusEnums::ConnectionStatus status) { std::cout << "Status of menu model changed to " << status << std::endl; if (status == DBusEnums::Connected) { printModel(this); } } void onModelChanged() { printModel(this); } private: void printModel(QMenuModel* model, int indent=0) { int count = model->rowCount(); for (int i = 0; i < count; ++i) { QModelIndex index = model->index(i, 0); QString label = model->data(index, QMenuModel::Label).toString(); QVariant submenu = model->data(index, QMenuModel::LinkSubMenu); for (int j = 0; j < indent * 2; ++j) std::cout << " "; std::cout << " > " << label.toUtf8().constData() << std::endl; if (submenu.isValid()) { printModel(qobject_cast(submenu.value()), indent + 1); } } } }; #include "info-menumodel.moc" int main(int argc, char** argv) { MenuModelMonitor monitor; monitor.start(); int result = QCoreApplication(argc, argv).exec(); monitor.stop(); return result; } qmenumodel-0.9.1/examples/README0000644000000000000000000000142614200511032013242 0ustar This directory contains examples that demonstrate how to use the QMenuModel binding in Qt and QML applications. To export an example menu model on the bus, run 'exportmenu.py'. To render this menu in a QML application, run 'render-menumodel.qml' in qmlscene. You will need to inform qmlscene of the location of the QMenuModel plugin if it’s not installed system-wide, e.g.: $ qmlscene -I libqmenumodel examples/render-menumodel.qml To compile a C++ executable that uses libqmenumodel to monitor and print information about the exported menu, run `cmake` and then `make` from this directory. This assumes that libqmenumodel-dev is installed on the system (it won’t work with a local copy). Then, run the executable generated: $ cmake . $ make $ ./info-menumodel qmenumodel-0.9.1/examples/render-menumodel.qml0000644000000000000000000001152614200511032016341 0ustar /* * 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: * Olivier Tilloy */ // This example QML application renders a menu model exposed on the session bus // under the well-known name org.ayatana.testmenu and at the object path // /org/ayatana/testmenu. import QtQuick 2.0 import QMenuModel 1.0 Item { id: container width: 300 height: 300 QDBusMenuModel { id: menuModel busType: DBus.SessionBus busName: "org.ayatana.testmenu" objectPath: "/org/ayatana/testmenu" onStatusChanged: { if (status == DBus.Connecting) { view.reset() } } } ListView { id: view property variant __back: [] anchors.left: parent.left anchors.right:parent.right anchors.top: parent.top anchors.bottom: backbutton.top anchors.margins: 10 spacing: 3 model: menuModel delegate: Rectangle { width: parent.width height: 30 radius: 3 color: { if (linkSubMenu == null) return "lightgrey" if (delegatearea.containsMouse) return "steelblue" return "lightsteelblue" } Text { anchors.fill: parent anchors.margins: 5 verticalAlignment: Text.AlignVCenter color: (linkSubMenu == null) ? "grey" : "black" text: { if (linkSubMenu == null) return "%1 (%2)".arg(label).arg(action) else return "submenu" } } MouseArea { id: delegatearea anchors.fill: parent hoverEnabled: true onClicked: { if (linkSubMenu == null) return var newback = view.__back newback.push(view.model) view.__back = newback view.model = linkSubMenu } onEntered: { var text = "" for (var prop in extra) { text += "%1 = %2\n".arg(prop).arg(extra[prop].toString()) } if (text != "") { tooltip.text = text tooltip.visible = true } } onExited: { tooltip.visible = false } onPositionChanged: { var pos = container.mapFromItem(delegatearea, mouse.x, mouse.y) tooltip.x = pos.x + 10 tooltip.y = pos.y + 10 } } } function goback() { var newback = view.__back model = newback[newback.length - 1] newback.pop() view.__back = newback } function reset() { while (view.__back.length > 0) { goback() } } } Rectangle { id: backbutton anchors.left: parent.left anchors.right: parent.right anchors.bottom: parent.bottom anchors.margins: 5 radius: 4 height: 50 color: (mousearea.enabled && mousearea.containsMouse) ? "steelblue" : "lightsteelblue" Text { anchors.fill: parent horizontalAlignment: Text.AlignHCenter verticalAlignment: Text.AlignVCenter text: "back" color: mousearea.enabled ? "black" : "grey" } MouseArea { id: mousearea anchors.fill: parent enabled: view.__back.length > 0 hoverEnabled: true onClicked: { view.goback() } } } Rectangle { id: tooltip property alias text: t.text visible: false width: t.width + 10 height: t.height + 10 radius: 4 color: "lightyellow" Text { id: t height: paintedHeight width: paintedWidth x: 5 y: 5 anchors.margins: 4 font.pixelSize: 11 } } Component.onCompleted: menuModel.start() } qmenumodel-0.9.1/Jenkinsfile0000644000000000000000000000601014200511032012722 0ustar pipeline { agent any stages { stage('Build source') { steps { sh '/usr/bin/build-source.sh' stash(name: 'source', includes: '*.gz,*.bz2,*.xz,*.deb,*.dsc,*.changes,*.buildinfo,lintian.txt') cleanWs(cleanWhenAborted: true, cleanWhenFailure: true, cleanWhenNotBuilt: true, cleanWhenSuccess: true, cleanWhenUnstable: true, deleteDirs: true) } } stage('Build binary - armhf') { steps { parallel( "Build binary - armhf": { node(label: 'arm64') { cleanWs(cleanWhenAborted: true, cleanWhenFailure: true, cleanWhenNotBuilt: true, cleanWhenSuccess: true, cleanWhenUnstable: true, deleteDirs: true) unstash 'source' sh '''export architecture="armhf" build-binary.sh''' stash(includes: '*.gz,*.bz2,*.xz,*.deb,*.dsc,*.changes,*.buildinfo,lintian.txt', name: 'build-armhf') cleanWs(cleanWhenAborted: true, cleanWhenFailure: true, cleanWhenNotBuilt: true, cleanWhenSuccess: true, cleanWhenUnstable: true, deleteDirs: true) } }, "Build binary - arm64": { node(label: 'arm64') { cleanWs(cleanWhenAborted: true, cleanWhenFailure: true, cleanWhenNotBuilt: true, cleanWhenSuccess: true, cleanWhenUnstable: true, deleteDirs: true) unstash 'source' sh '''export architecture="arm64" build-binary.sh''' stash(includes: '*.gz,*.bz2,*.xz,*.deb,*.dsc,*.changes,*.buildinfo,lintian.txt', name: 'build-arm64') cleanWs(cleanWhenAborted: true, cleanWhenFailure: true, cleanWhenNotBuilt: true, cleanWhenSuccess: true, cleanWhenUnstable: true, deleteDirs: true) } }, "Build binary - amd64": { node(label: 'amd64') { cleanWs(cleanWhenAborted: true, cleanWhenFailure: true, cleanWhenNotBuilt: true, cleanWhenSuccess: true, cleanWhenUnstable: true, deleteDirs: true) unstash 'source' sh '''export architecture="amd64" build-binary.sh''' stash(includes: '*.gz,*.bz2,*.xz,*.deb,*.dsc,*.changes,*.buildinfo,lintian.txt', name: 'build-amd64') cleanWs(cleanWhenAborted: true, cleanWhenFailure: true, cleanWhenNotBuilt: true, cleanWhenSuccess: true, cleanWhenUnstable: true, deleteDirs: true) } } ) } } stage('Results') { steps { cleanWs(cleanWhenAborted: true, cleanWhenFailure: true, cleanWhenNotBuilt: true, cleanWhenSuccess: true, cleanWhenUnstable: true, deleteDirs: true) unstash 'build-armhf' unstash 'build-arm64' unstash 'build-amd64' archiveArtifacts(artifacts: '*.gz,*.bz2,*.xz,*.deb,*.dsc,*.changes,*.buildinfo', fingerprint: true, onlyIfSuccessful: true) sh '''/usr/bin/build-repo.sh''' } } stage('Cleanup') { steps { cleanWs(cleanWhenAborted: true, cleanWhenFailure: true, cleanWhenNotBuilt: true, cleanWhenSuccess: true, cleanWhenUnstable: true, deleteDirs: true) } } } } qmenumodel-0.9.1/libqmenumodel/CMakeLists.txt0000644000000000000000000000011314200511032016131 0ustar project(libqmenumodel) add_subdirectory(src) add_subdirectory(QMenuModel) qmenumodel-0.9.1/libqmenumodel/QMenuModel/CMakeLists.txt0000644000000000000000000000146114200511032020146 0ustar project(qmlplugin) set(QMLPLUGIN_SRC plugin.cpp ) add_library(qmenumodel-qml MODULE ${QMLPLUGIN_SRC} ) include_directories( ${src_SOURCE_DIR} ${GLIB_INCLUDE_DIRS} ) find_package(Qt5Qml REQUIRED) find_package(Qt5Quick REQUIRED) target_link_libraries(qmenumodel-qml qmenumodel ${GLIB_LDFLAGS} ${GIO_LDFLAGS} Qt5::Qml Qt5::Quick ) execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/qmldir" "${CMAKE_CURRENT_BINARY_DIR}/qmldir") set(QT_IMPORTS_DIR "${CMAKE_INSTALL_LIBDIR}/qt5/qml") set(QMLPLUGIN_INSTALL_PREFIX "${QT_IMPORTS_DIR}/QMenuModel.1") install(TARGETS qmenumodel-qml DESTINATION ${QMLPLUGIN_INSTALL_PREFIX}) install(FILES qmldir DESTINATION ${QMLPLUGIN_INSTALL_PREFIX}) qmenumodel-0.9.1/libqmenumodel/QMenuModel/plugin.cpp0000644000000000000000000000352114200511032017407 0ustar /* * Copyright 2012 Canonical Ltd. * Copyright 2021 Robert Tari * * 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 * Robert Tari */ #include "plugin.h" #include "qmenumodel.h" #include "qdbusmenumodel.h" #include "qdbusactiongroup.h" #include "qstateaction.h" #include "ayatanamenuaction.h" #include "ayatanamenumodel.h" #include void QMenuModelQmlPlugin::initializeEngine(QQmlEngine *engine, const char *uri) { } void QMenuModelQmlPlugin::registerTypes(const char *uri) { qmlRegisterUncreatableType(uri, 1, 0, "QMenuModel", "QMenuModel is a interface"); qmlRegisterUncreatableType(uri, 1, 0, "QStateAction", "QStateAction must be created by QDBusActionGroup::action"); qmlRegisterUncreatableType(uri, 1, 0, "DBus", "DBus is only a namespace"); qmlRegisterType(uri, 1, 0, "QDBusMenuModel"); qmlRegisterType(uri, 1, 0, "QDBusActionGroup"); qmlRegisterType(uri, 1, 0, "AyatanaMenuModel"); qmlRegisterType(uri, 1, 0, "AyatanaMenuAction"); } qmenumodel-0.9.1/libqmenumodel/QMenuModel/plugin.h0000644000000000000000000000204714200511032017056 0ustar /* * 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 QMENUMODELQMLPLUGIN_H #define QMENUMODELQMLPLUGIN_H #include class QMenuModelQmlPlugin : public QQmlExtensionPlugin { Q_OBJECT Q_PLUGIN_METADATA(IID "org.ayatana.qmenumodel") public: void initializeEngine(QQmlEngine *engine, const char *uri); void registerTypes(const char *uri); }; #endif qmenumodel-0.9.1/libqmenumodel/QMenuModel/qmldir0000644000000000000000000000005114200511032016613 0ustar module QMenuModel plugin qmenumodel-qml qmenumodel-0.9.1/libqmenumodel/src/actionstateparser.cpp0000644000000000000000000000175614200511032020435 0ustar /* * Copyright 2013 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: * Nick Dedekind */ #include "actionstateparser.h" #include "converter.h" ActionStateParser::ActionStateParser(QObject* parent) : QObject(parent) { } QVariant ActionStateParser::toQVariant(GVariant* state) const { if (state) { return Converter::toQVariant(state); } return QVariant(); }qmenumodel-0.9.1/libqmenumodel/src/actionstateparser.h0000644000000000000000000000202614200511032020071 0ustar /* * Copyright 2013 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: * Nick Dedekind */ #ifndef ACTIONSTATEPARSER_H #define ACTIONSTATEPARSER_H #include #include typedef struct _GVariant GVariant; class ActionStateParser : public QObject { Q_OBJECT public: ActionStateParser(QObject* parent = 0); virtual QVariant toQVariant(GVariant* state) const; }; #endif // ACTIONSTATEPARSER_H qmenumodel-0.9.1/libqmenumodel/src/ayatanamenuaction.cpp0000644000000000000000000000711314200511032020374 0ustar /* * Copyright 2013 Canonical Ltd. * Copyright 2022 Robert Tari * * 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: * Nick Dedekind * Robert Tari */ #include "ayatanamenuaction.h" #include "ayatanamenumodel.h" #include "ayatanamenuactionevents.h" AyatanaMenuAction::AyatanaMenuAction(QObject* parent) : QObject(parent), m_valid(false), m_enabled(false), m_model(NULL), m_index(-1) { } AyatanaMenuAction::~AyatanaMenuAction() { unregisterAction(); } QString AyatanaMenuAction::name() const { return m_name; } void AyatanaMenuAction::setName(const QString& name) { if (m_name != name) { m_name = name; Q_EMIT nameChanged(m_name); } } AyatanaMenuModel* AyatanaMenuAction::model() const { return m_model; } void AyatanaMenuAction::setModel(AyatanaMenuModel* model) { if (m_model != model) { unregisterAction(); m_model = model; registerAction(); Q_EMIT modelChanged(model); } } QVariant AyatanaMenuAction::state() const { return m_state; } void AyatanaMenuAction::setState(const QVariant& state) { if (m_state != state) { m_state = state; Q_EMIT stateChanged(m_state); } } bool AyatanaMenuAction::isEnabled() const { return m_enabled; } void AyatanaMenuAction::setEnabled(bool enabled) { if (m_enabled != enabled) { m_enabled = enabled; Q_EMIT enabledChanged(m_enabled); } } bool AyatanaMenuAction::isValid() const { return m_valid; } void AyatanaMenuAction::setValid(bool valid) { if (m_valid != valid) { m_valid = valid; Q_EMIT validChanged(m_valid); } } int AyatanaMenuAction::index() const { return m_index; } void AyatanaMenuAction::setIndex(int i) { if (i != m_index) { m_index = i; Q_EMIT indexChanged(m_index); } } void AyatanaMenuAction::registerAction() { if (m_model) { m_model->registerAction(this); } } void AyatanaMenuAction::unregisterAction() { if (m_model) { m_model->unregisterAction(this); } } bool AyatanaMenuAction::event(QEvent* e) { if (e->type() == AyatanaMenuActionAddEvent::eventType) { AyatanaMenuActionAddEvent *umaae = static_cast(e); setEnabled(umaae->enabled); setState(umaae->state); setValid(true); return true; } else if (e->type() == AyatanaMenuActionEnabledChangedEvent::eventType) { AyatanaMenuActionEnabledChangedEvent *umaece = static_cast(e); setEnabled(umaece->enabled); return true; } else if (e->type() == AyatanaMenuActionStateChangeEvent::eventType) { AyatanaMenuActionStateChangeEvent *umasce = static_cast(e); setState(umasce->state); return true; } else if (e->type() == AyatanaMenuActionRemoveEvent::eventType) { setValid(false); return true; } return QObject::event(e); } qmenumodel-0.9.1/libqmenumodel/src/ayatanamenuactionevents.cpp0000644000000000000000000000356014200511032021623 0ustar /* * Copyright 2013 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: * Nicholas Dedekind (QEvent::registerEventType()); const QEvent::Type AyatanaMenuActionRemoveEvent::eventType = static_cast(QEvent::registerEventType()); const QEvent::Type AyatanaMenuActionEnabledChangedEvent::eventType = static_cast(QEvent::registerEventType()); const QEvent::Type AyatanaMenuActionStateChangeEvent::eventType = static_cast(QEvent::registerEventType()); AyatanaMenuActionAddEvent::AyatanaMenuActionAddEvent(bool _enabled, const QVariant& _state) : QEvent(AyatanaMenuActionAddEvent::eventType), enabled(_enabled), state(_state) {} AyatanaMenuActionRemoveEvent::AyatanaMenuActionRemoveEvent() : QEvent(AyatanaMenuActionRemoveEvent::eventType) { } AyatanaMenuActionEnabledChangedEvent::AyatanaMenuActionEnabledChangedEvent(bool _enabled) : QEvent(AyatanaMenuActionEnabledChangedEvent::eventType), enabled(_enabled) {} AyatanaMenuActionStateChangeEvent::AyatanaMenuActionStateChangeEvent(const QVariant& _state) : QEvent(AyatanaMenuActionStateChangeEvent::eventType), state(_state) {} qmenumodel-0.9.1/libqmenumodel/src/ayatanamenuactionevents.h0000644000000000000000000000341114200511032021263 0ustar /* * Copyright 2013 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: * Nicholas Dedekind #include /* Event for a ayatanamenuaction add */ class AyatanaMenuActionAddEvent : public QEvent { public: static const QEvent::Type eventType; AyatanaMenuActionAddEvent(bool enabled, const QVariant& state); bool enabled; QVariant state; }; /* Event for a ayatanamenuaction remove */ class AyatanaMenuActionRemoveEvent : public QEvent { public: static const QEvent::Type eventType; AyatanaMenuActionRemoveEvent(); }; /* Event for change in enabled value of a ayatanamenuaction */ class AyatanaMenuActionEnabledChangedEvent : public QEvent { public: static const QEvent::Type eventType; AyatanaMenuActionEnabledChangedEvent(bool enabled); int enabled; }; /* Event for change in state value of a ayatanamenuaction */ class AyatanaMenuActionStateChangeEvent : public QEvent { public: static const QEvent::Type eventType; AyatanaMenuActionStateChangeEvent(const QVariant& state); QVariant state; }; #endif //AYATANAMENUACTIONEVENTS_H qmenumodel-0.9.1/libqmenumodel/src/ayatanamenuaction.h0000644000000000000000000000460714200511032020046 0ustar /* * Copyright 2013 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: Nick Dedekind */ #ifndef AYATANAMENUACTION_H #define AYATANAMENUACTION_H #include #include class AyatanaMenuModel; class AyatanaMenuAction: public QObject { Q_OBJECT Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged) Q_PROPERTY(QVariant state READ state NOTIFY stateChanged) Q_PROPERTY(bool enabled READ isEnabled NOTIFY enabledChanged) Q_PROPERTY(bool valid READ isValid NOTIFY validChanged) Q_PROPERTY(AyatanaMenuModel* model READ model WRITE setModel NOTIFY modelChanged) Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged) public: AyatanaMenuAction(QObject* parent = 0); ~AyatanaMenuAction(); QString name() const; void setName(const QString& str); AyatanaMenuModel* model() const; void setModel(AyatanaMenuModel* model); int index() const; void setIndex(int i); QVariant state() const; bool isEnabled() const; bool isValid() const; Q_SIGNALS: Q_INVOKABLE void activate(const QVariant& parameter = QVariant()); Q_INVOKABLE void changeState(const QVariant& parameter); void nameChanged(const QString& name); void modelChanged(AyatanaMenuModel* model); void stateChanged(const QVariant& name); void enabledChanged(bool enabled); void validChanged(bool valid); void indexChanged(int index); protected: virtual bool event(QEvent* e); void setState(const QVariant& state); void setEnabled(bool enabled); void setValid(bool valid); private: void unregisterAction(); void registerAction(); QString m_name; QVariant m_state; bool m_valid; bool m_enabled; AyatanaMenuModel* m_model; int m_index; }; #endif // AYATANAMENUACTIONGROUP_H qmenumodel-0.9.1/libqmenumodel/src/ayatanamenumodel.cpp0000644000000000000000000010236714200511032020226 0ustar /* * Copyright 2013 Canonical Ltd. * Copyright 2022 Robert Tari * * 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: * Lars Uebernickel * Robert Tari */ #include "ayatanamenumodel.h" #include "converter.h" #include "actionstateparser.h" #include "ayatanamenumodelevents.h" #include "ayatanamenuaction.h" #include "ayatanamenuactionevents.h" #include "logging.h" #include #include #include #include extern "C" { #include "gtk/gtkactionmuxer.h" #include "gtk/gtkmenutracker.h" #include "gtk/gtksimpleactionobserver.h" } Q_LOGGING_CATEGORY(ayatanamenumodel, "qmenumodel.ayatanamenumodel", QtCriticalMsg) G_DEFINE_QUARK (AYATANA_MENU_MODEL, ayatana_menu_model) G_DEFINE_QUARK (AYATANA_SUBMENU_MODEL, ayatana_submenu_model) G_DEFINE_QUARK (AYATANA_MENU_ITEM_EXTENDED_ATTRIBUTES, ayatana_menu_item_extended_attributes) G_DEFINE_QUARK (AYATANA_MENU_ACTION, ayatana_menu_action) enum MenuRoles { LabelRole = Qt::DisplayRole + 1, SensitiveRole, IsSeparatorRole, IconRole, TypeRole, ExtendedAttributesRole, ActionRole, ActionStateRole, IsCheckRole, IsRadioRole, IsToggledRole, ShortcutRole, HasSubmenuRole }; class AyatanaMenuModelPrivate { public: AyatanaMenuModelPrivate(AyatanaMenuModel *model); AyatanaMenuModelPrivate(const AyatanaMenuModelPrivate& other, AyatanaMenuModel *model); ~AyatanaMenuModelPrivate(); void clearItems(bool resetModel=true); void clearName(); void updateActions(); void updateMenuModel(); QVariant itemState(GtkMenuTrackerItem *item); AyatanaMenuModel *model; GtkActionMuxer *muxer; GtkMenuTracker *menutracker; GSequence *items; GDBusConnection *connection; QByteArray busName; QByteArray nameOwner; guint nameWatchId; QVariantMap actions; QByteArray menuObjectPath; QHash roles; ActionStateParser* actionStateParser; QHash registeredActions; bool destructorGuard; static void nameAppeared(GDBusConnection *connection, const gchar *name, const gchar *owner, gpointer user_data); static void nameVanished(GDBusConnection *connection, const gchar *name, gpointer user_data); static void menuItemInserted(GPtrArray *items, gint position, gpointer user_data); static void menuItemRemoved(gint position, gint n_items, gpointer user_data); static void menuItemChanged(GObject *object, GParamSpec *pspec, gpointer user_data); static void registeredActionAdded(GtkSimpleActionObserver *observer_item, const gchar *action_name, gboolean enabled, GVariant *state); static void registeredActionEnabledChanged(GtkSimpleActionObserver *observer_item, const gchar *action_name, gboolean enabled); static void registeredActionStateChanged(GtkSimpleActionObserver *observer_item, const gchar *action_name, GVariant *state); static void registeredActionRemoved(GtkSimpleActionObserver *observer_item, const gchar *action_name); gchar * fullActionName(AyatanaMenuAction *action); void updateRegisteredAction(AyatanaMenuAction *action); }; void menu_item_free (gpointer data) { GtkMenuTrackerItem *item = (GtkMenuTrackerItem *) data; g_signal_handlers_disconnect_by_func (item, (gpointer) AyatanaMenuModelPrivate::menuItemChanged, NULL); g_object_unref (item); } AyatanaMenuModelPrivate::AyatanaMenuModelPrivate(AyatanaMenuModel *model) { this->model = model; this->menutracker = NULL; this->connection = NULL; this->nameWatchId = 0; this->actionStateParser = new ActionStateParser(model); this->destructorGuard = false; this->muxer = gtk_action_muxer_new (); this->items = g_sequence_new (menu_item_free); } AyatanaMenuModelPrivate::AyatanaMenuModelPrivate(const AyatanaMenuModelPrivate& other, AyatanaMenuModel *model) { this->model = model; this->menutracker = NULL; this->connection = NULL; this->nameWatchId = 0; this->actionStateParser = new ActionStateParser(model); this->destructorGuard = false; this->muxer = GTK_ACTION_MUXER( g_object_ref(other.muxer)); this->items = g_sequence_new (menu_item_free); } AyatanaMenuModelPrivate::~AyatanaMenuModelPrivate() { this->destructorGuard = true; this->clearItems(false); g_sequence_free(this->items); g_clear_pointer (&this->menutracker, gtk_menu_tracker_free); g_clear_object (&this->muxer); g_clear_object (&this->connection); QHash::const_iterator it = this->registeredActions.constBegin(); for (; it != this->registeredActions.constEnd(); ++it) { g_object_unref(it.value()); it.key()->setModel(NULL); } this->registeredActions.clear(); if (this->nameWatchId) g_bus_unwatch_name (this->nameWatchId); } void AyatanaMenuModelPrivate::clearItems(bool resetModel) { AyatanaMenuModelClearEvent ummce(resetModel); QCoreApplication::sendEvent(model, &ummce); } void AyatanaMenuModelPrivate::clearName() { this->clearItems(); this->nameOwner = QByteArray(); this->updateActions(); this->updateMenuModel(); Q_EMIT model->nameOwnerChanged (this->nameOwner); } void AyatanaMenuModelPrivate::updateActions() { Q_FOREACH (QString prefix, this->actions.keys()) gtk_action_muxer_remove (this->muxer, prefix.toUtf8()); if (this->nameOwner.isEmpty()) return; for (QVariantMap::const_iterator it = this->actions.constBegin(); it != this->actions.constEnd(); ++it) { GDBusActionGroup *actions; actions = g_dbus_action_group_get (this->connection, this->nameOwner, it.value().toByteArray()); gtk_action_muxer_insert (this->muxer, it.key().toUtf8(), G_ACTION_GROUP (actions)); g_object_unref (actions); } } void AyatanaMenuModelPrivate::updateMenuModel() { this->clearItems(); g_clear_pointer (&this->menutracker, gtk_menu_tracker_free); if (!this->nameOwner.isEmpty()) { GDBusMenuModel *menu; menu = g_dbus_menu_model_get (this->connection, this->nameOwner, this->menuObjectPath.constData()); this->menutracker = gtk_menu_tracker_new (GTK_ACTION_OBSERVABLE (this->muxer), G_MENU_MODEL (menu), TRUE, NULL, menuItemInserted, menuItemRemoved, this); g_object_unref (menu); } } QVariant AyatanaMenuModelPrivate::itemState(GtkMenuTrackerItem *item) { QVariant result; GVariant *state = gtk_menu_tracker_item_get_action_state (item); if (state != NULL) { if (actionStateParser != NULL) { result = actionStateParser->toQVariant(state); } g_variant_unref (state); } return result; } void AyatanaMenuModelPrivate::nameAppeared(GDBusConnection *connection, const gchar *name, const gchar *owner, gpointer user_data) { AyatanaMenuModelPrivate *priv = (AyatanaMenuModelPrivate *)user_data; priv->connection = (GDBusConnection *) g_object_ref (connection); priv->nameOwner = owner; priv->updateActions(); priv->updateMenuModel(); Q_EMIT priv->model->nameOwnerChanged (priv->nameOwner); } void AyatanaMenuModelPrivate::nameVanished(GDBusConnection *connection, const gchar *name, gpointer user_data) { AyatanaMenuModelPrivate *priv = (AyatanaMenuModelPrivate *)user_data; priv->clearName(); } void AyatanaMenuModelPrivate::menuItemInserted(GPtrArray *items, gint position, gpointer user_data) { AyatanaMenuModelPrivate *priv = (AyatanaMenuModelPrivate *)user_data; AyatanaMenuModelAddRowEvent ummare(items, position); QCoreApplication::sendEvent(priv->model, &ummare); } void AyatanaMenuModelPrivate::menuItemRemoved(gint position, gint n_items, gpointer user_data) { AyatanaMenuModelPrivate *priv = (AyatanaMenuModelPrivate *)user_data; AyatanaMenuModelRemoveRowEvent ummrre(position, n_items); QCoreApplication::sendEvent(priv->model, &ummrre); } void AyatanaMenuModelPrivate::menuItemChanged(GObject *object, GParamSpec *pspec, gpointer user_data) { GSequenceIter *it = (GSequenceIter *) user_data; GtkMenuTrackerItem *item; AyatanaMenuModel *model; gint position; item = (GtkMenuTrackerItem *) g_sequence_get (it); model = (AyatanaMenuModel *) g_object_get_qdata (G_OBJECT (item), ayatana_menu_model_quark ()); position = g_sequence_iter_get_position (it); AyatanaMenuModelDataChangeEvent ummdce(position); QCoreApplication::sendEvent(model, &ummdce); } AyatanaMenuModel::AyatanaMenuModel(QObject *parent): QAbstractListModel(parent) { priv = new AyatanaMenuModelPrivate(this); } AyatanaMenuModel::AyatanaMenuModel(const AyatanaMenuModelPrivate& other, AyatanaMenuModel *parent): QAbstractListModel(parent) { priv = new AyatanaMenuModelPrivate(other, this); } AyatanaMenuModel::~AyatanaMenuModel() { delete priv; } QByteArray AyatanaMenuModel::busName() const { return priv->busName; } QByteArray AyatanaMenuModel::nameOwner() const { return priv->nameOwner; } void AyatanaMenuModel::setBusName(const QByteArray &name) { if (name == priv->busName) return; priv->clearName(); if (priv->nameWatchId) g_bus_unwatch_name (priv->nameWatchId); priv->nameWatchId = g_bus_watch_name (G_BUS_TYPE_SESSION, name.constData(), G_BUS_NAME_WATCHER_FLAGS_AUTO_START, AyatanaMenuModelPrivate::nameAppeared, AyatanaMenuModelPrivate::nameVanished, priv, NULL); priv->busName = name; Q_EMIT busNameChanged (priv->busName); } QVariantMap AyatanaMenuModel::actions() const { return priv->actions; } void AyatanaMenuModel::setActions(const QVariantMap &actions) { priv->actions = actions; priv->updateActions(); } QByteArray AyatanaMenuModel::menuObjectPath() const { return priv->menuObjectPath; } void AyatanaMenuModel::setMenuObjectPath(const QByteArray &path) { priv->menuObjectPath = path; priv->updateMenuModel(); } ActionStateParser* AyatanaMenuModel::actionStateParser() const { return priv->actionStateParser; } void AyatanaMenuModel::setActionStateParser(ActionStateParser* actionStateParser) { if (priv->actionStateParser != actionStateParser) { priv->actionStateParser = actionStateParser; Q_EMIT actionStateParserChanged(actionStateParser); } } int AyatanaMenuModel::rowCount(const QModelIndex &parent) const { return !parent.isValid() ? g_sequence_get_length (priv->items) : 0; } int AyatanaMenuModel::columnCount(const QModelIndex &parent) const { return 1; } static QString iconUri(GIcon *icon) { QString uri; if (G_IS_THEMED_ICON (icon)) { const gchar* const* iconNames = g_themed_icon_get_names (G_THEMED_ICON (icon)); guint index = 0; while(iconNames[index] != NULL) { if (QIcon::hasThemeIcon(iconNames[index])) { uri = QString("image://theme/") + iconNames[index]; break; } index++; } } else if (G_IS_FILE_ICON (icon)) { GFile *file; file = g_file_icon_get_file (G_FILE_ICON (icon)); if (file) { gchar *fileuri; fileuri = g_file_get_uri (file); uri = QString(fileuri); g_free (fileuri); } } else if (G_IS_BYTES_ICON (icon)) { gsize size; gconstpointer data; gchar *base64; data = g_bytes_get_data (g_bytes_icon_get_bytes (G_BYTES_ICON (icon)), &size); base64 = g_base64_encode ((const guchar *) data, size); uri = QString("data://"); uri.append (base64); g_free (base64); } return uri; } QVariant AyatanaMenuModel::data(const QModelIndex &index, int role) const { GSequenceIter *it; GtkMenuTrackerItem *item; it = g_sequence_get_iter_at_pos (priv->items, index.row()); if (g_sequence_iter_is_end (it)) { return QVariant(); } item = (GtkMenuTrackerItem *) g_sequence_get (it); if (!item) { return QVariant(); } switch (role) { case LabelRole: return gtk_menu_tracker_item_get_label (item); case SensitiveRole: return gtk_menu_tracker_item_get_sensitive (item) == TRUE ? true : false; case IsSeparatorRole: return gtk_menu_tracker_item_get_is_separator (item) == TRUE ? true : false; case IconRole: { GIcon *icon = gtk_menu_tracker_item_get_icon (item); if (icon) { QString uri = iconUri(icon); g_object_unref (icon); return uri; } else return QString(); } case TypeRole: { gchar *type; auto ret = gtk_menu_tracker_item_get_attribute (item, "x-ayatana-type", "s", &type); // If we can't get x-ayatana-type, try legacy x-canonical-type type if (!ret) ret = gtk_menu_tracker_item_get_attribute (item, "x-canonical-type", "s", &type); if (ret) { QVariant v(type); g_free (type); return v; } else return QVariant(); } case ExtendedAttributesRole: { QVariantMap *map = (QVariantMap *) g_object_get_qdata (G_OBJECT (item), ayatana_menu_item_extended_attributes_quark ()); return map ? *map : QVariant(); } case ActionRole: { gchar *action_name = gtk_menu_tracker_item_get_action_name (item); QString v(action_name); g_free(action_name); return v; } case ActionStateRole: return priv->itemState(item); case IsCheckRole: return gtk_menu_tracker_item_get_role (item) == GTK_MENU_TRACKER_ITEM_ROLE_CHECK; case IsRadioRole: return gtk_menu_tracker_item_get_role (item) == GTK_MENU_TRACKER_ITEM_ROLE_RADIO; case IsToggledRole: return gtk_menu_tracker_item_get_toggled (item) != FALSE; case ShortcutRole: return QKeySequence(gtk_menu_tracker_item_get_accel (item), QKeySequence::NativeText); case HasSubmenuRole: return gtk_menu_tracker_item_get_has_submenu (item) != FALSE; default: return QVariant(); } } QModelIndex AyatanaMenuModel::index(int row, int column, const QModelIndex &parent) const { return createIndex(row, column); } QModelIndex AyatanaMenuModel::parent(const QModelIndex &index) const { return QModelIndex(); } QHash AyatanaMenuModel::roleNames() const { QHash names; names[LabelRole] = "label"; names[SensitiveRole] = "sensitive"; names[IsSeparatorRole] = "isSeparator"; names[IconRole] = "icon"; names[TypeRole] = "type"; names[ExtendedAttributesRole] = "ext"; names[ActionRole] = "action"; names[ActionStateRole] = "actionState"; names[IsCheckRole] = "isCheck"; names[IsRadioRole] = "isRadio"; names[IsToggledRole] = "isToggled"; names[ShortcutRole] = "shortcut"; names[HasSubmenuRole] = "hasSubmenu"; return names; } QObject * AyatanaMenuModel::submenu(int position, QQmlComponent* actionStateParser) { GSequenceIter *it; GtkMenuTrackerItem *item; AyatanaMenuModel *model; it = g_sequence_get_iter_at_pos (priv->items, position); if (g_sequence_iter_is_end (it)) { return NULL; } item = (GtkMenuTrackerItem *) g_sequence_get (it); if (!item || !gtk_menu_tracker_item_get_has_submenu (item)) { return NULL; } model = (AyatanaMenuModel *) g_object_get_qdata (G_OBJECT (item), ayatana_submenu_model_quark ()); if (model == NULL) { model = new AyatanaMenuModel(*priv, this); if (actionStateParser) { ActionStateParser* parser = qobject_cast(actionStateParser->create()); if (parser) { model->setActionStateParser(parser); } } model->priv->menutracker = gtk_menu_tracker_new_for_item_submenu (item, AyatanaMenuModelPrivate::menuItemInserted, AyatanaMenuModelPrivate::menuItemRemoved, model->priv); g_object_set_qdata (G_OBJECT (item), ayatana_submenu_model_quark (), model); } return model; } static void freeExtendedAttrs(gpointer data) { QVariantMap *extendedAttrs = (QVariantMap *) data; delete extendedAttrs; } static QVariant attributeToQVariant(GVariant *value, const QString &type) { QVariant result; if (type == "int") { if (g_variant_is_of_type (value, G_VARIANT_TYPE_INT32)) { result = QVariant(g_variant_get_int32(value)); } } else if (type == "int64") { if (g_variant_is_of_type (value, G_VARIANT_TYPE_INT64)) { result = QVariant((qlonglong)g_variant_get_int64(value)); } } else if (type == "bool") { if (g_variant_is_of_type (value, G_VARIANT_TYPE_BOOLEAN)) { result = QVariant(g_variant_get_boolean(value)); } } else if (type == "string") { if (g_variant_is_of_type (value, G_VARIANT_TYPE_STRING)) { result = QVariant(g_variant_get_string(value, NULL)); } } else if (type == "double") { if (g_variant_is_of_type (value, G_VARIANT_TYPE_DOUBLE)) { result = QVariant(g_variant_get_double(value)); } } else if (type == "variant") { if (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT)) { result = Converter::toQVariant(value); } } else if (type == "icon") { GIcon *icon = g_icon_deserialize (value); if (icon) { result = iconUri(icon); g_object_unref (icon); } else { result = QVariant(""); } } return result; } /* convert 'some-key' to 'someKey' or 'SomeKey'. (from dconf-qt) */ static QString qtify_name(const char *name) { bool next_cap = false; QString result; while (*name) { if (*name == '-') { next_cap = true; } else if (next_cap) { result.append(toupper(*name)); next_cap = false; } else { result.append(*name); } name++; } return result; } bool AyatanaMenuModel::loadExtendedAttributes(int position, const QVariantMap &schema) { GSequenceIter *it; GtkMenuTrackerItem *item; QVariantMap *extendedAttrs; it = g_sequence_get_iter_at_pos (priv->items, position); if (g_sequence_iter_is_end (it)) { return false; } item = (GtkMenuTrackerItem *) g_sequence_get (it); if (!item) { return false; } extendedAttrs = new QVariantMap; for (QVariantMap::const_iterator it = schema.constBegin(); it != schema.constEnd(); ++it) { QString name = it.key(); QString type = it.value().toString(); GVariant *value = gtk_menu_tracker_item_get_attribute_value (item, name.toUtf8(), NULL); if (value == NULL) { qCWarning(ayatanamenumodel, "loadExtendedAttributes: menu item does not contain '%s'", it.key().toUtf8().constData()); continue; } const QVariant &qvalue = attributeToQVariant(value, type); if (qvalue.isValid()) extendedAttrs->insert(qtify_name (name.toUtf8()), qvalue); else qCWarning(ayatanamenumodel, "loadExtendedAttributes: key '%s' is of type '%s' (expected '%s')", name.toUtf8().constData(), g_variant_get_type_string(value), type.toUtf8().constData()); g_variant_unref (value); } g_object_set_qdata_full (G_OBJECT (item), ayatana_menu_item_extended_attributes_quark (), extendedAttrs, freeExtendedAttrs); Q_EMIT dataChanged(index(position, 0), index(position, 0), QVector() << ExtendedAttributesRole); return true; } QVariant AyatanaMenuModel::get(int row, const QByteArray &role) { if (priv->roles.isEmpty()) { QHash names = roleNames(); Q_FOREACH (int role, names.keys()) priv->roles.insert(names[role], role); } return this->data(this->index(row, 0), priv->roles[role]); } void AyatanaMenuModel::activate(int index, const QVariant& parameter) { GSequenceIter *it; GtkMenuTrackerItem *item; GVariant *value; const GVariantType *parameter_type; it = g_sequence_get_iter_at_pos (priv->items, index); if (g_sequence_iter_is_end (it)) { return; } item = (GtkMenuTrackerItem *) g_sequence_get (it); if (!item) { return; } if (parameter.isValid()) { gchar *action; action = gtk_menu_tracker_item_get_action_name (item); parameter_type = g_action_group_get_action_parameter_type (G_ACTION_GROUP (priv->muxer), action); value = Converter::toGVariantWithSchema(parameter, g_variant_type_peek_string (parameter_type)); g_action_group_activate_action (G_ACTION_GROUP (priv->muxer), action, value); g_free (action); } else { gtk_menu_tracker_item_activated (item); } } void AyatanaMenuModel::aboutToShow(int index) { GSequenceIter *it = g_sequence_get_iter_at_pos (priv->items, index); if (g_sequence_iter_is_end (it)) { return; } auto item = static_cast(g_sequence_get(it)); if (!item) { return; } quint64 actionTag; if (gtk_menu_tracker_item_get_attribute (item, "qtubuntu-tag", "t", &actionTag)) { // Child AyatanaMenuModel have priv->connection null, so climb to the parent until we find a non null one AyatanaMenuModelPrivate *privToUse = priv; while (privToUse && !privToUse->connection) { auto pModel = dynamic_cast(privToUse->model->QObject::parent()); if (pModel) { privToUse = pModel->priv; } else { privToUse = nullptr; } } if (privToUse) { g_dbus_connection_call (privToUse->connection, privToUse->busName, privToUse->menuObjectPath, "qtubuntu.actions.extra", "aboutToShow", g_variant_new("(t)", actionTag), nullptr, G_DBUS_CALL_FLAGS_NO_AUTO_START, G_MAXINT, nullptr, nullptr, nullptr); } } } void AyatanaMenuModel::activateByVariantString(int index, const QString& parameter) { activate(index, Converter::toQVariantFromVariantString(parameter)); } void AyatanaMenuModel::changeStateByVariantString(int index, const QString& parameter) { changeState(index, Converter::toQVariantFromVariantString(parameter)); } void AyatanaMenuModel::changeState(int index, const QVariant& parameter) { GSequenceIter *it; GtkMenuTrackerItem* item; GVariant* data; GVariant* current_state; it = g_sequence_get_iter_at_pos (priv->items, index); if (g_sequence_iter_is_end (it)) { return; } item = (GtkMenuTrackerItem *) g_sequence_get (it); if (!item) { return; } current_state = gtk_menu_tracker_item_get_action_state (item); if (current_state) { // Attempt to convert the parameter to the expected type data = Converter::toGVariantWithSchema(parameter, g_variant_get_type_string(current_state)); g_variant_unref (current_state); } else { data = Converter::toGVariant(parameter); } gtk_menu_tracker_item_change_state (item, data); if (data) { g_variant_unref(data); } } bool AyatanaMenuModel::event(QEvent* e) { if (e->type() == AyatanaMenuModelClearEvent::eventType) { AyatanaMenuModelClearEvent *emmce = static_cast(e); GSequenceIter *begin; GSequenceIter *end; if (emmce->reset) beginResetModel(); begin = g_sequence_get_begin_iter (priv->items); end = g_sequence_get_end_iter (priv->items); g_sequence_remove_range (begin, end); if (emmce->reset) endResetModel(); return true; } else if (e->type() == AyatanaMenuModelAddRowEvent::eventType) { AyatanaMenuModelAddRowEvent *ummrce = static_cast(e); GSequenceIter *it; it = g_sequence_get_iter_at_pos (priv->items, ummrce->position); beginInsertRows(QModelIndex(), ummrce->position, ummrce->position + ummrce->items->len - 1); for (gint i = ummrce->items->len - 1; i >= 0; --i) { GtkMenuTrackerItem *item = (GtkMenuTrackerItem*)g_ptr_array_index(ummrce->items, i); it = g_sequence_insert_before (it, g_object_ref (item)); g_object_set_qdata (G_OBJECT (item), ayatana_menu_model_quark (), this); g_signal_connect (item, "notify", G_CALLBACK (AyatanaMenuModelPrivate::menuItemChanged), it); } endInsertRows(); return true; } else if (e->type() == AyatanaMenuModelRemoveRowEvent::eventType) { AyatanaMenuModelRemoveRowEvent *ummrre = static_cast(e); beginRemoveRows(QModelIndex(), ummrre->position, ummrre->position + ummrre->nItems - 1); for (int i = 0; i < ummrre->nItems; ++i) { GSequenceIter *it = g_sequence_get_iter_at_pos (priv->items, ummrre->position); if (!g_sequence_iter_is_end (it)) { g_sequence_remove (it); } } endRemoveRows(); return true; } else if (e->type() == AyatanaMenuModelDataChangeEvent::eventType) { AyatanaMenuModelDataChangeEvent *ummdce = static_cast(e); Q_EMIT dataChanged(index(ummdce->position, 0), index(ummdce->position, 0)); return true; } return QAbstractListModel::event(e); } void AyatanaMenuModel::registerAction(AyatanaMenuAction* action) { if (priv->destructorGuard) return; if (!priv->registeredActions.contains(action)) { GtkSimpleActionObserver* observer_item; observer_item = gtk_simple_action_observer_new(GTK_ACTION_OBSERVABLE (priv->muxer), AyatanaMenuModelPrivate::registeredActionAdded, AyatanaMenuModelPrivate::registeredActionEnabledChanged, AyatanaMenuModelPrivate::registeredActionStateChanged, AyatanaMenuModelPrivate::registeredActionRemoved); g_object_set_qdata (G_OBJECT (observer_item), ayatana_menu_action_quark (), action); priv->registeredActions[action] = observer_item; connect(action, SIGNAL(nameChanged(const QString&)), SLOT(onRegisteredActionNameChanged(const QString&))); connect(action, SIGNAL(indexChanged(int)), SLOT(onRegisteredActionIndexChanged(int))); connect(action, SIGNAL(activate(const QVariant&)), SLOT(onRegisteredActionActivated(const QVariant&))); connect(action, SIGNAL(changeState(const QVariant&)), SLOT(onRegisteredActionStateChanged(const QVariant&))); } } void AyatanaMenuModel::unregisterAction(AyatanaMenuAction* action) { if (priv->destructorGuard) return; if (priv->registeredActions.contains(action)) { GtkSimpleActionObserver* observer_item; observer_item = priv->registeredActions[action]; g_object_unref(observer_item); priv->registeredActions.remove(action); disconnect(action); } } /* Returns the full name for @action * * If @action is associated with a menu item that is inside of a * section or submenu with "action-namespace" set, this namespace * is prepended to @action->name() */ char * AyatanaMenuModelPrivate::fullActionName(AyatanaMenuAction *action) { GSequenceIter *iter; QByteArray bytes; const gchar *name; bytes = action->name().toUtf8(); name = bytes.constData(); iter = g_sequence_get_iter_at_pos (this->items, action->index()); if (!g_sequence_iter_is_end (iter)) { GtkMenuTrackerItem *item; const gchar *action_namespace; item = (GtkMenuTrackerItem *) g_sequence_get (iter); if (!item) { return g_strdup (name); } action_namespace = gtk_menu_tracker_item_get_action_namespace (item); if (action_namespace != NULL) return g_strjoin (".", action_namespace, name, NULL); } return g_strdup (name); } void AyatanaMenuModelPrivate::updateRegisteredAction(AyatanaMenuAction *action) { GtkSimpleActionObserver *observer_item; gchar *action_name; gboolean enabled; GVariant *state; if (!action || !this->registeredActions.contains(action)) return; action_name = fullActionName(action); observer_item = this->registeredActions[action]; gtk_simple_action_observer_register_action (observer_item, action_name); if (g_action_group_query_action (G_ACTION_GROUP (this->muxer), action_name, &enabled, NULL, NULL, NULL, &state)) { AyatanaMenuActionAddEvent umaae(enabled, Converter::toQVariant(state)); QCoreApplication::sendEvent(action, &umaae); if (state) { g_variant_unref (state); } } g_free(action_name); } void AyatanaMenuModel::onRegisteredActionNameChanged(const QString& name) { priv->updateRegisteredAction(qobject_cast(sender())); } void AyatanaMenuModel::onRegisteredActionIndexChanged(int index) { priv->updateRegisteredAction(qobject_cast(sender())); } void AyatanaMenuModel::onRegisteredActionActivated(const QVariant& parameter) { AyatanaMenuAction* action = qobject_cast(sender()); if (!action || action->name().isEmpty()) return; gchar* action_name = priv->fullActionName(action); g_action_group_activate_action (G_ACTION_GROUP (priv->muxer), action_name, Converter::toGVariant(parameter)); g_free(action_name); } void AyatanaMenuModel::onRegisteredActionStateChanged(const QVariant& parameter) { AyatanaMenuAction* action = qobject_cast(sender()); if (!action || action->name().isEmpty()) return; gchar* action_name = priv->fullActionName(action); g_action_group_change_action_state (G_ACTION_GROUP (priv->muxer), action_name, Converter::toGVariant(parameter)); g_free(action_name); } void AyatanaMenuModelPrivate::registeredActionAdded(GtkSimpleActionObserver *observer_item, const gchar *action_name, gboolean enabled, GVariant *state) { AyatanaMenuAction *action; action = (AyatanaMenuAction *) g_object_get_qdata (G_OBJECT (observer_item), ayatana_menu_action_quark ()); if (action) { AyatanaMenuActionAddEvent umaae(enabled, Converter::toQVariant(state)); QCoreApplication::sendEvent(action, &umaae); } } void AyatanaMenuModelPrivate::registeredActionEnabledChanged(GtkSimpleActionObserver *observer_item, const gchar *action_name, gboolean enabled) { AyatanaMenuAction *action; action = (AyatanaMenuAction *) g_object_get_qdata (G_OBJECT (observer_item), ayatana_menu_action_quark ()); if (action) { AyatanaMenuActionEnabledChangedEvent umaece(enabled); QCoreApplication::sendEvent(action, &umaece); } } void AyatanaMenuModelPrivate::registeredActionStateChanged(GtkSimpleActionObserver *observer_item, const gchar *action_name, GVariant *state) { AyatanaMenuAction *action; action = (AyatanaMenuAction *) g_object_get_qdata (G_OBJECT (observer_item), ayatana_menu_action_quark ()); if (action) { AyatanaMenuActionStateChangeEvent umasce(Converter::toQVariant(state)); QCoreApplication::sendEvent(action, &umasce); } } void AyatanaMenuModelPrivate::registeredActionRemoved(GtkSimpleActionObserver *observer_item, const gchar *action_name) { AyatanaMenuAction *action; action = (AyatanaMenuAction *) g_object_get_qdata (G_OBJECT (observer_item), ayatana_menu_action_quark ()); if (action) { AyatanaMenuActionRemoveEvent umare; QCoreApplication::sendEvent(action, &umare); } } qmenumodel-0.9.1/libqmenumodel/src/ayatanamenumodelevents.cpp0000644000000000000000000000463014200511032021445 0ustar /* * Copyright 2013 Canonical Ltd. * Copyright 2022 Robert Tari * * 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: * Nicholas Dedekind */ #include #include #include "ayatanamenumodelevents.h" #include "ayatanamenumodel.h" const QEvent::Type AyatanaMenuModelClearEvent::eventType = static_cast(QEvent::registerEventType()); const QEvent::Type AyatanaMenuModelAddRowEvent::eventType = static_cast(QEvent::registerEventType()); const QEvent::Type AyatanaMenuModelRemoveRowEvent::eventType = static_cast(QEvent::registerEventType()); const QEvent::Type AyatanaMenuModelDataChangeEvent::eventType = static_cast(QEvent::registerEventType()); AyatanaMenuModelClearEvent::AyatanaMenuModelClearEvent(bool _reset) : QEvent(AyatanaMenuModelClearEvent::eventType), reset(_reset) {} AyatanaMenuModelAddRowEvent::AyatanaMenuModelAddRowEvent(GPtrArray *_items, int _position) : QEvent(AyatanaMenuModelAddRowEvent::eventType), items(_items), position(_position) { if (items) { for (guint i = 0; i < items->len; ++i) g_object_ref(g_ptr_array_index(items, i)); g_ptr_array_ref(items); } } AyatanaMenuModelAddRowEvent::~AyatanaMenuModelAddRowEvent() { if (items) { for (guint i = 0; i < items->len; ++i) g_object_unref(g_ptr_array_index(items, i)); g_ptr_array_unref(items); } } AyatanaMenuModelRemoveRowEvent::AyatanaMenuModelRemoveRowEvent(int _position, int _nItems) : QEvent(AyatanaMenuModelRemoveRowEvent::eventType), position(_position), nItems(_nItems) {} AyatanaMenuModelDataChangeEvent::AyatanaMenuModelDataChangeEvent(int _position) : QEvent(AyatanaMenuModelDataChangeEvent::eventType), position(_position) {} qmenumodel-0.9.1/libqmenumodel/src/ayatanamenumodelevents.h0000644000000000000000000000355514200511032021117 0ustar /* * Copyright 2013 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: * Nicholas Dedekind #include typedef struct _GtkMenuTrackerItem GtkMenuTrackerItem; /* Event for a ayatanamenumodel clear */ class AyatanaMenuModelClearEvent : public QEvent { public: static const QEvent::Type eventType; AyatanaMenuModelClearEvent(bool reset); bool reset; }; /* Event for a row add for ayatanamenumodel */ class AyatanaMenuModelAddRowEvent : public QEvent { public: static const QEvent::Type eventType; AyatanaMenuModelAddRowEvent(GPtrArray *_items, int position); ~AyatanaMenuModelAddRowEvent(); GPtrArray *items; int position; }; /* Event for a row remove for ayatanamenumodel */ class AyatanaMenuModelRemoveRowEvent : public QEvent { public: static const QEvent::Type eventType; AyatanaMenuModelRemoveRowEvent(int position, int nItems); int position; int nItems; }; /* Event for a row data change for ayatanamenumodel */ class AyatanaMenuModelDataChangeEvent : public QEvent { public: static const QEvent::Type eventType; AyatanaMenuModelDataChangeEvent(int position); int position; }; #endif //AYATANAMENUMODELEVENTS_H qmenumodel-0.9.1/libqmenumodel/src/ayatanamenumodel.h0000644000000000000000000000741514200511032017671 0ustar /* * Copyright 2013 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: Lars Uebernickel */ #ifndef AYATANAMENUMODEL_H #define AYATANAMENUMODEL_H #include class ActionStateParser; class QQmlComponent; class AyatanaMenuAction; class AyatanaMenuModel: public QAbstractListModel { Q_OBJECT Q_PROPERTY(QByteArray busName READ busName WRITE setBusName NOTIFY busNameChanged) Q_PROPERTY(QByteArray nameOwner READ nameOwner NOTIFY nameOwnerChanged) Q_PROPERTY(QVariantMap actions READ actions WRITE setActions NOTIFY actionsChanged) Q_PROPERTY(QByteArray menuObjectPath READ menuObjectPath WRITE setMenuObjectPath NOTIFY menuObjectPathChanged) Q_PROPERTY(ActionStateParser* actionStateParser READ actionStateParser WRITE setActionStateParser NOTIFY actionStateParserChanged) public: AyatanaMenuModel(QObject *parent = NULL); virtual ~AyatanaMenuModel(); QByteArray busName() const; void setBusName(const QByteArray &name); QByteArray nameOwner() const; QVariantMap actions() const; void setActions(const QVariantMap &actions); QByteArray menuObjectPath() const; void setMenuObjectPath(const QByteArray &path); ActionStateParser* actionStateParser() const; void setActionStateParser(ActionStateParser* actionStateParser); 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_INVOKABLE QObject * submenu(int position, QQmlComponent* actionStateParser = NULL); Q_INVOKABLE bool loadExtendedAttributes(int position, const QVariantMap &schema); Q_INVOKABLE QVariant get(int row, const QByteArray &role); Q_INVOKABLE void activate(int index, const QVariant& parameter = QVariant()); Q_INVOKABLE void aboutToShow(int index); Q_INVOKABLE void activateByVariantString(int index, const QString& parameter = QString()); Q_INVOKABLE void changeState(int index, const QVariant& parameter); Q_INVOKABLE void changeStateByVariantString(int index, const QString& parameter); void registerAction(AyatanaMenuAction* action); void unregisterAction(AyatanaMenuAction* action); Q_SIGNALS: void busNameChanged(const QByteArray &name); void nameOwnerChanged(const QByteArray &owner); void actionsChanged(const QByteArray &path); void menuObjectPathChanged(const QByteArray &path); void actionStateParserChanged(ActionStateParser* parser); protected Q_SLOTS: void onRegisteredActionNameChanged(const QString& name); void onRegisteredActionIndexChanged(int); void onRegisteredActionActivated(const QVariant& parameter); void onRegisteredActionStateChanged(const QVariant& parameter); protected: virtual bool event(QEvent* e); private: class AyatanaMenuModelPrivate *priv; friend class AyatanaMenuModelPrivate; AyatanaMenuModel(const AyatanaMenuModelPrivate& other, AyatanaMenuModel *parent); }; #endif qmenumodel-0.9.1/libqmenumodel/src/CMakeLists.txt0000644000000000000000000000332014200511032016723 0ustar project(src) set(QMENUMODEL_SRC actionstateparser.cpp converter.cpp dbus-enums.h menunode.cpp qmenumodel.cpp qdbusobject.cpp qdbusmenumodel.cpp qdbusactiongroup.cpp qmenumodelevents.cpp qstateaction.cpp ayatanamenuaction.cpp ayatanamenuactionevents.cpp ayatanamenumodel.cpp ayatanamenumodelevents.cpp gtk/gtkactionmuxer.c gtk/gtkactionmuxer.h gtk/gtkactionobservable.c gtk/gtkactionobservable.h gtk/gtkactionobserver.c gtk/gtkactionobserver.h gtk/gtksimpleactionobserver.c gtk/gtksimpleactionobserver.h gtk/gtkmenutracker.c gtk/gtkmenutracker.h gtk/gtkmenutrackeritem.c gtk/gtkmenutrackeritem.h ) set(SHAREDLIBNAME qmenumodel) add_library(${SHAREDLIBNAME} SHARED ${QMENUMODEL_SRC} ) set_target_properties(${SHAREDLIBNAME} PROPERTIES COMPILE_FLAGS -fPIC SOVERSION 1 VERSION 1.0.0 ) include_directories( ${GLIB_INCLUDE_DIRS} ${GIO_INCLUDE_DIRS} ) find_package(Qt5Quick REQUIRED) target_link_libraries(${SHAREDLIBNAME} ${GLIB_LDFLAGS} ${GIO_LDFLAGS} Qt5::Core Qt5::Qml Qt5::Quick ) install(TARGETS ${SHAREDLIBNAME} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}) set(QMENUMODEL_HEADERS actionstateparser.h dbus-enums.h qdbusactiongroup.h qdbusmenumodel.h qdbusobject.h qmenumodel.h qstateaction.h ayatanamenuaction.h ayatanamenumodel.h ) set(INCLUDEDIR qmenumodel) install(FILES ${QMENUMODEL_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${INCLUDEDIR} ) set(PCFILE qmenumodel.pc) configure_file(${PCFILE}.in ${CMAKE_CURRENT_BINARY_DIR}/${PCFILE} @ONLY) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${PCFILE} DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig ) qmenumodel-0.9.1/libqmenumodel/src/converter.cpp0000644000000000000000000003252214200511032016704 0ustar /* * Copyright 2012-2016 Canonical Ltd. * Copyright 2022 Robert Tari * * 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 * Marco Trevisan * Robert Tari */ #include #include "converter.h" #include #include #include /*! \internal */ QVariant Converter::toQVariant(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(qint16(g_variant_get_int16(value))); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT16)) { result.setValue(quint16(g_variant_get_uint16(value))); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT32)) { result.setValue(qint32(g_variant_get_int32(value))); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT32)) { result.setValue(quint32(g_variant_get_uint32(value))); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_INT64)) { result.setValue(qint64(g_variant_get_int64(value))); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_UINT64)) { result.setValue(quint64(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::fromUtf8(v, size)); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_STRING_ARRAY)) { gsize size = 0; const gchar **sa = g_variant_get_strv(value, &size); QStringList list; for (gsize i = 0; i < size; ++i) { list << QString::fromUtf8(sa[i]); } result.setValue(list); g_free(sa); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTESTRING)) { result.setValue(QByteArray(g_variant_get_bytestring(value))); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_BYTESTRING_ARRAY)) { gsize size = 0; const gchar **bsa = g_variant_get_bytestring_array(value, &size); QByteArrayList list; for (gsize i = 0; i < size; ++i) { list << bsa[i]; } result.setValue(list); g_free(bsa); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_VARIANT)) { GVariant *var = g_variant_get_variant(value); result = toQVariant(var); g_variant_unref(var); } else if (g_variant_type_equal(type, G_VARIANT_TYPE_VARDICT)) { GVariantIter iter; GVariant *vvalue; gchar *key; QVariantMap qmap; g_variant_iter_init (&iter, value); while (g_variant_iter_loop (&iter, "{sv}", &key, &vvalue)) { qmap.insert(QString::fromUtf8(key), toQVariant(vvalue)); } result.setValue(qmap); } else if (g_variant_type_is_array(type)) { QVariantList lst; for (int i = 0, iMax = g_variant_n_children(value); i < iMax; i++) { GVariant *child = g_variant_get_child_value(value, i); lst << toQVariant(child); g_variant_unref(child); } result.setValue(lst); } else if (g_variant_type_is_tuple(type)) { gsize size = g_variant_n_children(value); QVariantList vlist; for (gsize i=0; i < size; i++) { GVariant *v = g_variant_get_child_value(value, i); if (v) { vlist << toQVariant(v); g_variant_unref(v); } } result.setValue(vlist); } else { qWarning() << "Unsupported GVariant value" << (char*) type; } /* TODO: implement convertions to others types * G_VARIANT_TYPE_HANDLE * G_VARIANT_TYPE_OBJECT_PATH * G_VARIANT_TYPE_SIGNATURE * G_VARIANT_TYPE_ANY * G_VARIANT_TYPE_BASIC * G_VARIANT_TYPE_MAYBE * G_VARIANT_TYPE_UNIT * G_VARIANT_TYPE_DICT_ENTRY * G_VARIANT_TYPE_DICTIONARY * G_VARIANT_TYPE_OBJECT_PATH_ARRAY */ return result; } QVariant Converter::toQVariantFromVariantString(const QString &variantString) { GVariant *gvariant; GError *error = NULL; if (variantString.isEmpty()) { return QVariant(); } gvariant = g_variant_parse (NULL, qUtf8Printable(variantString), NULL, NULL, &error); if (error) { qWarning() << "Impossible to parse" << variantString << "as variant string:"<< error->message; g_error_free (error); return QVariant(); } const QVariant& qvariant = Converter::toQVariant(gvariant); g_variant_unref (gvariant); return qvariant; } GVariant* Converter::toGVariant(const QVariant &value) { GVariant *result = NULL; if (value.isNull() || !value.isValid()) return result; switch((QMetaType::Type)value.type()) { case QMetaType::Bool: result = g_variant_new_boolean(value.toBool()); break; case QMetaType::QByteArray: result = g_variant_new_bytestring(value.toByteArray()); break; case QMetaType::Double: result = g_variant_new_double(value.toDouble()); break; case QMetaType::Int: result = g_variant_new_int32(value.toInt()); break; case QMetaType::LongLong: result = g_variant_new_int64(value.toLongLong()); break; case QMetaType::QString: result = g_variant_new_string(qUtf8Printable(value.toString())); break; case QMetaType::UInt: result = g_variant_new_uint32(value.toUInt()); break; case QMetaType::ULongLong: result = g_variant_new_uint64(value.toULongLong()); break; case QMetaType::UChar: result = g_variant_new_byte(value.value()); break; case QMetaType::Short: result = g_variant_new_int16(value.value()); break; case QMetaType::UShort: result = g_variant_new_uint16(value.value()); break; case QMetaType::Long: result = g_variant_new_int64(value.value()); break; case QMetaType::ULong: result = g_variant_new_uint64(value.value()); break; case QMetaType::QVariantMap: { GVariantBuilder *b; b = g_variant_builder_new(G_VARIANT_TYPE_VARDICT); QMapIterator i(value.toMap()); while (i.hasNext()) { i.next(); g_variant_builder_add(b, "{sv}", qUtf8Printable(i.key()), toGVariant(i.value())); } result = g_variant_builder_end(b); g_variant_builder_unref(b); break; } case QMetaType::QByteArrayList: { const QByteArrayList &list = qvariant_cast(value); GVariantBuilder *b = g_variant_builder_new(G_VARIANT_TYPE_BYTESTRING_ARRAY); for (const QByteArray &ba : list) { g_variant_builder_add_value(b, g_variant_new_bytestring(ba)); } result = g_variant_builder_end(b); g_variant_builder_unref(b); break; } case QMetaType::QVariantList: { GVariantBuilder *b = g_variant_builder_new(G_VARIANT_TYPE_TUPLE); for (const QVariant &v : value.toList()) { g_variant_builder_add_value(b, toGVariant(v)); } result = g_variant_builder_end(b); g_variant_builder_unref(b); break; } case QMetaType::QStringList: { GVariantBuilder *b = g_variant_builder_new(G_VARIANT_TYPE_STRING_ARRAY); for (const QString &s : value.toStringList()) { g_variant_builder_add(b, "s", qUtf8Printable(s)); } result = g_variant_builder_end(b); g_variant_builder_unref(b); break; } default: qWarning() << "QVariant type not supported:" << value.type(); } return result; } GVariant* Converter::toGVariantWithSchema(const QVariant &value, const char* schema) { if (!g_variant_type_string_is_valid(schema)) { return Converter::toGVariant(value); } GVariant* result = NULL; GVariantType* schema_type = g_variant_type_new(schema); if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_BOOLEAN)) { if (value.canConvert()) { result = g_variant_new_boolean (value.value()); } } else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_BYTE)) { if (value.canConvert()) { result = g_variant_new_byte (value.value()); } } else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_INT16)) { if (value.canConvert()) { result = g_variant_new_int16 (value.value()); } } else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_UINT16)) { if (value.canConvert()) { result = g_variant_new_uint16 (value.value()); } } else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_INT32)) { if (value.canConvert()) { result = g_variant_new_int32 (value.value()); } } else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_UINT32)) { if (value.canConvert()) { result = g_variant_new_uint32 (value.value()); } } else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_INT64)) { if (value.canConvert()) { result = g_variant_new_int64 (value.value()); } } else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_UINT64)) { if (value.canConvert()) { result = g_variant_new_uint64 (value.value()); } } else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_DOUBLE)) { if (value.canConvert()) { result = g_variant_new_double (value.value()); } } else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_STRING)) { if (value.canConvert()) { result = g_variant_new_string(qUtf8Printable(value.toString())); } } else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_VARIANT)) { result = g_variant_new_variant(Converter::toGVariant(value)); } else if (g_variant_type_equal(schema_type, G_VARIANT_TYPE_VARDICT)) { if (value.canConvert(QVariant::Map)) { result = Converter::toGVariant(value.toMap()); } } else if (g_variant_type_is_array(schema_type)) { if (value.canConvert(QVariant::List)) { const GVariantType* entryType = g_variant_type_element(schema_type); const gchar* entryTypeString = g_variant_type_peek_string(entryType); GVariantBuilder *b = g_variant_builder_new(G_VARIANT_TYPE_ARRAY); bool ok = true; for (const QVariant &v : value.toList()) { GVariant *data = toGVariantWithSchema(v, entryTypeString); if (data) { g_variant_builder_add_value(b, data); } else { ok = false; qWarning() << "Failed to convert list to array with schema:" << schema; break; } } if (ok) { result = g_variant_builder_end(b); } g_variant_builder_unref(b); } } else if (g_variant_type_is_tuple(schema_type)) { if (value.canConvert(QVariant::List)) { const GVariantType* entryType = g_variant_type_first(schema_type); GVariantBuilder *b = g_variant_builder_new(G_VARIANT_TYPE_TUPLE); bool ok = true; for (const QVariant &v : value.toList()) { gchar* entryTypeString = g_variant_type_dup_string(entryType); GVariant *data = toGVariantWithSchema(v, entryTypeString); g_free(entryTypeString); if (data) { g_variant_builder_add_value(b, data); entryType = g_variant_type_next(entryType); if (!entryType) break; } else { ok = false; qWarning() << "Failed to convert list to array with schema:" << schema; break; } } if (ok) { result = g_variant_builder_end(b); } g_variant_builder_unref(b); } } // fallback to straight convert. if (!result) { result = Converter::toGVariant(value); } g_free(schema_type); return result; } qmenumodel-0.9.1/libqmenumodel/src/converter.h0000644000000000000000000000243214200511032016346 0ustar /* * 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 CONVERTER_H #define CONVERTER_H typedef struct _GVariant GVariant; class QString; class QVariant; class Converter { public: static QVariant toQVariant(GVariant *value); static QVariant toQVariantFromVariantString(const QString &variantString); static GVariant* toGVariant(const QVariant &value); // This converts a QVariant to a GVariant using a provided gvariant schema as // a conversion base (it will attempt to convert to this format). static GVariant* toGVariantWithSchema(const QVariant &value, const char* schema); }; #endif // CONVERTER_H qmenumodel-0.9.1/libqmenumodel/src/dbus-enums.h0000644000000000000000000000237414200511032016426 0ustar /* * 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: * Olivier Tilloy */ #ifndef __DBUS_ENUMS__ #define __DBUS_ENUMS__ #include // This class acts as a namespace only, with the addition that its enums // are registered to be exposed on the QML side. class DBusEnums : public QObject { Q_OBJECT Q_ENUMS(BusType) Q_ENUMS(ConnectionStatus) public: enum BusType { None = 0, SessionBus, SystemBus, LastBusType }; enum ConnectionStatus { Disconnected = 0, Connecting, Connected }; private: DBusEnums() {} }; #endif // __DBUS_ENUMS__ qmenumodel-0.9.1/libqmenumodel/src/gtk/config.h0000644000000000000000000000000014200511032016356 0ustar qmenumodel-0.9.1/libqmenumodel/src/gtk/gtkactionmuxer.c0000644000000000000000000006114314200511032020167 0ustar /* * Copyright © 2011 Canonical Limited * * This library 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; either * version 2 of the licence, or (at your option) any later version. * * This library 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 library. If not, see . * * Author: Ryan Lortie */ #include "config.h" #include "gtkactionmuxer.h" #include "gtkactionobservable.h" #include "gtkactionobserver.h" #include /** * SECTION:gtkactionmuxer * @short_description: Aggregate and monitor several action groups * * #GtkActionMuxer is a #GActionGroup and #GtkActionObservable that is * capable of containing other #GActionGroup instances. * * The typical use is aggregating all of the actions applicable to a * particular context into a single action group, with namespacing. * * Consider the case of two action groups -- one containing actions * applicable to an entire application (such as 'quit') and one * containing actions applicable to a particular window in the * application (such as 'fullscreen'). * * In this case, each of these action groups could be added to a * #GtkActionMuxer with the prefixes "app" and "win", respectively. This * would expose the actions as "app.quit" and "win.fullscreen" on the * #GActionGroup interface presented by the #GtkActionMuxer. * * Activations and state change requests on the #GtkActionMuxer are wired * through to the underlying action group in the expected way. * * This class is typically only used at the site of "consumption" of * actions (eg: when displaying a menu that contains many actions on * different objects). */ static void gtk_action_muxer_group_iface_init (GActionGroupInterface *iface); static void gtk_action_muxer_observable_iface_init (GtkActionObservableInterface *iface); typedef GObjectClass GtkActionMuxerClass; struct _GtkActionMuxer { GObject parent_instance; GHashTable *observed_actions; GHashTable *groups; GtkActionMuxer *parent; }; G_DEFINE_TYPE_WITH_CODE (GtkActionMuxer, gtk_action_muxer, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_ACTION_GROUP, gtk_action_muxer_group_iface_init) G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTION_OBSERVABLE, gtk_action_muxer_observable_iface_init)) enum { PROP_0, PROP_PARENT, NUM_PROPERTIES }; static GParamSpec *properties[NUM_PROPERTIES]; typedef struct { GtkActionMuxer *muxer; GSList *watchers; gchar *fullname; } Action; typedef struct { GtkActionMuxer *muxer; GActionGroup *group; gchar *prefix; gulong handler_ids[4]; } Group; static void gtk_action_muxer_append_group_actions (gpointer key, gpointer value, gpointer user_data) { const gchar *prefix = key; Group *group = value; GArray *actions = user_data; gchar **group_actions; gchar **action; group_actions = g_action_group_list_actions (group->group); for (action = group_actions; *action; action++) { gchar *fullname; fullname = g_strconcat (prefix, ".", *action, NULL); g_array_append_val (actions, fullname); } g_strfreev (group_actions); } static gchar ** gtk_action_muxer_list_actions (GActionGroup *action_group) { GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group); GArray *actions; actions = g_array_new (TRUE, FALSE, sizeof (gchar *)); for ( ; muxer != NULL; muxer = muxer->parent) { g_hash_table_foreach (muxer->groups, gtk_action_muxer_append_group_actions, actions); } return (gchar **) g_array_free (actions, FALSE); } static Group * gtk_action_muxer_find_group (GtkActionMuxer *muxer, const gchar *full_name, const gchar **action_name) { const gchar *dot; gchar *prefix; Group *group; dot = strchr (full_name, '.'); if (!dot) return NULL; prefix = g_strndup (full_name, dot - full_name); group = g_hash_table_lookup (muxer->groups, prefix); g_free (prefix); if (action_name) *action_name = dot + 1; return group; } static void gtk_action_muxer_action_enabled_changed (GtkActionMuxer *muxer, const gchar *action_name, gboolean enabled) { Action *action; GSList *node; action = g_hash_table_lookup (muxer->observed_actions, action_name); for (node = action ? action->watchers : NULL; node; node = node->next) gtk_action_observer_action_enabled_changed (node->data, GTK_ACTION_OBSERVABLE (muxer), action_name, enabled); g_action_group_action_enabled_changed (G_ACTION_GROUP (muxer), action_name, enabled); } static void gtk_action_muxer_group_action_enabled_changed (GActionGroup *action_group, const gchar *action_name, gboolean enabled, gpointer user_data) { Group *group = user_data; gchar *fullname; fullname = g_strconcat (group->prefix, ".", action_name, NULL); gtk_action_muxer_action_enabled_changed (group->muxer, fullname, enabled); g_free (fullname); } static void gtk_action_muxer_parent_action_enabled_changed (GActionGroup *action_group, const gchar *action_name, gboolean enabled, gpointer user_data) { GtkActionMuxer *muxer = user_data; gtk_action_muxer_action_enabled_changed (muxer, action_name, enabled); } static void gtk_action_muxer_action_state_changed (GtkActionMuxer *muxer, const gchar *action_name, GVariant *state) { Action *action; GSList *node; action = g_hash_table_lookup (muxer->observed_actions, action_name); for (node = action ? action->watchers : NULL; node; node = node->next) gtk_action_observer_action_state_changed (node->data, GTK_ACTION_OBSERVABLE (muxer), action_name, state); g_action_group_action_state_changed (G_ACTION_GROUP (muxer), action_name, state); } static void gtk_action_muxer_group_action_state_changed (GActionGroup *action_group, const gchar *action_name, GVariant *state, gpointer user_data) { Group *group = user_data; gchar *fullname; fullname = g_strconcat (group->prefix, ".", action_name, NULL); gtk_action_muxer_action_state_changed (group->muxer, fullname, state); g_free (fullname); } static void gtk_action_muxer_parent_action_state_changed (GActionGroup *action_group, const gchar *action_name, GVariant *state, gpointer user_data) { GtkActionMuxer *muxer = user_data; gtk_action_muxer_action_state_changed (muxer, action_name, state); } static void gtk_action_muxer_action_added (GtkActionMuxer *muxer, const gchar *action_name, GActionGroup *original_group, const gchar *orignal_action_name) { const GVariantType *parameter_type; gboolean enabled; GVariant *state; Action *action; action = g_hash_table_lookup (muxer->observed_actions, action_name); if (action && action->watchers && g_action_group_query_action (original_group, orignal_action_name, &enabled, ¶meter_type, NULL, NULL, &state)) { GSList *node; for (node = action->watchers; node; node = node->next) gtk_action_observer_action_added (node->data, GTK_ACTION_OBSERVABLE (muxer), action_name, parameter_type, enabled, state); if (state) g_variant_unref (state); } g_action_group_action_added (G_ACTION_GROUP (muxer), action_name); } static void gtk_action_muxer_action_added_to_group (GActionGroup *action_group, const gchar *action_name, gpointer user_data) { Group *group = user_data; gchar *fullname; fullname = g_strconcat (group->prefix, ".", action_name, NULL); gtk_action_muxer_action_added (group->muxer, fullname, action_group, action_name); g_free (fullname); } static void gtk_action_muxer_action_added_to_parent (GActionGroup *action_group, const gchar *action_name, gpointer user_data) { GtkActionMuxer *muxer = user_data; gtk_action_muxer_action_added (muxer, action_name, action_group, action_name); } static void gtk_action_muxer_action_removed (GtkActionMuxer *muxer, const gchar *action_name) { Action *action; GSList *node; action = g_hash_table_lookup (muxer->observed_actions, action_name); for (node = action ? action->watchers : NULL; node; node = node->next) gtk_action_observer_action_removed (node->data, GTK_ACTION_OBSERVABLE (muxer), action_name); g_action_group_action_removed (G_ACTION_GROUP (muxer), action_name); } static void gtk_action_muxer_action_removed_from_group (GActionGroup *action_group, const gchar *action_name, gpointer user_data) { Group *group = user_data; gchar *fullname; fullname = g_strconcat (group->prefix, ".", action_name, NULL); gtk_action_muxer_action_removed (group->muxer, fullname); g_free (fullname); } static void gtk_action_muxer_action_removed_from_parent (GActionGroup *action_group, const gchar *action_name, gpointer user_data) { GtkActionMuxer *muxer = user_data; gtk_action_muxer_action_removed (muxer, action_name); } static gboolean gtk_action_muxer_query_action (GActionGroup *action_group, const gchar *action_name, gboolean *enabled, const GVariantType **parameter_type, const GVariantType **state_type, GVariant **state_hint, GVariant **state) { GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group); Group *group; const gchar *unprefixed_name; group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name); if (group) return g_action_group_query_action (group->group, unprefixed_name, enabled, parameter_type, state_type, state_hint, state); if (muxer->parent) return g_action_group_query_action (G_ACTION_GROUP (muxer->parent), action_name, enabled, parameter_type, state_type, state_hint, state); return FALSE; } static void gtk_action_muxer_activate_action (GActionGroup *action_group, const gchar *action_name, GVariant *parameter) { GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group); Group *group; const gchar *unprefixed_name; group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name); if (group) g_action_group_activate_action (group->group, unprefixed_name, parameter); else if (muxer->parent) g_action_group_activate_action (G_ACTION_GROUP (muxer->parent), action_name, parameter); } static void gtk_action_muxer_change_action_state (GActionGroup *action_group, const gchar *action_name, GVariant *state) { GtkActionMuxer *muxer = GTK_ACTION_MUXER (action_group); Group *group; const gchar *unprefixed_name; group = gtk_action_muxer_find_group (muxer, action_name, &unprefixed_name); if (group) g_action_group_change_action_state (group->group, unprefixed_name, state); else if (muxer->parent) g_action_group_change_action_state (G_ACTION_GROUP (muxer->parent), action_name, state); } static void gtk_action_muxer_unregister_internal (Action *action, gpointer observer) { GtkActionMuxer *muxer = action->muxer; GSList **ptr; for (ptr = &action->watchers; *ptr; ptr = &(*ptr)->next) if ((*ptr)->data == observer) { *ptr = g_slist_remove (*ptr, observer); if (action->watchers == NULL) g_hash_table_remove (muxer->observed_actions, action->fullname); break; } } static void gtk_action_muxer_weak_notify (gpointer data, GObject *where_the_object_was) { Action *action = data; gtk_action_muxer_unregister_internal (action, where_the_object_was); } static void gtk_action_muxer_register_observer (GtkActionObservable *observable, const gchar *name, GtkActionObserver *observer) { GtkActionMuxer *muxer = GTK_ACTION_MUXER (observable); Action *action; action = g_hash_table_lookup (muxer->observed_actions, name); if (action == NULL) { action = g_slice_new (Action); action->muxer = muxer; action->fullname = g_strdup (name); action->watchers = NULL; g_hash_table_insert (muxer->observed_actions, action->fullname, action); } action->watchers = g_slist_prepend (action->watchers, observer); g_object_weak_ref (G_OBJECT (observer), gtk_action_muxer_weak_notify, action); } static void gtk_action_muxer_unregister_observer (GtkActionObservable *observable, const gchar *name, GtkActionObserver *observer) { GtkActionMuxer *muxer = GTK_ACTION_MUXER (observable); Action *action; action = g_hash_table_lookup (muxer->observed_actions, name); g_object_weak_unref (G_OBJECT (observer), gtk_action_muxer_weak_notify, action); gtk_action_muxer_unregister_internal (action, observer); } static void gtk_action_muxer_free_group (gpointer data) { Group *group = data; gint i; /* 'for loop' or 'four loop'? */ for (i = 0; i < 4; i++) g_signal_handler_disconnect (group->group, group->handler_ids[i]); g_object_unref (group->group); g_free (group->prefix); g_slice_free (Group, group); } static void gtk_action_muxer_free_action (gpointer data) { Action *action = data; GSList *it; for (it = action->watchers; it; it = it->next) g_object_weak_unref (G_OBJECT (it->data), gtk_action_muxer_weak_notify, action); g_slist_free (action->watchers); g_free (action->fullname); g_slice_free (Action, action); } static void gtk_action_muxer_finalize (GObject *object) { GtkActionMuxer *muxer = GTK_ACTION_MUXER (object); g_assert_cmpint (g_hash_table_size (muxer->observed_actions), ==, 0); g_hash_table_unref (muxer->observed_actions); g_hash_table_unref (muxer->groups); G_OBJECT_CLASS (gtk_action_muxer_parent_class) ->finalize (object); } static void gtk_action_muxer_dispose (GObject *object) { GtkActionMuxer *muxer = GTK_ACTION_MUXER (object); if (muxer->parent) { g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_added_to_parent, muxer); g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_removed_from_parent, muxer); g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_enabled_changed, muxer); g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_state_changed, muxer); g_clear_object (&muxer->parent); } g_hash_table_remove_all (muxer->observed_actions); G_OBJECT_CLASS (gtk_action_muxer_parent_class) ->dispose (object); } static void gtk_action_muxer_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { GtkActionMuxer *muxer = GTK_ACTION_MUXER (object); switch (property_id) { case PROP_PARENT: g_value_set_object (value, gtk_action_muxer_get_parent (muxer)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void gtk_action_muxer_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { GtkActionMuxer *muxer = GTK_ACTION_MUXER (object); switch (property_id) { case PROP_PARENT: gtk_action_muxer_set_parent (muxer, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); } } static void gtk_action_muxer_init (GtkActionMuxer *muxer) { muxer->observed_actions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gtk_action_muxer_free_action); muxer->groups = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, gtk_action_muxer_free_group); } static void gtk_action_muxer_observable_iface_init (GtkActionObservableInterface *iface) { iface->register_observer = gtk_action_muxer_register_observer; iface->unregister_observer = gtk_action_muxer_unregister_observer; } static void gtk_action_muxer_group_iface_init (GActionGroupInterface *iface) { iface->list_actions = gtk_action_muxer_list_actions; iface->query_action = gtk_action_muxer_query_action; iface->activate_action = gtk_action_muxer_activate_action; iface->change_action_state = gtk_action_muxer_change_action_state; } static void gtk_action_muxer_class_init (GObjectClass *class) { class->get_property = gtk_action_muxer_get_property; class->set_property = gtk_action_muxer_set_property; class->finalize = gtk_action_muxer_finalize; class->dispose = gtk_action_muxer_dispose; properties[PROP_PARENT] = g_param_spec_object ("parent", "Parent", "The parent muxer", GTK_TYPE_ACTION_MUXER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (class, NUM_PROPERTIES, properties); } /** * gtk_action_muxer_insert: * @muxer: a #GtkActionMuxer * @prefix: the prefix string for the action group * @action_group: a #GActionGroup * * Adds the actions in @action_group to the list of actions provided by * @muxer. @prefix is prefixed to each action name, such that for each * action x in @action_group, there is an equivalent * action @prefix.x in @muxer. * * For example, if @prefix is "app" and @action_group * contains an action called "quit", then @muxer will * now contain an action called "app.quit". * * If any #GtkActionObservers are registered for actions in the group, * "action_added" notifications will be emitted, as appropriate. * * @prefix must not contain a dot ('.'). */ void gtk_action_muxer_insert (GtkActionMuxer *muxer, const gchar *prefix, GActionGroup *action_group) { gchar **actions; Group *group; gint i; /* TODO: diff instead of ripout and replace */ gtk_action_muxer_remove (muxer, prefix); group = g_slice_new (Group); group->muxer = muxer; group->group = g_object_ref (action_group); group->prefix = g_strdup (prefix); g_hash_table_insert (muxer->groups, group->prefix, group); actions = g_action_group_list_actions (group->group); for (i = 0; actions[i]; i++) gtk_action_muxer_action_added_to_group (group->group, actions[i], group); g_strfreev (actions); group->handler_ids[0] = g_signal_connect (group->group, "action-added", G_CALLBACK (gtk_action_muxer_action_added_to_group), group); group->handler_ids[1] = g_signal_connect (group->group, "action-removed", G_CALLBACK (gtk_action_muxer_action_removed_from_group), group); group->handler_ids[2] = g_signal_connect (group->group, "action-enabled-changed", G_CALLBACK (gtk_action_muxer_group_action_enabled_changed), group); group->handler_ids[3] = g_signal_connect (group->group, "action-state-changed", G_CALLBACK (gtk_action_muxer_group_action_state_changed), group); } /** * gtk_action_muxer_remove: * @muxer: a #GtkActionMuxer * @prefix: the prefix of the action group to remove * * Removes a #GActionGroup from the #GtkActionMuxer. * * If any #GtkActionObservers are registered for actions in the group, * "action_removed" notifications will be emitted, as appropriate. */ void gtk_action_muxer_remove (GtkActionMuxer *muxer, const gchar *prefix) { Group *group; group = g_hash_table_lookup (muxer->groups, prefix); if (group != NULL) { gchar **actions; gint i; g_hash_table_steal (muxer->groups, prefix); actions = g_action_group_list_actions (group->group); for (i = 0; actions[i]; i++) gtk_action_muxer_action_removed_from_group (group->group, actions[i], group); g_strfreev (actions); gtk_action_muxer_free_group (group); } } /** * gtk_action_muxer_new: * * Creates a new #GtkActionMuxer. */ GtkActionMuxer * gtk_action_muxer_new (void) { return g_object_new (GTK_TYPE_ACTION_MUXER, NULL); } /** * gtk_action_muxer_get_parent: * @muxer: a #GtkActionMuxer * * Returns: (transfer none): the parent of @muxer, or NULL. */ GtkActionMuxer * gtk_action_muxer_get_parent (GtkActionMuxer *muxer) { g_return_val_if_fail (GTK_IS_ACTION_MUXER (muxer), NULL); return muxer->parent; } /** * gtk_action_muxer_set_parent: * @muxer: a #GtkActionMuxer * @parent: (allow-none): the new parent #GtkActionMuxer * * Sets the parent of @muxer to @parent. */ void gtk_action_muxer_set_parent (GtkActionMuxer *muxer, GtkActionMuxer *parent) { g_return_if_fail (GTK_IS_ACTION_MUXER (muxer)); g_return_if_fail (parent == NULL || GTK_IS_ACTION_MUXER (parent)); if (muxer->parent == parent) return; if (muxer->parent != NULL) { gchar **actions; gchar **it; actions = g_action_group_list_actions (G_ACTION_GROUP (muxer->parent)); for (it = actions; *it; it++) gtk_action_muxer_action_removed (muxer, *it); g_strfreev (actions); g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_added_to_parent, muxer); g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_action_removed_from_parent, muxer); g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_enabled_changed, muxer); g_signal_handlers_disconnect_by_func (muxer->parent, gtk_action_muxer_parent_action_state_changed, muxer); g_object_unref (muxer->parent); } muxer->parent = parent; if (muxer->parent != NULL) { gchar **actions; gchar **it; g_object_ref (muxer->parent); actions = g_action_group_list_actions (G_ACTION_GROUP (muxer->parent)); for (it = actions; *it; it++) gtk_action_muxer_action_added (muxer, *it, G_ACTION_GROUP (muxer->parent), *it); g_strfreev (actions); g_signal_connect (muxer->parent, "action-added", G_CALLBACK (gtk_action_muxer_action_added_to_parent), muxer); g_signal_connect (muxer->parent, "action-removed", G_CALLBACK (gtk_action_muxer_action_removed_from_parent), muxer); g_signal_connect (muxer->parent, "action-enabled-changed", G_CALLBACK (gtk_action_muxer_parent_action_enabled_changed), muxer); g_signal_connect (muxer->parent, "action-state-changed", G_CALLBACK (gtk_action_muxer_parent_action_state_changed), muxer); } g_object_notify_by_pspec (G_OBJECT (muxer), properties[PROP_PARENT]); } qmenumodel-0.9.1/libqmenumodel/src/gtk/gtkactionmuxer.h0000644000000000000000000000462014200511032020171 0ustar /* * Copyright © 2011 Canonical Limited * * This library 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; either * version 2 of the licence, or (at your option) any later version. * * This library 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 library. If not, see . * * Author: Ryan Lortie */ #ifndef __GTK_ACTION_MUXER_H__ #define __GTK_ACTION_MUXER_H__ #include G_BEGIN_DECLS #define GTK_TYPE_ACTION_MUXER (gtk_action_muxer_get_type ()) #define GTK_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ GTK_TYPE_ACTION_MUXER, GtkActionMuxer)) #define GTK_IS_ACTION_MUXER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ GTK_TYPE_ACTION_MUXER)) typedef struct _GtkActionMuxer GtkActionMuxer; GType gtk_action_muxer_get_type (void); GtkActionMuxer * gtk_action_muxer_new (void); void gtk_action_muxer_insert (GtkActionMuxer *muxer, const gchar *prefix, GActionGroup *action_group); void gtk_action_muxer_remove (GtkActionMuxer *muxer, const gchar *prefix); GtkActionMuxer * gtk_action_muxer_get_parent (GtkActionMuxer *muxer); void gtk_action_muxer_set_parent (GtkActionMuxer *muxer, GtkActionMuxer *parent); G_END_DECLS #endif /* __GTK_ACTION_MUXER_H__ */ qmenumodel-0.9.1/libqmenumodel/src/gtk/gtkactionobservable.c0000644000000000000000000000513714200511032021154 0ustar /* * Copyright © 2011 Canonical Limited * * This library 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; either version 2 of the * licence or (at your option) any later version. * * This library 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 library. If not, see . * * Authors: Ryan Lortie */ #include "config.h" #include "gtkactionobservable.h" G_DEFINE_INTERFACE (GtkActionObservable, gtk_action_observable, G_TYPE_OBJECT) /* * SECTION:gtkactionobserable * @short_description: an interface implemented by objects that report * changes to actions */ void gtk_action_observable_default_init (GtkActionObservableInterface *iface) { } /** * gtk_action_observable_register_observer: * @observable: a #GtkActionObservable * @action_name: the name of the action * @observer: the #GtkActionObserver to which the events will be reported * * Registers @observer as being interested in changes to @action_name on * @observable. */ void gtk_action_observable_register_observer (GtkActionObservable *observable, const gchar *action_name, GtkActionObserver *observer) { g_return_if_fail (GTK_IS_ACTION_OBSERVABLE (observable)); GTK_ACTION_OBSERVABLE_GET_IFACE (observable) ->register_observer (observable, action_name, observer); } /** * gtk_action_observable_unregister_observer: * @observable: a #GtkActionObservable * @action_name: the name of the action * @observer: the #GtkActionObserver to which the events will be reported * * Removes the registration of @observer as being interested in changes * to @action_name on @observable. * * If the observer was registered multiple times, it must be * unregistered an equal number of times. */ void gtk_action_observable_unregister_observer (GtkActionObservable *observable, const gchar *action_name, GtkActionObserver *observer) { g_return_if_fail (GTK_IS_ACTION_OBSERVABLE (observable)); GTK_ACTION_OBSERVABLE_GET_IFACE (observable) ->unregister_observer (observable, action_name, observer); } qmenumodel-0.9.1/libqmenumodel/src/gtk/gtkactionobservable.h0000644000000000000000000000577314200511032021167 0ustar /* * Copyright © 2011 Canonical Limited * * This library 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; either version 2 of the * licence or (at your option) any later version. * * This library 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 library. If not, see . * * Authors: Ryan Lortie */ #ifndef __GTK_ACTION_OBSERVABLE_H__ #define __GTK_ACTION_OBSERVABLE_H__ #include "gtkactionobserver.h" G_BEGIN_DECLS #define GTK_TYPE_ACTION_OBSERVABLE (gtk_action_observable_get_type ()) #define GTK_ACTION_OBSERVABLE(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ GTK_TYPE_ACTION_OBSERVABLE, GtkActionObservable)) #define GTK_IS_ACTION_OBSERVABLE(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ GTK_TYPE_ACTION_OBSERVABLE)) #define GTK_ACTION_OBSERVABLE_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \ GTK_TYPE_ACTION_OBSERVABLE, \ GtkActionObservableInterface)) typedef struct _GtkActionObservableInterface GtkActionObservableInterface; struct _GtkActionObservableInterface { GTypeInterface g_iface; void (* register_observer) (GtkActionObservable *observable, const gchar *action_name, GtkActionObserver *observer); void (* unregister_observer) (GtkActionObservable *observable, const gchar *action_name, GtkActionObserver *observer); }; GType gtk_action_observable_get_type (void); void gtk_action_observable_register_observer (GtkActionObservable *observable, const gchar *action_name, GtkActionObserver *observer); void gtk_action_observable_unregister_observer (GtkActionObservable *observable, const gchar *action_name, GtkActionObserver *observer); G_END_DECLS #endif /* __GTK_ACTION_OBSERVABLE_H__ */ qmenumodel-0.9.1/libqmenumodel/src/gtk/gtkactionobserver.c0000644000000000000000000001360214200511032020653 0ustar /* * Copyright © 2011 Canonical Limited * * This library 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; either version 2 of the * licence or (at your option) any later version. * * This library 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 library. If not, see . * * Authors: Ryan Lortie */ #include "config.h" #include "gtkactionobserver.h" G_DEFINE_INTERFACE (GtkActionObserver, gtk_action_observer, G_TYPE_OBJECT) /** * SECTION:gtkactionobserver * @short_description: an interface implemented by objects that are * interested in monitoring actions for changes * * GtkActionObserver is a simple interface allowing objects that wish to * be notified of changes to actions to be notified of those changes. * * It is also possible to monitor changes to action groups using * #GObject signals, but there are a number of reasons that this * approach could become problematic: * * - there are four separate signals that must be manually connected * and disconnected * * - when a large number of different observers wish to monitor a * (usually disjoint) set of actions within the same action group, * there is only one way to avoid having all notifications delivered * to all observers: signal detail. In order to use signal detail, * each action name must be quarked, which is not always practical. * * - even if quarking is acceptable, #GObject signal details are * implemented by scanning a linked list, so there is no real * decrease in complexity */ void gtk_action_observer_default_init (GtkActionObserverInterface *class) { } /** * gtk_action_observer_action_added: * @observer: a #GtkActionObserver * @observable: the source of the event * @action_name: the name of the action * @enabled: %TRUE if the action is now enabled * @parameter_type: the parameter type for action invocations, or %NULL * if no parameter is required * @state: the current state of the action, or %NULL if the action is * stateless * * This function is called when an action that the observer is * registered to receive events for is added. * * This function should only be called by objects with which the * observer has explicitly registered itself to receive events. */ void gtk_action_observer_action_added (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, const GVariantType *parameter_type, gboolean enabled, GVariant *state) { g_return_if_fail (GTK_IS_ACTION_OBSERVER (observer)); GTK_ACTION_OBSERVER_GET_IFACE (observer) ->action_added (observer, observable, action_name, parameter_type, enabled, state); } /** * gtk_action_observer_action_enabled_changed: * @observer: a #GtkActionObserver * @observable: the source of the event * @action_name: the name of the action * @enabled: %TRUE if the action is now enabled * * This function is called when an action that the observer is * registered to receive events for becomes enabled or disabled. * * This function should only be called by objects with which the * observer has explicitly registered itself to receive events. */ void gtk_action_observer_action_enabled_changed (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, gboolean enabled) { g_return_if_fail (GTK_IS_ACTION_OBSERVER (observer)); GTK_ACTION_OBSERVER_GET_IFACE (observer) ->action_enabled_changed (observer, observable, action_name, enabled); } /** * gtk_action_observer_action_state_changed: * @observer: a #GtkActionObserver * @observable: the source of the event * @action_name: the name of the action * @state: the new state of the action * * This function is called when an action that the observer is * registered to receive events for changes to its state. * * This function should only be called by objects with which the * observer has explicitly registered itself to receive events. */ void gtk_action_observer_action_state_changed (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, GVariant *state) { g_return_if_fail (GTK_IS_ACTION_OBSERVER (observer)); GTK_ACTION_OBSERVER_GET_IFACE (observer) ->action_state_changed (observer, observable, action_name, state); } /** * gtk_action_observer_action_removed: * @observer: a #GtkActionObserver * @observable: the source of the event * @action_name: the name of the action * * This function is called when an action that the observer is * registered to receive events for is removed. * * This function should only be called by objects with which the * observer has explicitly registered itself to receive events. */ void gtk_action_observer_action_removed (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name) { g_return_if_fail (GTK_IS_ACTION_OBSERVER (observer)); GTK_ACTION_OBSERVER_GET_IFACE (observer) ->action_removed (observer, observable, action_name); } qmenumodel-0.9.1/libqmenumodel/src/gtk/gtkactionobserver.h0000644000000000000000000001164514200511032020665 0ustar /* * Copyright © 2011 Canonical Limited * * This library 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; either version 2 of the * licence or (at your option) any later version. * * This library 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 library. If not, see . * * Authors: Ryan Lortie */ #ifndef __GTK_ACTION_OBSERVER_H__ #define __GTK_ACTION_OBSERVER_H__ #include G_BEGIN_DECLS #define GTK_TYPE_ACTION_OBSERVER (gtk_action_observer_get_type ()) #define GTK_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ GTK_TYPE_ACTION_OBSERVER, GtkActionObserver)) #define GTK_IS_ACTION_OBSERVER(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ GTK_TYPE_ACTION_OBSERVER)) #define GTK_ACTION_OBSERVER_GET_IFACE(inst) (G_TYPE_INSTANCE_GET_INTERFACE ((inst), \ GTK_TYPE_ACTION_OBSERVER, GtkActionObserverInterface)) typedef struct _GtkActionObserverInterface GtkActionObserverInterface; typedef struct _GtkActionObservable GtkActionObservable; typedef struct _GtkActionObserver GtkActionObserver; struct _GtkActionObserverInterface { GTypeInterface g_iface; void (* action_added) (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, const GVariantType *parameter_type, gboolean enabled, GVariant *state); void (* action_enabled_changed) (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, gboolean enabled); void (* action_state_changed) (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, GVariant *state); void (* action_removed) (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name); }; GType gtk_action_observer_get_type (void); void gtk_action_observer_action_added (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, const GVariantType *parameter_type, gboolean enabled, GVariant *state); void gtk_action_observer_action_enabled_changed (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, gboolean enabled); void gtk_action_observer_action_state_changed (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, GVariant *state); void gtk_action_observer_action_removed (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name); G_END_DECLS #endif /* __GTK_ACTION_OBSERVER_H__ */ qmenumodel-0.9.1/libqmenumodel/src/gtk/gtkmenutracker.c0000644000000000000000000004555014200511032020155 0ustar /* * Copyright © 2013 Canonical Limited * * This library 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; either * version 2 of the licence, or (at your option) any later version. * * This library 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 library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Author: Ryan Lortie */ #include "config.h" #include "gtkmenutracker.h" /** * SECTION:gtkmenutracker * @Title: GtkMenuTracker * @Short_description: A helper class for interpreting #GMenuModel * * #GtkMenuTracker is a simple object to ease implementations of #GMenuModel. * Given a #GtkActionObservable (usually a #GActionMuxer) along with a * #GMenuModel, it will tell you which menu items to create and where to place * them. If a menu item is removed, it will tell you the position of the menu * item to remove. * * Using #GtkMenuTracker is fairly simple. The only guarantee you must make * to #GtkMenuTracker is that you must obey all insert signals and track the * position of items that #GtkMenuTracker gives you. That is, #GtkMenuTracker * expects positions of all the latter items to change when it calls your * insertion callback with an early position, as it may ask you to remove * an item with a readjusted position later. * * #GtkMenuTracker will give you a #GtkMenuTrackerItem in your callback. You * must hold onto this object until a remove signal is emitted. This item * represents a single menu item, which can be one of three classes: normal item, * separator, or submenu. * * Certain properties on the #GtkMenuTrackerItem are mutable, and you must * listen for changes in the item. For more details, see the documentation * for #GtkMenuTrackerItem along with https://live.gnome.org/GApplication/GMenuModel. * * The idea of @with_separators is for special cases where menu models may * be tracked in places where separators are not available, like in toplevel * "File", "Edit" menu bars. Ignoring separator items is wrong, as #GtkMenuTracker * expects the position to change, so we must tell #GtkMenuTracker to ignore * separators itself. */ typedef struct _GtkMenuTrackerSection GtkMenuTrackerSection; struct _GtkMenuTracker { GtkActionObservable *observable; GtkMenuTrackerInsertFunc insert_func; GtkMenuTrackerRemoveFunc remove_func; gpointer user_data; GtkMenuTrackerSection *toplevel; }; struct _GtkMenuTrackerSection { GMenuModel *model; GSList *items; gchar *action_namespace; guint with_separators : 1; guint has_separator : 1; gulong handler; }; static GtkMenuTrackerSection * gtk_menu_tracker_section_new (GtkMenuTracker *tracker, GMenuModel *model, gboolean with_separators, gint offset, const gchar *action_namespace, GPtrArray *items_already_created); static void gtk_menu_tracker_section_free (GtkMenuTrackerSection *section); static GtkMenuTrackerSection * gtk_menu_tracker_section_find_model (GtkMenuTrackerSection *section, GMenuModel *model, gint *offset) { GSList *item; if (section->has_separator) (*offset)++; if (section->model == model) return section; for (item = section->items; item; item = item->next) { GtkMenuTrackerSection *subsection = item->data; if (subsection) { GtkMenuTrackerSection *found_section; found_section = gtk_menu_tracker_section_find_model (subsection, model, offset); if (found_section) return found_section; } else (*offset)++; } return FALSE; } /* this is responsible for syncing the showing of a separator for a * single subsection (and its children). * * we only ever show separators if we have _actual_ children (ie: we do * not show a separator if the section contains only empty child * sections). it's difficult to determine this on-the-fly, so we have * this separate function to come back later and figure it out. * * 'section' is that section. * * 'tracker' is passed in so that we can emit callbacks when we decide * to add/remove separators. * * 'offset' is passed in so we know which position to emit in our * callbacks. ie: if we add a separator right at the top of this * section then we would emit it with this offset. deeper inside, we * adjust accordingly. * * could_have_separator is true in two situations: * * - our parent section had with_separators defined and we are not the * first section (ie: we should add a separator if we have content in * order to divide us from the items above) * * - if we had a 'label' attribute set for this section * * parent_model and parent_index are passed in so that we can give them * to the insertion callback so that it can see the label (and anything * else that happens to be defined on the section). * * we iterate each item in ourselves. for subsections, we recursively * run ourselves to sync separators. after we are done, we notice if we * have any items in us or if we are completely empty and sync if our * separator is shown or not. */ static gint gtk_menu_tracker_section_sync_separators (GtkMenuTrackerSection *section, GtkMenuTracker *tracker, gint offset, gboolean could_have_separator, GMenuModel *parent_model, gint parent_index) { gboolean should_have_separator; gint n_items = 0; GSList *item; gint i = 0; for (item = section->items; item; item = item->next) { GtkMenuTrackerSection *subsection = item->data; if (subsection) { gboolean could_have_separator; could_have_separator = (section->with_separators && i > 0) || g_menu_model_get_item_attribute (section->model, i, "label", "s", NULL); n_items += gtk_menu_tracker_section_sync_separators (subsection, tracker, offset + n_items, could_have_separator, section->model, i); } else n_items++; i++; } should_have_separator = could_have_separator && n_items != 0; if (should_have_separator > section->has_separator) { /* Add a separator */ GtkMenuTrackerItem *item; GPtrArray *items = g_ptr_array_new (); item = _gtk_menu_tracker_item_new (tracker->observable, parent_model, parent_index, NULL, TRUE); g_ptr_array_add (items, (gpointer) item); (* tracker->insert_func) (items, offset, tracker->user_data); g_ptr_array_unref (items); g_object_unref (item); section->has_separator = TRUE; } else if (should_have_separator < section->has_separator) { /* Remove a separator */ (* tracker->remove_func) (offset, 1, tracker->user_data); section->has_separator = FALSE; } n_items += section->has_separator; return n_items; } static gint gtk_menu_tracker_section_measure (GtkMenuTrackerSection *section) { GSList *item; gint n_items; if (section == NULL) return 1; n_items = 0; if (section->has_separator) n_items++; for (item = section->items; item; item = item->next) n_items += gtk_menu_tracker_section_measure (item->data); return n_items; } static void gtk_menu_tracker_remove_items (GtkMenuTracker *tracker, GSList **change_point, gint offset, gint n_items) { gint i; gint n_total_items = 0; for (i = 0; i < n_items; i++) { GtkMenuTrackerSection *subsection; gint n; subsection = (*change_point)->data; *change_point = g_slist_delete_link (*change_point, *change_point); n = gtk_menu_tracker_section_measure (subsection); gtk_menu_tracker_section_free (subsection); n_total_items += n; } if (n_total_items) { (* tracker->remove_func) (offset, n_total_items, tracker->user_data); } } static void gtk_menu_tracker_add_items (GtkMenuTracker *tracker, GtkMenuTrackerSection *section, GSList **change_point, gint offset, GMenuModel *model, gint position, gint n_items, GPtrArray *items_already_created ) { GPtrArray *items; if (items_already_created) { items = items_already_created; } else { items = g_ptr_array_new (); } while (n_items--) { GMenuModel *submenu; submenu = g_menu_model_get_item_link (model, position + n_items, G_MENU_LINK_SECTION); g_assert (submenu != model); if (submenu != NULL) { GtkMenuTrackerSection *subsection; gchar *action_namespace = NULL; g_menu_model_get_item_attribute (model, position + n_items, G_MENU_ATTRIBUTE_ACTION_NAMESPACE, "s", &action_namespace); if (section->action_namespace) { gchar *namespace; namespace = g_strjoin (".", section->action_namespace, action_namespace, NULL); subsection = gtk_menu_tracker_section_new (tracker, submenu, FALSE, offset, namespace, items_already_created); g_free (namespace); } else subsection = gtk_menu_tracker_section_new (tracker, submenu, FALSE, offset, section->action_namespace, items_already_created); *change_point = g_slist_prepend (*change_point, subsection); g_free (action_namespace); g_object_unref (submenu); } else { GtkMenuTrackerItem *item; item = _gtk_menu_tracker_item_new (tracker->observable, model, position + n_items, section->action_namespace, FALSE); g_ptr_array_insert (items, 0, (gpointer) item); *change_point = g_slist_prepend (*change_point, NULL); } } if (!items_already_created) { if (items->len) { (* tracker->insert_func) (items, offset, tracker->user_data); for (gint i = 0; i < items->len; ++i) g_object_unref(g_ptr_array_index(items, i)); } g_ptr_array_unref (items); } } static void gtk_menu_tracker_model_changed (GMenuModel *model, gint position, gint removed, gint added, gpointer user_data) { GtkMenuTracker *tracker = user_data; GtkMenuTrackerSection *section; GSList **change_point; gint offset = 0; gint i; /* First find which section the changed model corresponds to, and the * position of that section within the overall menu. */ section = gtk_menu_tracker_section_find_model (tracker->toplevel, model, &offset); /* Next, seek through that section to the change point. This gives us * the correct GSList** to make the change to and also finds the final * offset at which we will make the changes (by measuring the number * of items within each item of the section before the change point). */ change_point = §ion->items; for (i = 0; i < position; i++) { offset += gtk_menu_tracker_section_measure ((*change_point)->data); change_point = &(*change_point)->next; } /* We remove items in order and add items in reverse order. This * means that the offset used for all inserts and removes caused by a * single change will be the same. * * This also has a performance advantage: GtkMenuShell stores the * menu items in a linked list. In the case where we are creating a * menu for the first time, adding the items in reverse order means * that we only ever insert at index zero, prepending the list. This * means that we can populate in O(n) time instead of O(n^2) that we * would do by appending. */ gtk_menu_tracker_remove_items (tracker, change_point, offset, removed); gtk_menu_tracker_add_items (tracker, section, change_point, offset, model, position, added, NULL); /* The offsets for insertion/removal of separators will be all over * the place, however... */ gtk_menu_tracker_section_sync_separators (tracker->toplevel, tracker, 0, FALSE, NULL, 0); } static void gtk_menu_tracker_section_free (GtkMenuTrackerSection *section) { if (section == NULL) return; g_signal_handler_disconnect (section->model, section->handler); g_slist_free_full (section->items, (GDestroyNotify) gtk_menu_tracker_section_free); g_free (section->action_namespace); g_object_unref (section->model); g_slice_free (GtkMenuTrackerSection, section); } static GtkMenuTrackerSection * gtk_menu_tracker_section_new (GtkMenuTracker *tracker, GMenuModel *model, gboolean with_separators, gint offset, const gchar *action_namespace, GPtrArray *items_already_created) { GtkMenuTrackerSection *section; section = g_slice_new0 (GtkMenuTrackerSection); section->model = g_object_ref (model); section->with_separators = with_separators; section->action_namespace = g_strdup (action_namespace); gtk_menu_tracker_add_items (tracker, section, §ion->items, offset, model, 0, g_menu_model_get_n_items (model), items_already_created); section->handler = g_signal_connect (model, "items-changed", G_CALLBACK (gtk_menu_tracker_model_changed), tracker); return section; } /*< private > * gtk_menu_tracker_new: * @model: the model to flatten * @with_separators: if the toplevel should have separators (ie: TRUE * for menus, FALSE for menubars) * @action_namespace: the passed-in action namespace * @insert_func: insert callback * @remove_func: remove callback * @user_data user data for callbacks * * Creates a GtkMenuTracker for @model, holding a ref on @model for as * long as the tracker is alive. * * This flattens out the model, merging sections and inserting * separators where appropriate. It monitors for changes and performs * updates on the fly. It also handles action_namespace for subsections * (but you will need to handle it yourself for submenus). * * When the tracker is first created, @insert_func will be called many * times to populate the menu with the initial contents of @model * (unless it is empty), before gtk_menu_tracker_new() returns. For * this reason, the menu that is using the tracker ought to be empty * when it creates the tracker. * * Future changes to @model will result in more calls to @insert_func * and @remove_func. * * The position argument to both functions is the linear 0-based * position in the menu at which the item in question should be inserted * or removed. * * For @insert_func, @model and @item_index are used to get the * information about the menu item to insert. @action_namespace is the * action namespace that actions referred to from that item should place * themselves in. Note that if the item is a submenu and the * "action-namespace" attribute is defined on the item, it will _not_ be * applied to the @action_namespace argument as it is meant for the * items inside of the submenu, not the submenu item itself. * * @is_separator is set to %TRUE in case the item being added is a * separator. @model and @item_index will still be meaningfully set in * this case -- to the section menu item corresponding to the separator. * This is useful if the section specifies a label, for example. If * there is an "action-namespace" attribute on this menu item then it * should be ignored by the consumer because #GtkMenuTracker has already * handled it. * * When using #GtkMenuTracker there is no need to hold onto @model or * monitor it for changes. The model will be unreffed when * gtk_menu_tracker_free() is called. */ GtkMenuTracker * gtk_menu_tracker_new (GtkActionObservable *observable, GMenuModel *model, gboolean with_separators, const gchar *action_namespace, GtkMenuTrackerInsertFunc insert_func, GtkMenuTrackerRemoveFunc remove_func, gpointer user_data) { GtkMenuTracker *tracker; tracker = g_slice_new (GtkMenuTracker); tracker->observable = g_object_ref (observable); tracker->insert_func = insert_func; tracker->remove_func = remove_func; tracker->user_data = user_data; tracker->toplevel = gtk_menu_tracker_section_new (tracker, model, with_separators, 0, action_namespace, NULL); gtk_menu_tracker_section_sync_separators (tracker->toplevel, tracker, 0, FALSE, NULL, 0); return tracker; } GtkMenuTracker * gtk_menu_tracker_new_for_item_submenu (GtkMenuTrackerItem *item, GtkMenuTrackerInsertFunc insert_func, GtkMenuTrackerRemoveFunc remove_func, gpointer user_data) { return gtk_menu_tracker_new (_gtk_menu_tracker_item_get_observable (item), _gtk_menu_tracker_item_get_submenu (item), TRUE, _gtk_menu_tracker_item_get_submenu_namespace (item), insert_func, remove_func, user_data); } /*< private > * gtk_menu_tracker_free: * @tracker: a #GtkMenuTracker * * Frees the tracker, ... */ void gtk_menu_tracker_free (GtkMenuTracker *tracker) { gtk_menu_tracker_section_free (tracker->toplevel); g_object_unref (tracker->observable); g_slice_free (GtkMenuTracker, tracker); } qmenumodel-0.9.1/libqmenumodel/src/gtk/gtkmenutracker.h0000644000000000000000000000567314200511032020164 0ustar /* * Copyright © 2013 Canonical Limited * * This library 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; either * version 2 of the licence, or (at your option) any later version. * * This library 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 library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Author: Ryan Lortie */ #ifndef __GTK_MENU_TRACKER_H__ #define __GTK_MENU_TRACKER_H__ #include "gtkmenutrackeritem.h" typedef struct _GtkMenuTracker GtkMenuTracker; typedef void (* GtkMenuTrackerInsertFunc) (GPtrArray *items, gint position, gpointer user_data); typedef void (* GtkMenuTrackerRemoveFunc) (gint position, gint n_items, gpointer user_data); GtkMenuTracker * gtk_menu_tracker_new (GtkActionObservable *observer, GMenuModel *model, gboolean with_separators, const gchar *action_namespace, GtkMenuTrackerInsertFunc insert_func, GtkMenuTrackerRemoveFunc remove_func, gpointer user_data); GtkMenuTracker * gtk_menu_tracker_new_for_item_submenu (GtkMenuTrackerItem *item, GtkMenuTrackerInsertFunc insert_func, GtkMenuTrackerRemoveFunc remove_func, gpointer user_data); void gtk_menu_tracker_free (GtkMenuTracker *tracker); #endif /* __GTK_MENU_TRACKER_H__ */ qmenumodel-0.9.1/libqmenumodel/src/gtk/gtkmenutrackeritem.c0000644000000000000000000007261214200511032021033 0ustar /* * Copyright © 2013 Canonical Limited * * This library 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; either * version 2 of the licence, or (at your option) any later version. * * This library 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 library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Author: Ryan Lortie */ #include "config.h" #include "gtkmenutrackeritem.h" /** * SECTION:gtkmenutrackeritem * @Title: GtkMenuTrackerItem * @Short_description: Small helper for model menu items * * A #GtkMenuTrackerItem is a small helper class used by #GtkMenuTracker to * represent menu items. It has one of three classes: normal item, separator, * or submenu. * * If an item is one of the non-normal classes (submenu, separator), only the * label of the item needs to be respected. Otherwise, all the properties * of the item contribute to the item's appearance and state. * * Implementing the appearance of the menu item is up to toolkits, and certain * toolkits may choose to ignore certain properties, like icon or accel. The * role of the item determines its accessibility role, along with its * decoration if the GtkMenuTrackerItem::toggled property is true. As an * example, if the item has the role %GTK_MENU_TRACKER_ITEM_ROLE_CHECK and * GtkMenuTrackerItem::toggled is %FALSE, its accessible role should be that of * a check menu item, and no decoration should be drawn. But if * GtkMenuTrackerItem::toggled is %TRUE, a checkmark should be drawn. * * All properties except for the two class-determining properties, * GtkMenuTrackerItem::is-separator and GtkMenuTrackerItem::has-submenu are * allowed to change, so listen to the notify signals to update your item's * appearance. When using a GObject library, this can conveniently be done * with g_object_bind_property() and #GBinding, and this is how this is * implemented in GTK+; the appearance side is implemented in #GtkModelMenuItem. * * When an item is clicked, simply call gtk_menu_tracker_item_activated() in * response. The #GtkMenuTrackerItem will take care of everything related to * activating the item and will itself update the state of all items in * response. * * Submenus are a special case of menu item. When an item is a submenu, you * should create a submenu for it with gtk_menu_tracker_new_item_for_submenu(), * and apply the same menu tracking logic you would for a toplevel menu. * Applications using submenus may want to lazily build their submenus in * response to the user clicking on it, as building a submenu may be expensive. * * Thus, the submenu has two special controls -- the submenu's visibility * should be controlled by the GtkMenuTrackerItem::submenu-shown property, * and if a user clicks on the submenu, do not immediately show the menu, * but call gtk_menu_tracker_item_request_submenu_shown() and wait for the * GtkMenuTrackerItem::submenu-shown property to update. If the user navigates, * the application may want to be notified so it can cancel the expensive * operation that it was using to build the submenu. Thus, * gtk_menu_tracker_item_request_submenu_shown() takes a boolean parameter. * Use %TRUE when the user wants to open the submenu, and %FALSE when the * user wants to close the submenu. */ typedef GObjectClass GtkMenuTrackerItemClass; struct _GtkMenuTrackerItem { GObject parent_instance; GtkActionObservable *observable; gchar *action_namespace; GMenuItem *item; GtkMenuTrackerItemRole role : 4; guint is_separator : 1; guint can_activate : 1; guint sensitive : 1; guint toggled : 1; guint submenu_shown : 1; guint submenu_requested : 1; GVariant *action_state; }; enum { PROP_0, PROP_IS_SEPARATOR, PROP_HAS_SUBMENU, PROP_LABEL, PROP_ICON, PROP_SENSITIVE, PROP_VISIBLE, PROP_ROLE, PROP_TOGGLED, PROP_ACCEL, PROP_SUBMENU_SHOWN, PROP_ACTION_NAME, PROP_ACTION_STATE, N_PROPS }; static GParamSpec *gtk_menu_tracker_item_pspecs[N_PROPS]; static void gtk_menu_tracker_item_init_observer_iface (GtkActionObserverInterface *iface); G_DEFINE_TYPE_WITH_CODE (GtkMenuTrackerItem, gtk_menu_tracker_item, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GTK_TYPE_ACTION_OBSERVER, gtk_menu_tracker_item_init_observer_iface)) GType gtk_menu_tracker_item_role_get_type (void) { static gsize gtk_menu_tracker_item_role_type; if (g_once_init_enter (>k_menu_tracker_item_role_type)) { static const GEnumValue values[] = { { GTK_MENU_TRACKER_ITEM_ROLE_NORMAL, "GTK_MENU_TRACKER_ITEM_ROLE_NORMAL", "normal" }, { GTK_MENU_TRACKER_ITEM_ROLE_CHECK, "GTK_MENU_TRACKER_ITEM_ROLE_CHECK", "check" }, { GTK_MENU_TRACKER_ITEM_ROLE_RADIO, "GTK_MENU_TRACKER_ITEM_ROLE_RADIO", "radio" }, { 0, NULL, NULL } }; GType type; type = g_enum_register_static ("GtkMenuTrackerItemRole", values); g_once_init_leave (>k_menu_tracker_item_role_type, type); } return gtk_menu_tracker_item_role_type; } static void gtk_menu_tracker_item_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { GtkMenuTrackerItem *self = GTK_MENU_TRACKER_ITEM (object); switch (prop_id) { case PROP_IS_SEPARATOR: g_value_set_boolean (value, gtk_menu_tracker_item_get_is_separator (self)); break; case PROP_HAS_SUBMENU: g_value_set_boolean (value, gtk_menu_tracker_item_get_has_submenu (self)); break; case PROP_LABEL: g_value_set_string (value, gtk_menu_tracker_item_get_label (self)); break; case PROP_ICON: g_value_set_object (value, gtk_menu_tracker_item_get_icon (self)); break; case PROP_SENSITIVE: g_value_set_boolean (value, gtk_menu_tracker_item_get_sensitive (self)); break; case PROP_VISIBLE: g_value_set_boolean (value, gtk_menu_tracker_item_get_visible (self)); break; case PROP_ROLE: g_value_set_enum (value, gtk_menu_tracker_item_get_role (self)); break; case PROP_TOGGLED: g_value_set_boolean (value, gtk_menu_tracker_item_get_toggled (self)); break; case PROP_ACCEL: g_value_set_string (value, gtk_menu_tracker_item_get_accel (self)); break; case PROP_SUBMENU_SHOWN: g_value_set_boolean (value, gtk_menu_tracker_item_get_submenu_shown (self)); break; case PROP_ACTION_NAME: g_value_take_string (value, gtk_menu_tracker_item_get_action_name (self)); case PROP_ACTION_STATE: g_value_set_variant (value, self->action_state); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void gtk_menu_tracker_item_finalize (GObject *object) { GtkMenuTrackerItem *self = GTK_MENU_TRACKER_ITEM (object); g_free (self->action_namespace); if (self->observable) g_object_unref (self->observable); if (self->action_state) g_variant_unref (self->action_state); g_object_unref (self->item); G_OBJECT_CLASS (gtk_menu_tracker_item_parent_class)->finalize (object); } static void gtk_menu_tracker_item_init (GtkMenuTrackerItem * self) { } static void gtk_menu_tracker_item_class_init (GtkMenuTrackerItemClass *class) { class->get_property = gtk_menu_tracker_item_get_property; class->finalize = gtk_menu_tracker_item_finalize; gtk_menu_tracker_item_pspecs[PROP_IS_SEPARATOR] = g_param_spec_boolean ("is-separator", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); gtk_menu_tracker_item_pspecs[PROP_HAS_SUBMENU] = g_param_spec_boolean ("has-submenu", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); gtk_menu_tracker_item_pspecs[PROP_LABEL] = g_param_spec_string ("label", "", "", NULL, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); gtk_menu_tracker_item_pspecs[PROP_ICON] = g_param_spec_object ("icon", "", "", G_TYPE_ICON, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); gtk_menu_tracker_item_pspecs[PROP_SENSITIVE] = g_param_spec_boolean ("sensitive", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); gtk_menu_tracker_item_pspecs[PROP_VISIBLE] = g_param_spec_boolean ("visible", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); gtk_menu_tracker_item_pspecs[PROP_ROLE] = g_param_spec_enum ("role", "", "", GTK_TYPE_MENU_TRACKER_ITEM_ROLE, GTK_MENU_TRACKER_ITEM_ROLE_NORMAL, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); gtk_menu_tracker_item_pspecs[PROP_TOGGLED] = g_param_spec_boolean ("toggled", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); gtk_menu_tracker_item_pspecs[PROP_ACCEL] = g_param_spec_string ("accel", "", "", NULL, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); gtk_menu_tracker_item_pspecs[PROP_SUBMENU_SHOWN] = g_param_spec_boolean ("submenu-shown", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); gtk_menu_tracker_item_pspecs[PROP_ACTION_NAME] = g_param_spec_boolean ("action-name", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); gtk_menu_tracker_item_pspecs[PROP_ACTION_STATE] = g_param_spec_boolean ("action-state", "", "", FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); g_object_class_install_properties (class, N_PROPS, gtk_menu_tracker_item_pspecs); } static void gtk_menu_tracker_item_action_added (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, const GVariantType *parameter_type, gboolean enabled, GVariant *state) { GtkMenuTrackerItem *self = GTK_MENU_TRACKER_ITEM (observer); GVariant *action_target; action_target = g_menu_item_get_attribute_value (self->item, G_MENU_ATTRIBUTE_TARGET, NULL); self->can_activate = (action_target == NULL && parameter_type == NULL) || (action_target != NULL && parameter_type != NULL && g_variant_is_of_type (action_target, parameter_type)); if (!self->can_activate) { if (action_target) g_variant_unref (action_target); return; } self->sensitive = enabled; if (action_target != NULL && state != NULL) { self->toggled = g_variant_equal (state, action_target); self->role = GTK_MENU_TRACKER_ITEM_ROLE_RADIO; } else if (state != NULL && g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN)) { self->toggled = g_variant_get_boolean (state); self->role = GTK_MENU_TRACKER_ITEM_ROLE_CHECK; } g_object_freeze_notify (G_OBJECT (self)); if (self->sensitive) g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_SENSITIVE]); if (self->toggled) g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_TOGGLED]); if (self->role != GTK_MENU_TRACKER_ITEM_ROLE_NORMAL) g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_ROLE]); if (state != NULL) { self->action_state = g_variant_ref (state); g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_ACTION_STATE]); } g_object_thaw_notify (G_OBJECT (self)); if (action_target) g_variant_unref (action_target); } static void gtk_menu_tracker_item_action_enabled_changed (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, gboolean enabled) { GtkMenuTrackerItem *self = GTK_MENU_TRACKER_ITEM (observer); if (!self->can_activate) return; if (self->sensitive == enabled) return; self->sensitive = enabled; g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_SENSITIVE]); } static void gtk_menu_tracker_item_action_state_changed (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, GVariant *state) { GtkMenuTrackerItem *self = GTK_MENU_TRACKER_ITEM (observer); GVariant *action_target; gboolean was_toggled; if (!self->can_activate) return; action_target = g_menu_item_get_attribute_value (self->item, G_MENU_ATTRIBUTE_TARGET, NULL); was_toggled = self->toggled; if (action_target) { self->toggled = g_variant_equal (state, action_target); g_variant_unref (action_target); } else if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN)) self->toggled = g_variant_get_boolean (state); else self->toggled = FALSE; if (self->toggled != was_toggled) g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_TOGGLED]); if (self->action_state) g_variant_unref (self->action_state); self->action_state = g_variant_ref (state); g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_ACTION_STATE]); } static void gtk_menu_tracker_item_action_removed (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name) { GtkMenuTrackerItem *self = GTK_MENU_TRACKER_ITEM (observer); if (!self->can_activate) return; g_object_freeze_notify (G_OBJECT (self)); if (self->sensitive) { self->sensitive = FALSE; g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_SENSITIVE]); } if (self->toggled) { self->toggled = FALSE; g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_TOGGLED]); } if (self->role != GTK_MENU_TRACKER_ITEM_ROLE_NORMAL) { self->role = GTK_MENU_TRACKER_ITEM_ROLE_NORMAL; g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_ROLE]); } if (self->action_state != NULL) { g_variant_unref (self->action_state); self->action_state = NULL; g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_ACTION_STATE]); } g_object_thaw_notify (G_OBJECT (self)); } static void gtk_menu_tracker_item_init_observer_iface (GtkActionObserverInterface *iface) { iface->action_added = gtk_menu_tracker_item_action_added; iface->action_enabled_changed = gtk_menu_tracker_item_action_enabled_changed; iface->action_state_changed = gtk_menu_tracker_item_action_state_changed; iface->action_removed = gtk_menu_tracker_item_action_removed; } GtkMenuTrackerItem * _gtk_menu_tracker_item_new (GtkActionObservable *observable, GMenuModel *model, gint item_index, const gchar *action_namespace, gboolean is_separator) { GtkMenuTrackerItem *self; const gchar *action_name; g_return_val_if_fail (GTK_IS_ACTION_OBSERVABLE (observable), NULL); g_return_val_if_fail (G_IS_MENU_MODEL (model), NULL); self = g_object_new (GTK_TYPE_MENU_TRACKER_ITEM, NULL); self->item = g_menu_item_new_from_model (model, item_index); self->action_namespace = g_strdup (action_namespace); self->observable = g_object_ref (observable); self->is_separator = is_separator; if (!is_separator && g_menu_item_get_attribute (self->item, "action", "&s", &action_name)) { GActionGroup *group = G_ACTION_GROUP (observable); const GVariantType *parameter_type; gboolean enabled; GVariant *state; gboolean found; state = NULL; if (action_namespace) { gchar *full_action; full_action = g_strjoin (".", action_namespace, action_name, NULL); gtk_action_observable_register_observer (self->observable, full_action, GTK_ACTION_OBSERVER (self)); found = g_action_group_query_action (group, full_action, &enabled, ¶meter_type, NULL, NULL, &state); g_free (full_action); } else { gtk_action_observable_register_observer (self->observable, action_name, GTK_ACTION_OBSERVER (self)); found = g_action_group_query_action (group, action_name, &enabled, ¶meter_type, NULL, NULL, &state); } if (found) gtk_menu_tracker_item_action_added (GTK_ACTION_OBSERVER (self), observable, NULL, parameter_type, enabled, state); else gtk_menu_tracker_item_action_removed (GTK_ACTION_OBSERVER (self), observable, NULL); if (state) g_variant_unref (state); } else { gboolean submenu_enabled; if (g_menu_item_get_attribute (self->item, "submenu-enabled", "b", &submenu_enabled)) { self->sensitive = submenu_enabled; } else { self->sensitive = TRUE; } } return self; } GtkActionObservable * _gtk_menu_tracker_item_get_observable (GtkMenuTrackerItem *self) { return self->observable; } /** * gtk_menu_tracker_item_get_is_separator: * @self: A #GtkMenuTrackerItem instance * * Returns whether the menu item is a separator. If so, only * certain properties may need to be obeyed. See the documentation * for #GtkMenuTrackerItem. */ gboolean gtk_menu_tracker_item_get_is_separator (GtkMenuTrackerItem *self) { return self->is_separator; } /** * gtk_menu_tracker_item_get_has_submenu: * @self: A #GtkMenuTrackerItem instance * * Returns whether the menu item has a submenu. If so, only * certain properties may need to be obeyed. See the documentation * for #GtkMenuTrackerItem. */ gboolean gtk_menu_tracker_item_get_has_submenu (GtkMenuTrackerItem *self) { GMenuModel *link; link = g_menu_item_get_link (self->item, G_MENU_LINK_SUBMENU); if (link) { g_object_unref (link); return TRUE; } else return FALSE; } const gchar * gtk_menu_tracker_item_get_label (GtkMenuTrackerItem *self) { const gchar *label = NULL; g_menu_item_get_attribute (self->item, G_MENU_ATTRIBUTE_LABEL, "&s", &label); return label; } /** * gtk_menu_tracker_item_get_icon: * * Returns: (transfer full): */ GIcon * gtk_menu_tracker_item_get_icon (GtkMenuTrackerItem *self) { GVariant *icon_data; GIcon *icon; icon_data = g_menu_item_get_attribute_value (self->item, "icon", NULL); if (icon_data == NULL) return NULL; icon = g_icon_deserialize (icon_data); g_variant_unref (icon_data); return icon; } gboolean gtk_menu_tracker_item_get_sensitive (GtkMenuTrackerItem *self) { return self->sensitive; } gboolean gtk_menu_tracker_item_get_visible (GtkMenuTrackerItem *self) { return TRUE; } GtkMenuTrackerItemRole gtk_menu_tracker_item_get_role (GtkMenuTrackerItem *self) { return self->role; } gboolean gtk_menu_tracker_item_get_toggled (GtkMenuTrackerItem *self) { return self->toggled; } const gchar * gtk_menu_tracker_item_get_accel (GtkMenuTrackerItem *self) { const gchar *accel = NULL; g_menu_item_get_attribute (self->item, "accel", "&s", &accel); return accel; } GMenuModel * _gtk_menu_tracker_item_get_submenu (GtkMenuTrackerItem *self) { return g_menu_item_get_link (self->item, "submenu"); } gchar * _gtk_menu_tracker_item_get_submenu_namespace (GtkMenuTrackerItem *self) { const gchar *namespace; if (g_menu_item_get_attribute (self->item, "action-namespace", "&s", &namespace)) { if (self->action_namespace) return g_strjoin (".", self->action_namespace, namespace, NULL); else return g_strdup (namespace); } else return g_strdup (self->action_namespace); } gboolean gtk_menu_tracker_item_get_should_request_show (GtkMenuTrackerItem *self) { return g_menu_item_get_attribute (self->item, "submenu-action", "&s", NULL); } gboolean gtk_menu_tracker_item_get_submenu_shown (GtkMenuTrackerItem *self) { return self->submenu_shown; } /** * gtk_menu_tracker_item_get_action_name: * @self: A #GtkMenuTrackerItem instance * * Returns a newly-allocated string containing the name of the action * associated with this menu item. * * Returns: (transfer full): the action name, free it with g_free() */ gchar * gtk_menu_tracker_item_get_action_name (GtkMenuTrackerItem *self) { const gchar *action_name; if (!g_menu_item_get_attribute (self->item, G_MENU_ATTRIBUTE_ACTION, "&s", &action_name)) return NULL; if (self->action_namespace) return g_strjoin (".", self->action_namespace, action_name, NULL); else return g_strdup (action_name); } GVariant * gtk_menu_tracker_item_get_action_state (GtkMenuTrackerItem *self) { if (self->action_state != NULL) return g_variant_ref (self->action_state); return NULL; } const gchar * gtk_menu_tracker_item_get_action_namespace (GtkMenuTrackerItem *self) { return self->action_namespace; } static void gtk_menu_tracker_item_set_submenu_shown (GtkMenuTrackerItem *self, gboolean submenu_shown) { if (submenu_shown == self->submenu_shown) return; self->submenu_shown = submenu_shown; g_object_notify_by_pspec (G_OBJECT (self), gtk_menu_tracker_item_pspecs[PROP_SUBMENU_SHOWN]); } void gtk_menu_tracker_item_activated (GtkMenuTrackerItem *self) { const gchar *action_name; GVariant *action_target; g_return_if_fail (GTK_IS_MENU_TRACKER_ITEM (self)); if (!self->can_activate) return; g_menu_item_get_attribute (self->item, G_MENU_ATTRIBUTE_ACTION, "&s", &action_name); action_target = g_menu_item_get_attribute_value (self->item, G_MENU_ATTRIBUTE_TARGET, NULL); if (self->action_namespace) { gchar *full_action; full_action = g_strjoin (".", self->action_namespace, action_name, NULL); g_action_group_activate_action (G_ACTION_GROUP (self->observable), full_action, action_target); g_free (full_action); } else g_action_group_activate_action (G_ACTION_GROUP (self->observable), action_name, action_target); if (action_target) g_variant_unref (action_target); } void gtk_menu_tracker_item_change_state (GtkMenuTrackerItem *self, GVariant *value) { const gchar *action_name; g_return_if_fail (GTK_IS_MENU_TRACKER_ITEM (self)); g_menu_item_get_attribute (self->item, G_MENU_ATTRIBUTE_ACTION, "&s", &action_name); if (self->action_namespace) { gchar *full_action; full_action = g_strjoin (".", self->action_namespace, action_name, NULL); g_action_group_change_action_state (G_ACTION_GROUP (self->observable), full_action, g_variant_ref(value)); g_free (full_action); } else g_action_group_change_action_state (G_ACTION_GROUP (self->observable), action_name, g_variant_ref(value)); } typedef struct { GtkMenuTrackerItem *item; gchar *submenu_action; gboolean first_time; } GtkMenuTrackerOpener; static void gtk_menu_tracker_opener_update (GtkMenuTrackerOpener *opener) { GActionGroup *group = G_ACTION_GROUP (opener->item->observable); gboolean is_open = TRUE; /* We consider the menu as being "open" if the action does not exist * or if there is another problem (no state, wrong state type, etc.). * If the action exists, with the correct state then we consider it * open if we have ever seen this state equal to TRUE. * * In the event that we see the state equal to FALSE, we force it back * to TRUE. We do not signal that the menu was closed because this is * likely to create UI thrashing. * * The only way the menu can have a true-to-false submenu-shown * transition is if the user calls _request_submenu_shown (FALSE). * That is handled in _free() below. */ if (g_action_group_has_action (group, opener->submenu_action)) { GVariant *state = g_action_group_get_action_state (group, opener->submenu_action); if (state) { if (g_variant_is_of_type (state, G_VARIANT_TYPE_BOOLEAN)) is_open = g_variant_get_boolean (state); g_variant_unref (state); } } /* If it is already open, signal that. * * If it is not open, ask it to open. */ if (is_open) gtk_menu_tracker_item_set_submenu_shown (opener->item, TRUE); if (!is_open || opener->first_time) { g_action_group_change_action_state (group, opener->submenu_action, g_variant_new_boolean (TRUE)); opener->first_time = FALSE; } } static void gtk_menu_tracker_opener_added (GActionGroup *group, const gchar *action_name, gpointer user_data) { GtkMenuTrackerOpener *opener = user_data; if (g_str_equal (action_name, opener->submenu_action)) gtk_menu_tracker_opener_update (opener); } static void gtk_menu_tracker_opener_removed (GActionGroup *action_group, const gchar *action_name, gpointer user_data) { GtkMenuTrackerOpener *opener = user_data; if (g_str_equal (action_name, opener->submenu_action)) gtk_menu_tracker_opener_update (opener); } static void gtk_menu_tracker_opener_changed (GActionGroup *action_group, const gchar *action_name, GVariant *new_state, gpointer user_data) { GtkMenuTrackerOpener *opener = user_data; if (g_str_equal (action_name, opener->submenu_action)) gtk_menu_tracker_opener_update (opener); } static void gtk_menu_tracker_opener_free (gpointer data) { GtkMenuTrackerOpener *opener = data; g_signal_handlers_disconnect_by_func (opener->item->observable, gtk_menu_tracker_opener_added, opener); g_signal_handlers_disconnect_by_func (opener->item->observable, gtk_menu_tracker_opener_removed, opener); g_signal_handlers_disconnect_by_func (opener->item->observable, gtk_menu_tracker_opener_changed, opener); g_action_group_change_action_state (G_ACTION_GROUP (opener->item->observable), opener->submenu_action, g_variant_new_boolean (FALSE)); gtk_menu_tracker_item_set_submenu_shown (opener->item, FALSE); g_free (opener->submenu_action); g_slice_free (GtkMenuTrackerOpener, opener); } static GtkMenuTrackerOpener * gtk_menu_tracker_opener_new (GtkMenuTrackerItem *item, const gchar *submenu_action) { GtkMenuTrackerOpener *opener; opener = g_slice_new (GtkMenuTrackerOpener); opener->first_time = TRUE; opener->item = item; if (item->action_namespace) opener->submenu_action = g_strjoin (".", item->action_namespace, submenu_action, NULL); else opener->submenu_action = g_strdup (submenu_action); g_signal_connect (item->observable, "action-added", G_CALLBACK (gtk_menu_tracker_opener_added), opener); g_signal_connect (item->observable, "action-removed", G_CALLBACK (gtk_menu_tracker_opener_removed), opener); g_signal_connect (item->observable, "action-state-changed", G_CALLBACK (gtk_menu_tracker_opener_changed), opener); gtk_menu_tracker_opener_update (opener); return opener; } void gtk_menu_tracker_item_request_submenu_shown (GtkMenuTrackerItem *self, gboolean shown) { const gchar *submenu_action; gboolean has_submenu_action; if (shown == self->submenu_requested) return; has_submenu_action = g_menu_item_get_attribute (self->item, "submenu-action", "&s", &submenu_action); self->submenu_requested = shown; /* If we have a submenu action, start a submenu opener and wait * for the reply from the client. Otherwise, simply open the * submenu immediately. */ if (has_submenu_action) { if (shown) g_object_set_data_full (G_OBJECT (self), "submenu-opener", gtk_menu_tracker_opener_new (self, submenu_action), gtk_menu_tracker_opener_free); else g_object_set_data (G_OBJECT (self), "submenu-opener", NULL); } else gtk_menu_tracker_item_set_submenu_shown (self, shown); } gboolean gtk_menu_tracker_item_get_attribute (GtkMenuTrackerItem *self, const gchar *attribute, const gchar *format, ...) { gboolean success = FALSE; GVariant *value; g_return_val_if_fail (GTK_IS_MENU_TRACKER_ITEM (self), FALSE); g_return_val_if_fail (attribute != NULL, FALSE); g_return_val_if_fail (format != NULL, FALSE); value = g_menu_item_get_attribute_value (self->item, attribute, NULL); if (value) { if (g_variant_check_format_string (value, format, TRUE)) { va_list args; va_start (args, format); g_variant_get_va (value, format, NULL, &args); va_end (args); success = TRUE; } g_variant_unref (value); } return success; } GVariant * gtk_menu_tracker_item_get_attribute_value (GtkMenuTrackerItem *self, const gchar *attribute, const GVariantType *expected_type) { return g_menu_item_get_attribute_value (self->item, attribute, expected_type); } qmenumodel-0.9.1/libqmenumodel/src/gtk/gtkmenutrackeritem.h0000644000000000000000000001232014200511032021026 0ustar /* * Copyright © 2011, 2013 Canonical Limited * * This library 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; either * version 2 of the licence, or (at your option) any later version. * * This library 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 library. If not, see . * * Author: Ryan Lortie */ #ifndef __GTK_MENU_TRACKER_ITEM_H__ #define __GTK_MENU_TRACKER_ITEM_H__ #include "gtkactionobservable.h" #define GTK_TYPE_MENU_TRACKER_ITEM (gtk_menu_tracker_item_get_type ()) #define GTK_MENU_TRACKER_ITEM(inst) (G_TYPE_CHECK_INSTANCE_CAST ((inst), \ GTK_TYPE_MENU_TRACKER_ITEM, GtkMenuTrackerItem)) #define GTK_IS_MENU_TRACKER_ITEM(inst) (G_TYPE_CHECK_INSTANCE_TYPE ((inst), \ GTK_TYPE_MENU_TRACKER_ITEM)) typedef struct _GtkMenuTrackerItem GtkMenuTrackerItem; #define GTK_TYPE_MENU_TRACKER_ITEM_ROLE (gtk_menu_tracker_item_role_get_type ()) typedef enum { GTK_MENU_TRACKER_ITEM_ROLE_NORMAL, GTK_MENU_TRACKER_ITEM_ROLE_CHECK, GTK_MENU_TRACKER_ITEM_ROLE_RADIO, } GtkMenuTrackerItemRole; GType gtk_menu_tracker_item_get_type (void) G_GNUC_CONST; GType gtk_menu_tracker_item_role_get_type (void) G_GNUC_CONST; GtkMenuTrackerItem * _gtk_menu_tracker_item_new (GtkActionObservable *observable, GMenuModel *model, gint item_index, const gchar *action_namespace, gboolean is_separator); GtkActionObservable * _gtk_menu_tracker_item_get_observable (GtkMenuTrackerItem *self); gboolean gtk_menu_tracker_item_get_is_separator (GtkMenuTrackerItem *self); gboolean gtk_menu_tracker_item_get_has_submenu (GtkMenuTrackerItem *self); const gchar * gtk_menu_tracker_item_get_label (GtkMenuTrackerItem *self); GIcon * gtk_menu_tracker_item_get_icon (GtkMenuTrackerItem *self); gboolean gtk_menu_tracker_item_get_sensitive (GtkMenuTrackerItem *self); gboolean gtk_menu_tracker_item_get_visible (GtkMenuTrackerItem *self); GtkMenuTrackerItemRole gtk_menu_tracker_item_get_role (GtkMenuTrackerItem *self); gboolean gtk_menu_tracker_item_get_toggled (GtkMenuTrackerItem *self); const gchar * gtk_menu_tracker_item_get_accel (GtkMenuTrackerItem *self); GMenuModel * _gtk_menu_tracker_item_get_submenu (GtkMenuTrackerItem *self); gchar * _gtk_menu_tracker_item_get_submenu_namespace (GtkMenuTrackerItem *self); gboolean gtk_menu_tracker_item_get_should_request_show (GtkMenuTrackerItem *self); void gtk_menu_tracker_item_activated (GtkMenuTrackerItem *self); void gtk_menu_tracker_item_change_state (GtkMenuTrackerItem *self, GVariant *value); void gtk_menu_tracker_item_request_submenu_shown (GtkMenuTrackerItem *self, gboolean shown); gboolean gtk_menu_tracker_item_get_submenu_shown (GtkMenuTrackerItem *self); gchar * gtk_menu_tracker_item_get_action_name (GtkMenuTrackerItem *self); GVariant * gtk_menu_tracker_item_get_action_state (GtkMenuTrackerItem *self); const gchar * gtk_menu_tracker_item_get_action_namespace (GtkMenuTrackerItem *self); gboolean gtk_menu_tracker_item_get_attribute (GtkMenuTrackerItem *self, const gchar *attribute, const gchar *format, ...); GVariant * gtk_menu_tracker_item_get_attribute_value (GtkMenuTrackerItem *self, const gchar *attribute, const GVariantType *expected_type); #endif qmenumodel-0.9.1/libqmenumodel/src/gtk/gtksimpleactionobserver.c0000644000000000000000000001347714200511032022077 0ustar /* * Copyright © 2013 Canonical Limited * * This library 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; either version 2 of the * licence or (at your option) any later version. * * This library 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 library. If not, see . * * Authors: Nick Dedekind observable); g_free(self->action_name); G_OBJECT_CLASS (gtk_simple_action_observer_parent_class)->finalize (object); } static void gtk_simple_action_observer_init (GtkSimpleActionObserver * self) { } static void gtk_simple_action_observer_class_init (GtkSimpleActionObserverClass *class) { class->finalize = gtk_simple_action_observer_finalize; } static void gtk_simple_action_observer_action_added (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, const GVariantType *parameter_type, gboolean enabled, GVariant *state) { GtkSimpleActionObserver* self; self = GTK_SIMPLE_ACTION_OBSERVER (observer); self->action_added(self, action_name, enabled, state); } static void gtk_simple_action_observer_action_enabled_changed (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, gboolean enabled) { GtkSimpleActionObserver* self; self = GTK_SIMPLE_ACTION_OBSERVER (observer); self->action_enabled_changed(self, action_name, enabled); } static void gtk_simple_action_observer_action_state_changed (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name, GVariant *state) { GtkSimpleActionObserver* self; self = GTK_SIMPLE_ACTION_OBSERVER (observer); self->action_state_changed(self, action_name, state); } static void gtk_simple_action_observer_action_removed (GtkActionObserver *observer, GtkActionObservable *observable, const gchar *action_name) { GtkSimpleActionObserver* self; self = GTK_SIMPLE_ACTION_OBSERVER (observer); self->action_removed(self, action_name); } static void gtk_simple_action_observer_init_observer_iface (GtkActionObserverInterface *iface) { iface->action_added = gtk_simple_action_observer_action_added; iface->action_enabled_changed = gtk_simple_action_observer_action_enabled_changed; iface->action_state_changed = gtk_simple_action_observer_action_state_changed; iface->action_removed = gtk_simple_action_observer_action_removed; } GtkSimpleActionObserver* gtk_simple_action_observer_new (GtkActionObservable *observable, GtkActionAddedFunc action_added, GtkActionEnabledChangedFunc action_enabled_changed, GtkActionStateChangedFunc action_state_changed, GtkActionRemovedFunc action_removed) { GtkSimpleActionObserver* self; self = g_object_new (GTK_TYPE_SIMPLE_ACTION_OBSERVER, NULL); self->observable = g_object_ref (observable); self->action_name = NULL; self->action_added = action_added; self->action_enabled_changed = action_enabled_changed; self->action_state_changed = action_state_changed; self->action_removed = action_removed; return self; } void gtk_simple_action_observer_register_action (GtkSimpleActionObserver *self, const gchar *action_name) { gtk_simple_action_observer_unregister_action(self); if (action_name && g_strcmp0(action_name, "") != 0) { self->action_name = g_strdup (action_name); if (g_strcmp0(self->action_name, "") != 0) { gtk_action_observable_register_observer (self->observable, self->action_name, GTK_ACTION_OBSERVER (self)); } } } void gtk_simple_action_observer_unregister_action (GtkSimpleActionObserver *self) { if (self->action_name) { gtk_action_observable_unregister_observer(self->observable, self->action_name, GTK_ACTION_OBSERVER (self)); g_free(self->action_name); self->action_name = NULL; } } qmenumodel-0.9.1/libqmenumodel/src/gtk/gtksimpleactionobserver.h0000644000000000000000000000664114200511032022077 0ustar /* * Copyright © 2013 Canonical Limited * * This library 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; either version 2 of the * licence or (at your option) any later version. * * This library 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 library. If not, see . * * Authors: Nick Dedekind . */ #ifndef QMENUMODEL_LOGGING_H #define QMENUMODEL_LOGGING_H #include Q_DECLARE_LOGGING_CATEGORY(ayatanamenumodel) #endif // QMENUMODEL_LOGGING_H qmenumodel-0.9.1/libqmenumodel/src/menunode.cpp0000644000000000000000000001375414200511032016515 0ustar /* * 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 "qmenumodelevents.h" #include #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), m_currentOpPosition(-1), m_currentOpAdded(0), m_currentOpRemoved(0) { 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; } int MenuNode::realPosition(int row) const { int result = row; if ((row >= 0) && (row < m_size)) { if (row >= m_currentOpPosition) { if ((m_currentOpRemoved > 0) && (row < (m_currentOpPosition + m_currentOpRemoved))) { result = -1; } else { result += (m_currentOpAdded - m_currentOpRemoved); } } return result; } else { return -1; } } 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)); } } m_size += added; for (int i = start; i < (start + added); i++) { MenuNode::create(m_model, i, this, m_listener); } } 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)); } } m_size -= removed; } } 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::commitOperation() { change(m_currentOpPosition, m_currentOpAdded, m_currentOpRemoved); m_currentOpPosition = -1; m_currentOpAdded = m_currentOpRemoved = 0; } void MenuNode::onItemsChanged(GMenuModel *model, gint position, gint removed, gint added, gpointer data) { MenuNode *self = reinterpret_cast(data); self->m_currentOpPosition = position; self->m_currentOpAdded = added; self->m_currentOpRemoved = removed; MenuNodeItemChangeEvent mnice(self, position, added, removed); QCoreApplication::sendEvent(self->m_listener, &mnice); self->commitOperation(); } qmenumodel-0.9.1/libqmenumodel/src/menunode.h0000644000000000000000000000401614200511032016151 0ustar /* * 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 MENUNODE_H #define MENUNODE_H #include #include #include #include #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); int realPosition(int row) const; void commitOperation(); 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; int m_currentOpPosition; int m_currentOpAdded; int m_currentOpRemoved; static void onItemsChanged(GMenuModel *model, gint position, gint removed, gint added, gpointer data); }; #endif qmenumodel-0.9.1/libqmenumodel/src/qdbusactiongroup.cpp0000644000000000000000000002065514200511032020272 0ustar /* * 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 "actionstateparser.h" #include "qdbusactiongroup.h" #include "qstateaction.h" #include "converter.h" #include "qmenumodelevents.h" // Qt #include #include #include /*! \qmltype QDBusActionGroup \inherits QDBusObject \brief A DBusActionGroup implementation to be used with \l QDBusMenuModel \b {This component is under heavy development.} This class can be used as a proxy for an action group that is exported over D-Bus \code QDBusActionGroup { id: actionGroup busType: 1 busName: "org.ayatana.menu" objectPath: "org/ayatana/menu/actions" } Button { onClicked: actionGroup.getAction("app.quit").trigger() } \endcode */ /*! \internal */ QDBusActionGroup::QDBusActionGroup(QObject *parent) :QObject(parent), QDBusObject(this), m_actionGroup(NULL), m_actionStateParser(new ActionStateParser(this)) { } /*! \internal */ QDBusActionGroup::~QDBusActionGroup() { clear(); } QStringList QDBusActionGroup::actions() const { if (!m_actionGroup) return QStringList(); QStringList list; gchar** actions = g_action_group_list_actions(m_actionGroup); for (uint i = 0; actions[i]; i++) { list << QString(actions[i]); } g_strfreev(actions); return list; } /*! \qmlmethod QDBusActionGroup::action(QString name) Look for a action with the same name and return a \l QStateAction object. \b Note: methods should only be called after the Component has completed. */ QStateAction *QDBusActionGroup::action(const QString &name) { QStateAction *act = actionImpl(name); if (act == 0) { act = new QStateAction(this, name); } return act; } QVariant QDBusActionGroup::actionState(const QString &name) { QVariant result; GVariant *state = g_action_group_get_action_state(m_actionGroup, name.toUtf8().data()); if (m_actionStateParser != NULL) { result = m_actionStateParser->toQVariant(state); } else { result = Converter::toQVariant(state); } if (state) { g_variant_unref(state); } return result; } bool QDBusActionGroup::hasAction(const QString &name) { if (m_actionGroup) { return g_action_group_has_action(m_actionGroup, name.toUtf8().data()); } else { return false; } } QStateAction *QDBusActionGroup::actionImpl(const QString &name) { Q_FOREACH(QStateAction *act, this->findChildren()) { if (act->name() == name) { return act; } } return 0; } /*! \internal */ void QDBusActionGroup::serviceVanish(GDBusConnection *) { setActionGroup(NULL); } /*! \internal */ void QDBusActionGroup::serviceAppear(GDBusConnection *connection) { GDBusActionGroup *ag = g_dbus_action_group_get(connection, busName().toUtf8().data(), objectPath().toUtf8().data()); setActionGroup(ag); if (ag == NULL) { stop(); } } /*! \internal */ void QDBusActionGroup::start() { QDBusObject::connect(); } /*! \internal */ void QDBusActionGroup::stop() { QDBusObject::disconnect(); } /*! \internal */ void QDBusActionGroup::setIntBusType(int busType) { if ((busType > DBusEnums::None) && (busType < DBusEnums::LastBusType)) { setBusType(static_cast(busType)); } } /*! \internal */ void QDBusActionGroup::setActionGroup(GDBusActionGroup *ag) { if (m_actionGroup == reinterpret_cast(ag)) { return; } clear(); m_actionGroup = reinterpret_cast(ag); if (m_actionGroup) { m_signalActionAddId = g_signal_connect(m_actionGroup, "action-added", G_CALLBACK(QDBusActionGroup::onActionAdded), this); m_signalActionRemovedId = g_signal_connect(m_actionGroup, "action-removed", G_CALLBACK(QDBusActionGroup::onActionRemoved), this); m_signalStateChangedId = g_signal_connect(m_actionGroup, "action-state-changed", G_CALLBACK(QDBusActionGroup::onActionStateChanged), this); gchar **actions = g_action_group_list_actions(m_actionGroup); for(guint i=0; i < g_strv_length(actions); i++) { DBusActionVisiblityEvent dave(actions[i], true); QCoreApplication::sendEvent(this, &dave); } g_strfreev(actions); } } ActionStateParser* QDBusActionGroup::actionStateParser() const { return m_actionStateParser; } void QDBusActionGroup::setActionStateParser(ActionStateParser* actionStateParser) { if (m_actionStateParser != actionStateParser) { m_actionStateParser = actionStateParser; Q_EMIT actionStateParserChanged(actionStateParser); } } /*! \internal */ void QDBusActionGroup::clear() { if (m_actionGroup) { g_signal_handler_disconnect(m_actionGroup, m_signalActionAddId); g_signal_handler_disconnect(m_actionGroup, m_signalActionRemovedId); g_signal_handler_disconnect(m_actionGroup, m_signalStateChangedId); m_signalActionAddId = m_signalActionRemovedId = m_signalStateChangedId = 0; } Q_FOREACH(QStateAction *act, this->findChildren()) { Q_EMIT actionVanish(act->name()); } if (m_actionGroup != NULL) { g_object_unref(m_actionGroup); m_actionGroup = NULL; } } /*! \internal */ void QDBusActionGroup::updateActionState(const QString &name, const QVariant &state) { if (m_actionGroup != NULL) { g_action_group_change_action_state(m_actionGroup, name.toUtf8().data(), Converter::toGVariant(state)); } } void QDBusActionGroup::activateAction(const QString &name, const QVariant ¶meter) { if (m_actionGroup != NULL) { g_action_group_activate_action(m_actionGroup, name.toUtf8().data(), Converter::toGVariant(parameter)); } } bool QDBusActionGroup::event(QEvent* e) { if (QDBusObject::event(e)) { return true; } else if (e->type() == DBusActionVisiblityEvent::eventType) { DBusActionVisiblityEvent *dave = static_cast(e); if (dave->visible) { Q_EMIT actionAppear(dave->name); } else { Q_EMIT actionVanish(dave->name); } Q_EMIT actionsChanged(); } else if (e->type() == DBusActionStateEvent::eventType) { DBusActionStateEvent *dase = static_cast(e); Q_EMIT actionStateChanged(dase->name, dase->state); } return QObject::event(e); } /*! \internal */ void QDBusActionGroup::onActionAdded(GDBusActionGroup *, gchar *name, gpointer data) { QDBusActionGroup *self = reinterpret_cast(data); DBusActionVisiblityEvent dave(name, true); QCoreApplication::sendEvent(self, &dave); } /*! \internal */ void QDBusActionGroup::onActionRemoved(GDBusActionGroup *, gchar *name, gpointer data) { QDBusActionGroup *self = reinterpret_cast(data); DBusActionVisiblityEvent dave(name, false); QCoreApplication::sendEvent(self, &dave); } /*! \internal */ void QDBusActionGroup::onActionStateChanged(GDBusActionGroup *, gchar *name, GVariant *value, gpointer data) { QDBusActionGroup *self = reinterpret_cast(data); DBusActionStateEvent dase(name, Converter::toQVariant(value)); QCoreApplication::sendEvent(self, &dase); } qmenumodel-0.9.1/libqmenumodel/src/qdbusactiongroup.h0000644000000000000000000000713214200511032017732 0ustar /* * 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 QDBUSACTIONGROUP_H #define QDBUSACTIONGROUP_H #include "qdbusobject.h" #include #include class QStateAction; class ActionStateParser; typedef char gchar; typedef void* gpointer; typedef struct _GVariant GVariant; typedef struct _GActionGroup GActionGroup; typedef struct _GDBusActionGroup GDBusActionGroup; 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) Q_PROPERTY(ActionStateParser* actionStateParser READ actionStateParser WRITE setActionStateParser NOTIFY actionStateParserChanged) Q_PROPERTY(QStringList actions READ actions NOTIFY actionsChanged) public: QDBusActionGroup(QObject *parent=0); ~QDBusActionGroup(); QStringList actions() const; void updateActionState(const QString &name, const QVariant &state); void activateAction(const QString &name, const QVariant ¶meter); bool hasAction(const QString &name); Q_INVOKABLE QStateAction *action(const QString &name); Q_INVOKABLE QVariant actionState(const QString &name); ActionStateParser* actionStateParser() const; // Ownership of the actionStateParser is not passed to this object. void setActionStateParser(ActionStateParser* actionStateParser); Q_SIGNALS: void busTypeChanged(DBusEnums::BusType type); void busNameChanged(const QString &busNameChanged); void objectPathChanged(const QString &objectPath); void statusChanged(DBusEnums::ConnectionStatus status); void actionAppear(const QString &name); void actionVanish(const QString &name); void actionStateChanged(const QString &name, QVariant state); void actionStateParserChanged(ActionStateParser* parser); void actionsChanged(); public Q_SLOTS: void start(); void stop(); protected: virtual void serviceAppear(GDBusConnection *connection); virtual void serviceVanish(GDBusConnection *connection); virtual bool event(QEvent* e); private: GActionGroup *m_actionGroup; int m_signalActionAddId; int m_signalActionRemovedId; int m_signalStateChangedId; ActionStateParser* m_actionStateParser; // workaround to support int as busType void setIntBusType(int busType); void setActionGroup(GDBusActionGroup *ag); QStateAction *actionImpl(const QString &name); void clear(); // glib slots static void onActionAdded(GDBusActionGroup *ag, gchar *name, gpointer data); static void onActionRemoved(GDBusActionGroup *ag, gchar *name, gpointer data); static void onActionStateChanged(GDBusActionGroup *ag, gchar *name, GVariant *value, gpointer data); }; #endif // QDBUSACTIONGROUP_H qmenumodel-0.9.1/libqmenumodel/src/qdbusmenumodel.cpp0000644000000000000000000000630314200511032017717 0ustar /* * 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 #include "qdbusmenumodel.h" #include "qmenumodelevents.h" #include /*! \qmltype QDBusMenuModel \inherits QDBusObject \brief The QDBusMenuModel class defines the list model for DBus menus \b {This component is under heavy development.} This class expose the menu previous exported over DBus. \code QDBusMenuModel { id: menuModel busType: 1 busName: "org.ayatana.menu" objectPath: "org/ayatana/menu" } ListView { id: view model: menuModel Component.onCompleted: menuModel.start() } \endcode */ QDBusMenuModel::QDBusMenuModel(QObject *parent) : QMenuModel(0, parent), QDBusObject(this) { } /*! \internal */ QDBusMenuModel::~QDBusMenuModel() { } /*! \qmlmethod QDBusMenuModel::start() Start dbus watch for the busName and wait until it appears. The status will change to connecting after call this function, and as soon the busName apperas and the objectPat was found this will change to Connected. \b Note: methods should only be called after the Component has completed. */ void QDBusMenuModel::start() { QDBusObject::connect(); } /*! \qmlmethod QDBusMenuModel::stop() Stops dbus watch and clear the model, the status wil change to Disconnected. \b Note: methods should only be called after the Component has completed. */ void QDBusMenuModel::stop() { QDBusObject::disconnect(); MenuModelEvent mme(NULL); QCoreApplication::sendEvent(this, &mme); } bool QDBusMenuModel::event(QEvent* e) { if (QDBusObject::event(e)) { return true; } return QMenuModel::event(e); } /*! \internal */ void QDBusMenuModel::serviceVanish(GDBusConnection *) { MenuModelEvent mme(NULL); QCoreApplication::sendEvent(this, &mme); } /*! \internal */ void QDBusMenuModel::serviceAppear(GDBusConnection *connection) { GMenuModel *model = G_MENU_MODEL(g_dbus_menu_model_get(connection, busName().toUtf8().data(), objectPath().toUtf8().data())); MenuModelEvent mme(model); QCoreApplication::sendEvent(this, &mme); //event handling takes care of the ref g_object_unref(model); } /*! \internal */ void QDBusMenuModel::setIntBusType(int busType) { if ((busType > DBusEnums::None) && (busType < DBusEnums::LastBusType)) { setBusType(static_cast(busType)); } } qmenumodel-0.9.1/libqmenumodel/src/qdbusmenumodel.h0000644000000000000000000000347214200511032017370 0ustar /* * 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 QDBUSMENUMODEL_H #define QDBUSMENUMODEL_H #include "qdbusobject.h" #include "qmenumodel.h" 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(DBusEnums::BusType type); void busNameChanged(const QString &busNameChanged); void objectPathChanged(const QString &objectPath); void statusChanged(DBusEnums::ConnectionStatus status); public Q_SLOTS: void start(); void stop(); protected: virtual void serviceAppear(GDBusConnection *connection); virtual void serviceVanish(GDBusConnection *connection); virtual bool event(QEvent* e); private: // workaround to support int as busType void setIntBusType(int busType); }; #endif qmenumodel-0.9.1/libqmenumodel/src/qdbusobject.cpp0000644000000000000000000001302014200511032017172 0ustar /* * 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 #include #include "qdbusobject.h" #include "qmenumodelevents.h" #include #include /*! \qmltype QDBusObject \brief The QDBusObject is a base class \b {This component is under heavy development.} This is a abstracted class used by QDBusMenuModel and QDBusActionGroup */ /*! \qmlproperty int QDBusObject::busType This property holds the dbus session type which will be used during the connection. This must be seteed before call start method The valid values are: \list \li 1 - SessionBus \li 2 - SystemBus \endlist */ /*! \qmlproperty int QDBusObject::busName This property holds the dbus service name related with menu. This must be seteed before call start method */ /*! \qmlproperty int QDBusObject::objectPath This property holds the dbus object path related with the menu. This must be seteed before call start method */ /*! \qmlproperty int QDBusObject::status This property holds current dbus connection status Te velid status are: \list \li 0 - Disconnected \li 1 - Connecting \li 2 - Connected \endlist */ QDBusObject::QDBusObject(QObject* listener) :m_listener(listener), m_watchId(0), m_busType(DBusEnums::None), m_status(DBusEnums::Disconnected) { qRegisterMetaType("DBusEnums::ConnectionStatus"); } QDBusObject::~QDBusObject() { if (m_watchId != 0) { g_bus_unwatch_name (m_watchId); m_watchId = 0; } } DBusEnums::BusType QDBusObject::busType() const { return m_busType; } void QDBusObject::setBusType(DBusEnums::BusType type) { if (m_busType != type) { if (m_status != DBusEnums::Disconnected) disconnect(); m_busType = type; Q_EMIT busTypeChanged(m_busType); } } QString QDBusObject::busName() const { return m_busName; } void QDBusObject::setBusName(const QString &busName) { if (m_busName != busName) { if (m_status != DBusEnums::Disconnected) disconnect(); m_busName = busName; Q_EMIT busNameChanged(m_busName); } } QString QDBusObject::objectPath() const { return m_objectPath; } void QDBusObject::setObjectPath(const QString &objectPath) { if (m_objectPath != objectPath) { if (m_status != DBusEnums::Disconnected) disconnect(); m_objectPath = objectPath; Q_EMIT objectPathChanged(m_objectPath); } } void QDBusObject::setStatus(DBusEnums::ConnectionStatus status) { if (m_status != status) { m_status = status; Q_EMIT statusChanged(m_status); } } DBusEnums::ConnectionStatus QDBusObject::status() const { return m_status; } void QDBusObject::connect() { if (m_status != DBusEnums::Disconnected) { return; } else if ((m_busType > DBusEnums::None) && !m_objectPath.isEmpty() && !m_busName.isEmpty()) { GBusType type = m_busType == DBusEnums::SessionBus ? G_BUS_TYPE_SESSION : G_BUS_TYPE_SYSTEM; m_watchId = g_bus_watch_name (type, m_busName.toUtf8().data(), G_BUS_NAME_WATCHER_FLAGS_AUTO_START, QDBusObject::onServiceAppeared, QDBusObject::onServiceVanished, this, NULL); setStatus(DBusEnums::Connecting); } else { qWarning() << "Invalid dbus connection args"; } } void QDBusObject::disconnect() { if (m_status != DBusEnums::Disconnected) { g_bus_unwatch_name (m_watchId); m_watchId = 0; setStatus(DBusEnums::Disconnected); } } void QDBusObject::onServiceAppeared(GDBusConnection *connection, const gchar *, const gchar *, gpointer data) { QDBusObject *self = reinterpret_cast(data); if (self->m_listener) { DbusObjectServiceEvent dose(connection, true); QCoreApplication::sendEvent(self->m_listener, &dose); } } void QDBusObject::onServiceVanished(GDBusConnection *connection, const gchar *, gpointer data) { QDBusObject *self = reinterpret_cast(data); if (self->m_listener) { DbusObjectServiceEvent dose(connection, false); QCoreApplication::sendEvent(self->m_listener, &dose); } } bool QDBusObject::event(QEvent* e) { if (e->type() == DbusObjectServiceEvent::eventType) { DbusObjectServiceEvent *dose = static_cast(e); if (dose->visible) { serviceAppear(dose->connection); setStatus(DBusEnums::Connected); } else { setStatus(DBusEnums::Connecting); serviceVanish(dose->connection); } return true; } return false; } qmenumodel-0.9.1/libqmenumodel/src/qdbusobject.h0000644000000000000000000000453314200511032016650 0ustar /* * 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 QDBUSOBJECT_H #define QDBUSOBJECT_H #include #include "dbus-enums.h" typedef unsigned int guint; typedef char gchar; typedef void* gpointer; typedef struct _GDBusConnection GDBusConnection; class QDBusObject { public: QDBusObject(QObject* listener); ~QDBusObject(); DBusEnums::BusType busType() const; void setBusType(DBusEnums::BusType type); QString busName() const; void setBusName(const QString &busName); QString objectPath() const; void setObjectPath(const QString &busName); DBusEnums::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(DBusEnums::BusType type) = 0; virtual void busNameChanged(const QString &busNameChanged) = 0; virtual void objectPathChanged(const QString &objectPath) = 0; virtual void statusChanged(DBusEnums::ConnectionStatus status) = 0; // This is not a Qbject, but we are passed events from superclass qobjects. virtual bool event(QEvent* e); private: QObject* m_listener; guint m_watchId; DBusEnums::BusType m_busType; QString m_busName; QString m_objectPath; DBusEnums::ConnectionStatus m_status; void setStatus(DBusEnums::ConnectionStatus status); // glib slots static void onServiceAppeared(GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer data); static void onServiceVanished(GDBusConnection *connection, const gchar *name, gpointer data); }; #endif qmenumodel-0.9.1/libqmenumodel/src/qmenumodel.cpp0000644000000000000000000001650714200511032017050 0ustar /* * 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 #include "qmenumodel.h" #include "menunode.h" #include "converter.h" #include "qmenumodelevents.h" #include #include /*! \qmltype QMenuModel \brief The QMenuModel class implements the base list model for menus \b {This component is under heavy development.} This is a abstracted class used by \l QDBusMenuModel. */ /*! \internal */ QMenuModel::QMenuModel(GMenuModel *other, QObject *parent) : QAbstractItemModel(parent), m_root(0) { setMenuModel(other); } /*! \internal */ QMenuModel::~QMenuModel() { clearModel(); } /*! \internal */ void QMenuModel::setMenuModel(GMenuModel *other) { if ((m_root != 0) && (m_root->model() == other)) { return; } beginResetModel(); clearModel(); if (other) { m_root = new MenuNode("", other, 0, 0, this); } endResetModel(); } /*! \internal */ void QMenuModel::clearModel() { if (m_root) { delete m_root; m_root = NULL; } } /*! \internal */ QHash QMenuModel::roleNames() const { static QHash roles; if (roles.isEmpty()) { roles[Action] = "action"; roles[Label] = "label"; roles[Extra] = "extra"; roles[Depth] = "depth"; roles[hasSection] = "hasSection"; roles[hasSubMenu] = "hasSubMenu"; } return roles; } /*! \internal */ QModelIndex QMenuModel::index(int row, int column, const QModelIndex &parent) const { MenuNode *node = nodeFromIndex(parent); if (node == 0) { return QModelIndex(); } if (parent.isValid()) { MenuNode *child = node->child(parent.row()); if (child) { node = child; } } 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 */ QVariant QMenuModel::data(const QModelIndex &index, int role) const { QVariant attribute; if (!index.isValid()) { return attribute; } MenuNode *node = nodeFromIndex(index); int row = node ? node->realPosition(index.row()) : -1; if (row >= 0) { 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; } } 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; } if (m_root) { return m_root->size(); } return 0; } /*! \internal */ int QMenuModel::columnCount(const QModelIndex &) const { return 1; } /*! \internal */ QVariant QMenuModel::getStringAttribute(MenuNode *node, int row, const QString &attribute) const { QVariant result; gchar* value = NULL; g_menu_model_get_item_attribute(node->model(), row, attribute.toUtf8().data(), "s", &value); if (value) { result = QVariant(QString::fromUtf8(value)); g_free(value); } return result; } /*! \internal */ QVariant QMenuModel::getExtraProperties(MenuNode *node, int row) const { GMenuAttributeIter *iter = g_menu_model_iterate_item_attributes(node->model(), row); if (iter == NULL) { return QVariant(); } QVariantMap extra; const gchar *attrName = NULL; GVariant *value = NULL; while (g_menu_attribute_iter_get_next (iter, &attrName, &value)) { if (strncmp("x-", attrName, 2) == 0) { extra.insert(parseExtraPropertyName(attrName), Converter::toQVariant(value)); } } return extra; } bool QMenuModel::event(QEvent* e) { if (e->type() == MenuNodeItemChangeEvent::eventType) { MenuNodeItemChangeEvent *mnice = static_cast(e); QModelIndex index = indexFromNode(mnice->node); if (mnice->removed > 0) { beginRemoveRows(index, mnice->position, mnice->position + mnice->removed - 1); mnice->node->commitOperation(); endRemoveRows(); } if (mnice->added > 0) { beginInsertRows(index, mnice->position, mnice->position + mnice->added - 1); mnice->node->commitOperation(); endInsertRows(); } return true; } else if (e->type() == MenuModelEvent::eventType) { MenuModelEvent *mme = static_cast(e); setMenuModel(mme->model); return true; } return QAbstractItemModel::event(e); } /*! \internal */ QModelIndex QMenuModel::indexFromNode(MenuNode *node) const { if (node == m_root) { return QModelIndex(); } return createIndex(node->position(), 0, node); } /*! \internal */ MenuNode *QMenuModel::nodeFromIndex(const QModelIndex &index) const { MenuNode *node = 0; if (index.isValid()) { node = reinterpret_cast(index.internalPointer()); } else { node = m_root; } 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)); } qmenumodel-0.9.1/libqmenumodel/src/qmenumodelevents.cpp0000644000000000000000000000524314200511032020270 0ustar /* * Copyright 2013 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: * Nicholas Dedekind #include #include "qmenumodelevents.h" const QEvent::Type MenuNodeItemChangeEvent::eventType = static_cast(QEvent::registerEventType()); const QEvent::Type DBusActionStateEvent::eventType = static_cast(QEvent::registerEventType()); const QEvent::Type DBusActionVisiblityEvent::eventType = static_cast(QEvent::registerEventType()); const QEvent::Type MenuModelEvent::eventType = static_cast(QEvent::registerEventType()); const QEvent::Type DbusObjectServiceEvent::eventType = static_cast(QEvent::registerEventType()); MenuNodeItemChangeEvent::MenuNodeItemChangeEvent(MenuNode* _node, int _position, int _removed, int _added) : QEvent(MenuNodeItemChangeEvent::eventType), node(_node), position(_position), removed(_removed), added(_added) {} DBusActionEvent::DBusActionEvent(const QString& _name, QEvent::Type type) : QEvent(type), name(_name) { } DBusActionVisiblityEvent::DBusActionVisiblityEvent(const QString& _name, bool _visible) : DBusActionEvent(_name, DBusActionVisiblityEvent::eventType), visible(_visible) { } DBusActionStateEvent::DBusActionStateEvent(const QString& _name, const QVariant& _state) : DBusActionEvent(_name, DBusActionStateEvent::eventType), state(_state) { } DbusObjectServiceEvent::DbusObjectServiceEvent(GDBusConnection* _connection, bool _visible) : QEvent(DbusObjectServiceEvent::eventType), connection(_connection), visible(_visible) { if (connection) { g_object_ref(connection); } } DbusObjectServiceEvent::~DbusObjectServiceEvent() { if (connection) { g_object_unref(connection); } } MenuModelEvent::MenuModelEvent(GMenuModel* _model) : QEvent(MenuModelEvent::eventType), model(_model) { if (model) { g_object_ref(model); } } MenuModelEvent::~MenuModelEvent() { if (model) { g_object_unref(model); } } qmenumodel-0.9.1/libqmenumodel/src/qmenumodelevents.h0000644000000000000000000000457314200511032017742 0ustar /* * Copyright 2013 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: * Nicholas Dedekind #include class MenuNode; typedef struct _GDBusConnection GDBusConnection; typedef struct _GMenuModel GMenuModel; /* Event for a connection update for a dbus object */ class DbusObjectServiceEvent : public QEvent { public: static const QEvent::Type eventType; DbusObjectServiceEvent(GDBusConnection* connection, bool visible); ~DbusObjectServiceEvent(); GDBusConnection* connection; bool visible; }; /* Event for an update to the gmenumodel */ class MenuModelEvent : public QEvent { public: static const QEvent::Type eventType; MenuModelEvent(GMenuModel *model); ~MenuModelEvent(); GMenuModel *model; }; /* Event for a GAction (base) */ class DBusActionEvent : public QEvent { public: QString name; protected: DBusActionEvent(const QString& name, QEvent::Type type); }; /* Event for a GAction add/remove */ class DBusActionVisiblityEvent : public DBusActionEvent { public: static const QEvent::Type eventType; DBusActionVisiblityEvent(const QString& name, bool visible); bool visible; }; /* Event for a GAction state value update */ class DBusActionStateEvent : public DBusActionEvent { public: static const QEvent::Type eventType; DBusActionStateEvent(const QString& name, const QVariant& state); QVariant state; }; /* Event for changing gmenumodel entries */ class MenuNodeItemChangeEvent : public QEvent { public: static const QEvent::Type eventType; MenuNodeItemChangeEvent(MenuNode* node, int position, int removed, int added); MenuNode* node; int position; int removed; int added; }; #endif //QMENUMODELEVENTS_H qmenumodel-0.9.1/libqmenumodel/src/qmenumodel.h0000644000000000000000000000427514200511032016514 0ustar /* * 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 QMENUTREEMODEL_H #define QMENUTREEMODEL_H #include class MenuNode; typedef struct _GMenuModel GMenuModel; class QMenuModel : public QAbstractItemModel { Q_OBJECT public: enum MenuRoles { Action = Qt::DisplayRole + 1, Label, Extra, Depth, hasSection, hasSubMenu }; ~QMenuModel(); /* QAbstractItemModel */ 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(); protected: QMenuModel(GMenuModel *other=0, QObject *parent=0); void setMenuModel(GMenuModel *model); GMenuModel *menuModel() const; virtual bool event(QEvent* e); private: MenuNode *m_root; MenuNode* nodeFromIndex(const QModelIndex &index) const; QModelIndex indexFromNode(MenuNode *node) 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; QString parseExtraPropertyName(const QString &name) const; void clearModel(); }; #endif qmenumodel-0.9.1/libqmenumodel/src/qmenumodel.pc.in0000644000000000000000000000055314200511032017267 0ustar prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=@CMAKE_INSTALL_PREFIX@ libdir=${exec_prefix}/@CMAKE_INSTALL_LIBDIR@ includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ Name: qmenumodel Description: Qt binding for GMenuModel. Version: @PROJECT_VERSION@ Requires.private: Qt5Core Qt5Widgets gio-2.0 Libs: -L${libdir} -l@SHAREDLIBNAME@ Cflags: -I${includedir}/@INCLUDEDIR@ qmenumodel-0.9.1/libqmenumodel/src/qstateaction.cpp0000644000000000000000000001123014200511032017365 0ustar /* * 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 "qstateaction.h" #include "converter.h" #include "qdbusactiongroup.h" /*! \qmltype QStateAction \inherits QAction \brief A QStateAction implementation to be used with \l QDBusActionGroup \b {This component is under heavy development.} This class can be used as a proxy for an action that is exported over D-Bus \code QDBusActionGroup { id: actionGroup busType: 1 busName: "org.ayatana.menu" objectPath: "org/ayatana/menu/actions" } Button { visible: actionGroup.getAction("button.bvisible").status } \endcode */ /*! \internal */ QStateAction::QStateAction(QDBusActionGroup *group, const QString &name) : QObject(group), m_group(group), m_name(name) { // This keep the code clean // But maybe we need move the action state control to QActionGroup to optimizations QObject::connect(m_group, SIGNAL(actionAppear(QString)), this, SLOT(onActionAppear(QString))); QObject::connect(m_group, SIGNAL(actionVanish(QString)), this, SLOT(onActionVanish(QString))); QObject::connect(m_group, SIGNAL(actionStateChanged(QString,QVariant)), this, SLOT(onActionStateChanged(QString,QVariant))); m_valid = m_group->hasAction(name); if (m_valid) { setState(m_group->actionState(name)); } } /*! \qmlproperty int QStateAction::state This property holds the current action state */ QVariant QStateAction::state() const { return m_state; } /*! \qmlproperty int QStateAction::isValid This property return if the current Action is valid or not A valid Action is a action which has a DBus action linked */ bool QStateAction::isValid() const { return m_valid; } /*! Request for the state of action to be changed to \a paramenter. This call merely requests a change. The action may refuse to change its state or may change its state to something other than \a paramenter. */ void QStateAction::updateState(const QVariant &state) { QVariant v = state; if (v.convert(m_state.type())) m_group->updateActionState(m_name, v); } /*! Request for the state of action to be changed to \a paramenter. This call merely requests a change. The action may refuse to change its state or may change its state to something other than \a paramenter. */ void QStateAction::updateStateByVariantString(const QString &state) { updateState(Converter::toQVariantFromVariantString(state)); } /*! Activates the action passing \a parameter. \a parameter must be the correct type of parameter for the action */ void QStateAction::activate(const QVariant ¶meter) { m_group->activateAction(m_name, parameter); } /*! Activates the action passing \a parameter using GLib Variant String format. \a parameter must be the correct type of parameter for the action */ void QStateAction::activateByVariantString(const QString ¶meter) { activate(Converter::toQVariantFromVariantString(parameter)); } /*! \internal */ QString QStateAction::name() const { return m_name; } /*! \internal */ void QStateAction::setValid(bool valid) { if (m_valid != valid) { m_valid = valid; Q_EMIT validChanged(m_valid); } } /*! \internal */ void QStateAction::setState(const QVariant &state) { QVariant v = state; if (!m_state.isValid() || (v.convert(m_state.type()) && v != m_state)) { m_state = v; Q_EMIT stateChanged(m_state); } } /*! \internal */ void QStateAction::onActionAppear(const QString &name) { if (m_name == name) { setState(m_group->actionState(name)); setValid(true); } } /*! \internal */ void QStateAction::onActionVanish(const QString &name) { if (m_name == name) { setState(QVariant()); setValid(false); } } /*! \internal */ void QStateAction::onActionStateChanged(const QString &name, const QVariant &state) { if (m_name == name) { setState(state); } } qmenumodel-0.9.1/libqmenumodel/src/qstateaction.h0000644000000000000000000000367614200511032017051 0ustar /* * 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 QDBUSACTION_H #define QDBUSACTION_H #include #include class QDBusActionGroup; class QStateAction : public QObject { Q_OBJECT Q_PROPERTY(QString name READ name) Q_PROPERTY(QVariant state READ state NOTIFY stateChanged) Q_PROPERTY(bool valid READ isValid NOTIFY validChanged) public: QVariant state() const; bool isValid() const; Q_INVOKABLE void activate(const QVariant ¶meter = QVariant()); Q_INVOKABLE void activateByVariantString(const QString ¶meter = QString()); Q_INVOKABLE void updateState(const QVariant ¶meter); Q_INVOKABLE void updateStateByVariantString(const QString ¶meter); Q_SIGNALS: void stateChanged(QVariant state); void validChanged(bool valid); private Q_SLOTS: void onActionAppear(const QString &name); void onActionVanish(const QString &name); void onActionStateChanged(const QString &name, const QVariant &state); private: QDBusActionGroup *m_group; QVariant m_state; bool m_valid; QString m_name; QStateAction(QDBusActionGroup *group, const QString &name); void setValid(bool valid); void setState(const QVariant &state); QString name() const; friend class QDBusActionGroup; }; #endif qmenumodel-0.9.1/NEWS0000644000000000000000000000264014200511032011242 0ustar Overview of changes in ayatana-indicator-session 0.9.1 * CMakeLists.txt: Rewrite and enable Ayatana-style options. * Fix naming of AyatanaMenuModel qml type. * Fix versioning in examples and tests. * Make the qmltests test imports, at least. * Fix all build warnings. * CMakeLists.txt: Clean up compilation flags. Overview of changes in ayatana-indicator-session 0.9.0 * Rename Unity* symbols. * Rename unity* source files. * libqmenumodel/src/CMakeLists.txt: Bump the SOVERSION of libqmenumodel. * Bump QMenuModel QML module to 1.0. * Rename com.ubuntu.menu. * examples/exportmenu.py: Rename x-canonical-currentvalue. * examples/unityqmlmenumodel.qml: Rename com.canonical.indicator.sound. * libqmenumodel/QMenuModel/plugin.h: Rename com.canonical.qmenumodel. * Rename com.canonical.test*. Overview of changes in ayatana-indicator-session 0.8.1 * Move glib includes outside of extern "C". * Add Travis CI builds configuration. Overview of changes in ayatana-indicator-session 0.8.0 * Move qmenumodel from UBports' project context over to Ayatana Indicators' project context. Same maintainers, same people involved, but the project is now maintained together with the other Indicators' related projects. * Upstream version bump (to go along with the other indicator related projects maintained by the Ayatana Indicators project). Next official release will be 0.8.0. qmenumodel-0.9.1/README0000644000000000000000000000235414200511032011425 0ustar QMenuModel - a Qt/QML binding for GMenuModel (see http://developer.gnome.org/gio/unstable/GMenuModel.html) = Building = The build system uses cmake. To compile, simply invoke cmake and then make, e.g.: $ cmake . $ make = Running unit tests = To run the unit tests, you will need dbus-test-runner. If it wasn’t previously installed, install it and then re-run cmake. Then run either of these commands: $ make test - or - $ ctest = Getting code coverage information = To run the unit tests and generate code coverage information, you need to re-run cmake with ENABLE_COVERAGE set to ON and then invoke `make lcov`. This requires lcov to be installed. $ cmake -DENABLE_COVERAGE=ON . $ make lcov This will generate a report (coverage/index.html) which you can view in a browser. = API documentation = To generate API documentation, you need to re-run cmake with GENERATE_DOC set to ON and then invoke `make qdoc`. This requires qdoc3 to be installed. $ cmake -DGENERATE_DOC=ON . $ make qdoc The documentation is generated in the HTML format under doc/html/. = Examples = There are examples of how to use QDBusMenuModel in QML, refer to the README file under the examples/ directory for instructions. qmenumodel-0.9.1/tests/client/actiongrouptest.cpp0000644000000000000000000001317114200511032017122 0ustar /* * Copyright 2012 Canonical Ltd. * Copyright 2022 Robert Tari * * 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 * Robert Tari */ #include "qdbusmenumodel.h" #include "qdbusactiongroup.h" #include "dbusmenuscript.h" #include "qstateaction.h" #include #include #include #include class ActionGroupTest : public QObject { Q_OBJECT private: QDBusMenuModel m_model; QDBusActionGroup m_actionGroup; DBusMenuScript m_script; void getMenuAction(QStateAction **act, int index) { // Append 2 menus m_script.walk(2); // Get Action QVariant action = m_model.data(m_model.index(index, 0), QMenuModel::Action); QVERIFY(action.isValid()); *act = m_actionGroup.action(action.toString()); QVERIFY(act); } private Q_SLOTS: void init() { m_model.stop(); m_model.setBusType(DBusEnums::SessionBus); m_model.setBusName(MENU_SERVICE_NAME); m_model.setObjectPath(MENU_OBJECT_PATH); m_actionGroup.stop(); m_actionGroup.setBusType(DBusEnums::SessionBus); m_actionGroup.setBusName(MENU_SERVICE_NAME); m_actionGroup.setObjectPath(MENU_OBJECT_PATH); // start model m_model.start(); m_actionGroup.start(); // Make menu available m_script.publishMenu(); } void initTestCase() { QVERIFY(m_script.connect()); } void cleanupTestCase() { m_script.quit(); } void cleanup() { m_script.unpublishMenu(); } /* * Test if the propety busType handle correct integer values */ void testBusTypeProperty() { m_actionGroup.setProperty("busType", 1); QCOMPARE(m_actionGroup.busType(), DBusEnums::SessionBus); m_actionGroup.setProperty("busType", 2); QCOMPARE(m_actionGroup.busType(), DBusEnums::SystemBus); m_actionGroup.setProperty("busType", 0); QCOMPARE(m_actionGroup.busType(), DBusEnums::SystemBus); m_actionGroup.setProperty("busType", 10); QCOMPARE(m_actionGroup.busType(), DBusEnums::SystemBus); } /* * Test if QDBusActionGroup change to correct state after DBus * ervice appear */ void testServiceAppear() { m_script.unpublishMenu(); QCOMPARE(m_actionGroup.status(), DBusEnums::Connecting); // Make menu available m_script.connect(); m_script.publishMenu(); QCOMPARE(m_actionGroup.status(), DBusEnums::Connected); } /* * Test if QDBusActionGroup change to correct state after DBus * service disappear */ void testServiceDisappear() { QCOMPARE(m_actionGroup.status(), DBusEnums::Connected); // Append menus m_script.walk(2); // Remove menu from dbus m_script.unpublishMenu(); QCOMPARE(m_actionGroup.status(), DBusEnums::Connecting); m_actionGroup.stop(); QCOMPARE(m_actionGroup.status(), DBusEnums::Disconnected); } void testActionName() { QStateAction *act = nullptr; getMenuAction(&act, 1); QCOMPARE(act->property("name").toString(), QString("Menu1Act")); } /* * Test if Action::trigger active the action over DBus */ void testStringActionActivation() { QStateAction *act = nullptr; getMenuAction(&act, 1); act->activate(QVariant("42")); // wait for dbus propagation QTest::qWait(500); QPair result = m_script.popActivatedAction(); QCOMPARE(result.first, QString("Menu1Act")); QCOMPARE(result.second.toString(), QString("42")); } void testStringActionActivationByVariantString() { QStateAction *act = nullptr; getMenuAction(&act, 1); act->activateByVariantString("\"53\""); // wait for dbus propagation QTest::qWait(500); QPair result = m_script.popActivatedAction(); QCOMPARE(result.first, QString("Menu1Act")); QCOMPARE(result.second.toString(), QString("53")); } /* * Test if Action became invalid after desappear from DBus */ void testRemoveAction() { // Append 2 menus m_script.walk(2); // Get Action QStateAction *act = m_actionGroup.action(QString("Menu1Act")); QVERIFY(act); QVERIFY(act->isValid()); // Remove 1 menu m_script.walk(1); //Check if action is invalid QVERIFY(!act->isValid()); } /* * Test if Action became valid after service appears */ void testActionIsValid() { // Get invalid Action QStateAction *act = m_actionGroup.action(QString("Menu1Act")); QVERIFY(act); QVERIFY(!act->isValid()); QVERIFY(!act->state().isValid()); // Append menus m_script.walk(2); // Action appear QVERIFY(act->isValid()); } }; QTEST_MAIN(ActionGroupTest); #include "actiongrouptest.moc" qmenumodel-0.9.1/tests/client/ayatanamenuactiontest.cpp0000644000000000000000000000312214200511032020264 0ustar /* * Copyright 2014 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: * Nick Dedekind */ #include "ayatanamenumodel.h" #include "ayatanamenuaction.h" #include #include class AyatanaMenuActionTest : public QObject { Q_OBJECT private: private Q_SLOTS: /* * Test if the propety busType handle correct integer values */ void testDestroyAfterModel() { AyatanaMenuModel* model = new AyatanaMenuModel; AyatanaMenuAction* action = new AyatanaMenuAction; action->setModel(model); delete model; delete action; } /* * Test if the propety busType handle correct integer values */ void testDestroyBeforeModel() { AyatanaMenuModel* model = new AyatanaMenuModel; AyatanaMenuAction* action = new AyatanaMenuAction; action->setModel(model); delete action; delete model; } }; QTEST_MAIN(AyatanaMenuActionTest) #include "ayatanamenuactiontest.moc" qmenumodel-0.9.1/tests/client/cachetest.cpp0000644000000000000000000001342414200511032015634 0ustar /* * 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" #include #include class TestModel : public QMenuModel { Q_OBJECT public: TestModel() : QMenuModel(0) { GMenu *menu3 = g_menu_new(); g_menu_append(menu3, "menu4", NULL); g_menu_append(menu3, "menu5", NULL); g_menu_append(menu3, "menu6", 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; } void removeItem(int section, int index) { GMenu *menu = m_menus[section]; g_menu_remove(menu, index); } void insertItem(int section, int index, const QString &label) { GMenu *menu = m_menus[section]; g_menu_insert(menu, index, label.toUtf8().data(), NULL); } QList cacheIndexes() const { QList indexes = cache().keys(); qSort(indexes); return indexes; } private: QList m_menus; }; class CacheTest : public QObject { Q_OBJECT private Q_SLOTS: // Verify that normal menu items are not cached (only sub-menus are) void testCacheContents() { TestModel menu; QVERIFY(menu.cacheIndexes().isEmpty()); menu.data(menu.index(1), QMenuModel::Label); QVERIFY(menu.cacheIndexes().isEmpty()); menu.data(menu.index(2), QMenuModel::Action); QVERIFY(menu.cacheIndexes().isEmpty()); } // Verify that the link attribute always returns the same cached menu void testStaticMenuCache() { TestModel menu; QModelIndex index = menu.index(3); QVariant data = menu.data(index, QMenuModel::LinkSection); QCOMPARE(menu.cacheIndexes(), QList() << 3); QVariant data2 = menu.data(index, QMenuModel::LinkSection); QCOMPARE(menu.cacheIndexes(), QList() << 3); QVERIFY(data.value() == data2.value()); QMenuModel *section = qvariant_cast(data); index = section->index(1); data = menu.data(index, QMenuModel::LinkSection); data2 = menu.data(index, QMenuModel::LinkSection); QCOMPARE(menu.cacheIndexes(), QList() << 3); QVERIFY(data.value() == data2.value()); } // Verify that the cache is correctly updated after inserting a new item void testInsertItems() { TestModel menu; QModelIndex index = menu.index(3); QVariant data = menu.data(index, QMenuModel::LinkSection); QCOMPARE(menu.cacheIndexes(), QList() << 3); menu.insertItem(0, 4, "newMenu"); QCOMPARE(menu.cacheIndexes(), QList() << 3); menu.insertItem(0, 1, "newMenu"); QCOMPARE(menu.cacheIndexes(), QList() << 4); index = menu.index(4); QVariant data2 = menu.data(index, QMenuModel::LinkSection); QCOMPARE(menu.cacheIndexes(), QList() << 4); QVERIFY(data.value() == data2.value()); } // Verify that the cache is correctly updated after removing an item that wasn’t cached void testRemoveNonCachedItem() { TestModel menu; QModelIndex index = menu.index(3); QVariant data = menu.data(index, QMenuModel::LinkSection); QCOMPARE(menu.cacheIndexes(), QList() << 3); menu.removeItem(0, 1); QCOMPARE(menu.cacheIndexes(), QList() << 2); index = menu.index(2); QVariant data2 = menu.data(index, QMenuModel::LinkSection); QCOMPARE(menu.cacheIndexes(), QList() << 2); QVERIFY(data.value() == data2.value()); } // Verify that the cache is correctly updated after removing a cached item void testRemoveCachedItem() { TestModel menu; QModelIndex index = menu.index(3); QVariant data = menu.data(index, QMenuModel::LinkSection); QCOMPARE(menu.cacheIndexes(), QList() << 3); menu.removeItem(0, 3); QVERIFY(menu.cacheIndexes().isEmpty()); } // Verify that the cache is correctly updated after multiple insertions and removals void testMultiplesUpdates() { TestModel menu; QVERIFY(menu.cacheIndexes().isEmpty()); menu.data(menu.index(3), QMenuModel::LinkSection); QCOMPARE(menu.cacheIndexes(), QList() << 3); menu.insertItem(0, 1, "newMenu"); menu.insertItem(0, 2, "newMenu"); menu.insertItem(0, 6, "newMenu"); menu.insertItem(0, 3, "newMenu"); menu.insertItem(0, 7, "newMenu"); QCOMPARE(menu.cacheIndexes(), QList() << 6); menu.removeItem(0, 4); menu.removeItem(0, 6); menu.removeItem(0, 2); QCOMPARE(menu.cacheIndexes(), QList() << 4); menu.removeItem(0, 4); QVERIFY(menu.cacheIndexes().isEmpty()); } }; QTEST_MAIN(CacheTest) #include "cachetest.moc" qmenumodel-0.9.1/tests/client/CMakeLists.txt0000644000000000000000000000510014200511032015715 0ustar macro(declare_test testname) add_executable(${testname} ${testname}.cpp) target_link_libraries(${testname} qmenumodel dbusmenuscript ${GLIB_LDFLAGS} ${GIO_LDFLAGS} Qt5::Core Qt5::DBus Qt5::Widgets Qt5::Test Qt5::Qml Qt5::Quick ) if(TEST_XML_OUTPUT) set(TEST_ARGS -p -xunitxml -p -o -p test_${testname}.xml) else() set(TEST_ARGS "") endif() add_test(${testname} ${DBUS_RUNNER} --task ${CMAKE_CURRENT_BINARY_DIR}/${testname} ${TEST_ARGS} --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};QT_QPA_PLATFORM=minimal") set(TESTS ${TESTS} ${testname}) endmacro(declare_test testname) macro(declare_simple_test testname) add_executable(${testname} ${testname}.cpp) target_link_libraries(${testname} qmenumodel ${GLIB_LDFLAGS} ${GIO_LDFLAGS} Qt5::Core Qt5::Test ) add_test(${testname} ${CMAKE_CURRENT_BINARY_DIR}/${testname}) set_tests_properties(${testname} PROPERTIES TIMEOUT ${CTEST_TESTING_TIMEOUT}) endmacro(declare_simple_test testname) include_directories(${src_SOURCE_DIR} ${dbusmenuscript_SOURCE_DIR} ${GLIB_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) declare_test(actiongrouptest) declare_test(qmltest) declare_test(convertertest) declare_test(modelsignalstest) declare_test(treetest) declare_test(ayatanamenuactiontest) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/qmlfiles.h.in ${CMAKE_CURRENT_BINARY_DIR}/qmlfiles.h) find_package(CoverageReport) enable_coverage_report( TARGETS qmenumodel FILTER ${CMAKE_SOURCE_DIR}/tests/* ${CMAKE_BINARY_DIR}/* TESTS ${TESTS} ) qmenumodel-0.9.1/tests/client/convertertest.cpp0000644000000000000000000003727414200511032016611 0ustar /* * Copyright 2012-2016 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 * Marco Trevisan */ #include #include "converter.h" #include #include #include class QGVariantType { public: QGVariantType() : type(NULL) {} QGVariantType(const GVariantType *gvtype) : type(gvtype) {} const GVariantType *getType() const { return type; } operator const GVariantType*() const { return type; } private: const GVariantType *type; }; Q_DECLARE_METATYPE(QGVariantType); class QGVariant { public: QGVariant() : variant(NULL) {} ~QGVariant() { if (variant) g_variant_unref(variant); } QGVariant(GVariant *gv) : variant(g_variant_ref_sink(gv)) {} QGVariant(const QGVariant &other) : QGVariant(other.variant) {} GVariant *gvariant() const { return variant; } operator GVariant*() const { return variant; } private: GVariant *variant; }; Q_DECLARE_METATYPE(QGVariant); class ConverterTest : public QObject { Q_OBJECT private: bool compare(const QVariant &qv, const GVariantType *type) { bool result; GVariant *gv = Converter::toGVariant(qv); result = g_variant_type_equal(g_variant_get_type(gv), type); if (!result) { qWarning() << "types are different: QVariant:" << qv.typeName() << "Result:" << g_variant_type_peek_string(g_variant_get_type(gv)) << "Expected:"<< g_variant_type_peek_string(type); } g_variant_unref(gv); return result; } bool compare(GVariant *gv, const QVariant::Type type) { g_variant_ref_sink(gv); const QVariant& qv = Converter::toQVariant(gv); bool result = (qv.type() == type); if (!result) { qWarning() << "types are different: GVariant:" << g_variant_type_peek_string(g_variant_get_type(gv)) << "Result:" << qv.type() << "Expected:"<< type; } g_variant_unref(gv); return result; } bool compareWithSchema(const QVariant &qv, const QString strType) { GVariantType* expected_type; expected_type = g_variant_type_new(strType.toUtf8().data()); bool result; GVariant *gv = Converter::toGVariantWithSchema(qv, strType.toUtf8().data()); result = g_variant_type_equal(g_variant_get_type(gv), expected_type); if (!result) { qWarning() << "types are different: QVariant:" << qv.typeName() << "Result:" << g_variant_type_peek_string(g_variant_get_type(gv)) << "Expected:"<< g_variant_type_peek_string(expected_type); } g_variant_unref(gv); g_variant_type_free(expected_type); return result; } private Q_SLOTS: /* * Test converter QVariant to GVariant */ void testConvertToGVariant_data() { QTest::addColumn("value"); QTest::addColumn("expectedType"); QTest::newRow("Boolean") << QVariant(true) << QGVariantType(G_VARIANT_TYPE_BOOLEAN); QTest::newRow("Byte") << QVariant::fromValue(42) << QGVariantType(G_VARIANT_TYPE_BYTE); QTest::newRow("Int16") << QVariant::fromValue(-42) << QGVariantType(G_VARIANT_TYPE_INT16); QTest::newRow("UInt16") << QVariant::fromValue(-42) << QGVariantType(G_VARIANT_TYPE_UINT16); QTest::newRow("Int32") << QVariant(-42) << QGVariantType(G_VARIANT_TYPE_INT32); QTest::newRow("UInt32") << QVariant((uint)42) << QGVariantType(G_VARIANT_TYPE_UINT32); QTest::newRow("Int64") << QVariant::fromValue(-42) << QGVariantType(G_VARIANT_TYPE_INT64); QTest::newRow("UInt64") << QVariant::fromValue(42) << QGVariantType(G_VARIANT_TYPE_UINT64); QTest::newRow("Int64") << QVariant::fromValue(-42) << QGVariantType(G_VARIANT_TYPE_INT64); QTest::newRow("UInt64") << QVariant::fromValue(42) << QGVariantType(G_VARIANT_TYPE_UINT64); QTest::newRow("Double") << QVariant((double)42.42) << QGVariantType(G_VARIANT_TYPE_DOUBLE); QTest::newRow("String") << QVariant(QString("42")) << QGVariantType(G_VARIANT_TYPE_STRING); QTest::newRow("String List") << QVariant(QStringList({"42", "42"})) << QGVariantType(G_VARIANT_TYPE_STRING_ARRAY); QTest::newRow("ByteArray") << QVariant(QByteArray("42")) << QGVariantType(G_VARIANT_TYPE_BYTESTRING); QTest::newRow("Map") << QVariant(QVariantMap()) << QGVariantType(G_VARIANT_TYPE_VARDICT); QTest::newRow("Map Filled") << QVariant(QVariantMap({{"fooBar", 0xdeadbeef}})) << QGVariantType(G_VARIANT_TYPE_VARDICT); QTest::newRow("List") << QVariant(QVariantList()) << QGVariantType(G_VARIANT_TYPE_UNIT); QTest::newRow("List Filled") << QVariant(QVariantList({"fooBar", 0xdeadbeef})) << QGVariantType(G_VARIANT_TYPE("(su)")); QVariant result; result.setValue(QByteArrayList({"42", "53"})); QTest::newRow("ByteArrayList") << result << QGVariantType(G_VARIANT_TYPE_BYTESTRING_ARRAY); } void testConvertToGVariant() { QFETCH(QVariant, value); QFETCH(QGVariantType, expectedType); QVERIFY(compare(value, expectedType)); } void testConvertToGVariantAndBack_data() { testConvertToGVariant_data(); } void testConvertToGVariantAndBack() { QFETCH(QVariant, value); GVariant *gv = Converter::toGVariant(value); QVERIFY(gv != NULL); QCOMPARE(Converter::toQVariant(gv), value); g_variant_unref(gv); } void testTupleConversion() { QVariantList qTuple; qTuple << 1 << "2" << 3.3; GVariant *gTuple = Converter::toGVariant(qTuple); QVERIFY(g_variant_type_is_tuple(g_variant_get_type(gTuple))); QCOMPARE(g_variant_n_children(gTuple), (gsize)3); GVariant *v = g_variant_get_child_value(gTuple, 0); int v0 = g_variant_get_int32(v); QCOMPARE(v0, 1); g_variant_unref(v); v = g_variant_get_child_value(gTuple, 1); const gchar *v1 = g_variant_get_string(v, NULL); QCOMPARE(QString(v1), QString("2")); g_variant_unref(v); v = g_variant_get_child_value(gTuple, 2); gdouble v2 = g_variant_get_double(v); QCOMPARE(v2, 3.3); g_variant_unref(v); g_variant_unref(gTuple); } void testConvertToGVariantWithSchema_data() { QTest::addColumn("value"); QTest::addColumn("schema"); // convert to byte QTest::newRow("byte") << QVariant::fromValue(1) << "y"; // convert to integer QTest::newRow("integer") << QVariant::fromValue(1) << "i"; QTest::newRow("integer from double") << QVariant::fromValue(1.1) << "i"; QTest::newRow("int16") << QVariant::fromValue(-1) << "n"; QTest::newRow("uint16") << QVariant::fromValue(1) << "q"; QTest::newRow("uint32") << QVariant::fromValue(1) << "u"; QTest::newRow("int64") << QVariant::fromValue(1) << "x"; QTest::newRow("uint64") << QVariant::fromValue(1) << "t"; // convert to variant QTest::newRow("variant from int") << QVariant::fromValue(1) << "v"; QTest::newRow("variant from double") << QVariant::fromValue(1.1) << "v"; QTest::newRow("variant from string") << QVariant::fromValue("string") << "v"; // convert to bool QTest::newRow("bool") << QVariant::fromValue(true) << "b"; QTest::newRow("bool from int") << QVariant::fromValue(1) << "b"; // convert to double QTest::newRow("double") << QVariant::fromValue(1.0) << "d"; QTest::newRow("double from int") << QVariant::fromValue(1) << "d"; // convert to string QTest::newRow("string") << QVariant::fromValue("FoooBar") << "s"; QTest::newRow("string from int") << QVariant::fromValue(1) << "s"; QTest::newRow("string from double") << QVariant::fromValue(1.1) << "s"; // convert to tuple QVariantList list = QVariantList() << QVariant::fromValue(true) << QVariant::fromValue(1) << QVariant::fromValue(1) << QVariant::fromValue("test1"); QTest::newRow("tuple") << QVariant(list) << "(bdis)"; // convert to array list = QVariantList() << QVariant::fromValue(1) << QVariant::fromValue(1); QTest::newRow("int list") << QVariant(list) << "ad"; list = QVariantList() << QVariant::fromValue("test1") << QVariant::fromValue("test2"); QTest::newRow("string list") << QVariant(list) << "as"; // convert to array of tuple QVariantList si1(QVariantList() << QVariant::fromValue("test1") << QVariant::fromValue(1)); QVariantList si2(QVariantList() << QVariant::fromValue("test1") << QVariant::fromValue(1)); list = QVariantList() << QVariant::fromValue(si1) << QVariant::fromValue(si2); QTest::newRow("array of tuple") << QVariant(list) << "a(sd)"; // convert to vardict QVariantMap map; map["test1"] = QVariant::fromValue(1); map["test2"] = QVariant::fromValue(1); QTest::newRow("map") << QVariant(map) << "a{sv}"; } void testConvertToGVariantWithSchema() { QFETCH(QVariant, value); QFETCH(QString, schema); QVERIFY(compareWithSchema(value, schema)); } /* * Test converter GVariant to QVariant */ void testConvertToQVariant_data() { QTest::addColumn("value"); QTest::addColumn("expectedType"); QTest::newRow("Boolean") << QGVariant(g_variant_new_boolean(TRUE)) << (unsigned) QVariant::Bool; QTest::newRow("Byte") << QGVariant(g_variant_new_byte(53)) << (unsigned) QMetaType::UChar; QTest::newRow("Int16") << QGVariant(g_variant_new_int16(-53)) << (unsigned) QMetaType::Short; QTest::newRow("UInt16") << QGVariant(g_variant_new_uint16(53)) << (unsigned) QMetaType::UShort; QTest::newRow("Int32") << QGVariant(g_variant_new_int32(-53)) << (unsigned) QVariant::Int; QTest::newRow("UInt32") << QGVariant(g_variant_new_uint32(53)) << (unsigned) QVariant::UInt; QTest::newRow("Int64") << QGVariant(g_variant_new_int64(-53)) << (unsigned) QVariant::LongLong; QTest::newRow("UInt64") << QGVariant(g_variant_new_uint64(53)) << (unsigned) QVariant::ULongLong; QTest::newRow("Double") << QGVariant(g_variant_new_double(53.3)) << (unsigned) QVariant::Double; QTest::newRow("String") << QGVariant(g_variant_new_string("53")) << (unsigned) QVariant::String; QTest::newRow("Byte string") << QGVariant(g_variant_new_bytestring("53")) << (unsigned) QVariant::ByteArray; QTest::newRow("Tuple") << QGVariant(g_variant_new("(si)", "foo", 53)) << (unsigned) QVariant::List; GVariantBuilder* builder = g_variant_builder_new(G_VARIANT_TYPE_VARDICT); g_variant_builder_add(builder, "{sv}", "fooo", g_variant_new_int64(53)); QTest::newRow("Map") << QGVariant(g_variant_builder_end(builder)) << (unsigned) QVariant::Map; g_variant_builder_unref(builder); builder = g_variant_builder_new(G_VARIANT_TYPE("ai")); g_variant_builder_add(builder, "i", g_variant_new_int32(53)); QTest::newRow("List") << QGVariant(g_variant_new("ai", builder)) << (unsigned) QVariant::List; g_variant_builder_unref(builder); QTest::newRow("Tuple") << QGVariant(g_variant_new("(i)", 53)) << (unsigned) QVariant::List; const gchar *byteArray[] = {"42", "53", NULL}; QTest::newRow("ByteArrayList") << QGVariant(g_variant_new_bytestring_array(byteArray, -1)) << (unsigned) QMetaType::QByteArrayList; QTest::newRow("String List") << QGVariant(g_variant_new_strv(byteArray, -1)) << (unsigned) QVariant::StringList; } void testConvertToQVariant() { QFETCH(QGVariant, value); QFETCH(unsigned, expectedType); QVERIFY(compare(value, (QVariant::Type) expectedType)); } void testConvertToQVariantAndBack_data() { testConvertToQVariant_data(); } void testConvertToQVariantAndBack() { QFETCH(QGVariant, value); QVariant qv = Converter::toQVariant(value); QVERIFY(qv.isValid()); GVariant *gv = Converter::toGVariant(qv); gboolean equals = g_variant_equal(value, gv); if (!equals && qv.type() == QVariant::List) { QVERIFY(g_variant_type_is_array(g_variant_get_type(value))); QVERIFY(g_variant_type_is_tuple(g_variant_get_type(gv))); gsize vsize = g_variant_n_children(value); QCOMPARE(vsize, g_variant_n_children(gv)); for (gsize i = 0; i < vsize; ++i) { equals = g_variant_equal(g_variant_get_child_value(value, i), g_variant_get_child_value(gv, i)); if (!equals) break; } } if (!equals) { gchar *vs = g_variant_print(value, TRUE); gchar *gvs = g_variant_print(gv, TRUE); qWarning() << "Values do not match. Old" << vs << "converted" << gvs; g_free(vs); g_free(gvs); } g_variant_unref(gv); QVERIFY(equals != FALSE); } void testConvertToQVariantFromString_data() { QTest::addColumn("value"); QTest::addColumn("expectedType"); QTest::newRow("Boolean") << "true" << (unsigned) QVariant::Bool; QTest::newRow("Byte") << "byte 0xFF" << (unsigned) QMetaType::UChar; QTest::newRow("Int16") << "int16 65" << (unsigned) QMetaType::Short; QTest::newRow("UInt16") << "uint16 65" << (unsigned) QMetaType::UShort; QTest::newRow("Int32") << "int32 65" << (unsigned) QVariant::Int; QTest::newRow("UInt32") << "uint32 65" << (unsigned) QVariant::UInt; QTest::newRow("Int64") << "int64 65" << (unsigned) QVariant::LongLong; QTest::newRow("UInt64") << "uint64 65" << (unsigned) QVariant::ULongLong; QTest::newRow("Double") << "double 65" << (unsigned) QVariant::Double; QTest::newRow("String") << "string '65'" << (unsigned) QVariant::String; QTest::newRow("String simple") << "\"65\"" << (unsigned) QVariant::String; QTest::newRow("String List") << "['foo', 'bar']" << (unsigned) QVariant::StringList; QTest::newRow("Byte string") << "b'fooo'" << (unsigned) QVariant::ByteArray; QTest::newRow("Map") << "{'foo': <65>}" << (unsigned) QVariant::Map; QTest::newRow("List") << "[65, 66]" << (unsigned) QVariant::List; QTest::newRow("Tuple") << "('foo', 65)" << (unsigned) QVariant::List; QTest::newRow("ByteArrayList") << "[b'foo', b'bar']" << (unsigned) QMetaType::QByteArrayList; } void testConvertToQVariantFromString() { QFETCH(QString, value); QFETCH(unsigned, expectedType); QCOMPARE(Converter::toQVariantFromVariantString(value).type(), (QVariant::Type) expectedType); } }; QTEST_MAIN(ConverterTest) #include "convertertest.moc" qmenumodel-0.9.1/tests/client/importtest.qml0000644000000000000000000000152414200511032016110 0ustar /* * Copyright 2012 Canonical Ltd. * Copyright 2021 UBports Foundation * * 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 * Dalton Durst */ import QtQuick 2.0 import QMenuModel 1.0 Item { } qmenumodel-0.9.1/tests/client/menuchangestest.cpp0000644000000000000000000000375114200511032017070 0ustar /* * 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 "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() { QVERIFY(m_script.connect()); } void cleanupTestCase() { m_script.quit(); } void init() { m_model.stop(); m_model.setBusType(DBusEnums::SessionBus); m_model.setBusName(MENU_SERVICE_NAME); m_model.setObjectPath(MENU_OBJECT_PATH); } void cleanup() { m_script.unpublishMenu(); } /* * Test it the model updates correct after remove or add a new menu */ 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" qmenumodel-0.9.1/tests/client/modelsignalstest.cpp0000644000000000000000000002051214200511032017246 0ustar /* * 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 "qmenumodel.h" #include #include #include #include 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: /* * 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" qmenumodel-0.9.1/tests/client/modeltest.cpp0000644000000000000000000001421114200511032015664 0ustar /* * 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 "qdbusmenumodel.h" #include "dbusmenuscript.h" #include #include #include #include #include class ModelTest : public QObject { Q_OBJECT private: DBusMenuScript m_script; QDBusMenuModel m_model; private Q_SLOTS: void initTestCase() { QVERIFY(m_script.connect()); } void cleanupTestCase() { m_script.quit(); } void init() { m_model.stop(); m_model.setBusType(DBusEnums::SessionBus); m_model.setBusName(MENU_SERVICE_NAME); m_model.setObjectPath(MENU_OBJECT_PATH); } void cleanup() { m_script.unpublishMenu(); } /* * Test if parent function always return a empty QModelIndex */ void testParent() { QCOMPARE(m_model.parent(QModelIndex()), QModelIndex()); } /* * Test if the propety busType handle correct integer values */ void testBusTypeProperty() { m_model.setProperty("busType", 1); QCOMPARE(m_model.busType(), DBusEnums::SessionBus); m_model.setProperty("busType", 2); QCOMPARE(m_model.busType(), DBusEnums::SystemBus); m_model.setProperty("busType", 0); QCOMPARE(m_model.busType(), DBusEnums::SystemBus); m_model.setProperty("busType", 10); QCOMPARE(m_model.busType(), DBusEnums::SystemBus); } /* * Test if model return the correct values for standard properties */ 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(), DBusEnums::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")); // Wait for menu load (submenus are loaded async) QTest::qWait(500); QCOMPARE(m_model.rowCount(m_model.index(2, 0)), 2); } /* * Test if the model parse correct GVariant values types */ 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(), DBusEnums::Connected); QCOMPARE(m_model.rowCount(), 4); QVariant e = m_model.data(m_model.index(0, 0), QMenuModel::Extra); QVERIFY(e.isValid()); QVariantMap extra = e.toMap(); // Boolean QVariant v = extra["boolean"]; QCOMPARE(v.type(), QVariant::Bool); QCOMPARE(v.toBool(), true); // Byte v = extra["byte"]; QCOMPARE(v.typeName(), "uchar"); QCOMPARE(v.value(), (uchar)42); // Int16 v = extra["int16"]; QCOMPARE(v.typeName(), "short"); QCOMPARE(v.value(), (short)-42); // UInt16 v = extra["uint16"]; QCOMPARE(v.typeName(), "ushort"); QCOMPARE(v.value(), (ushort)42); // Int32 v = extra["int32"]; QCOMPARE(v.type(), QVariant::Int); QCOMPARE(v.toInt(), -42); // UInt32 v = extra["uint32"]; QCOMPARE(v.type(), QVariant::UInt); QCOMPARE(v.toUInt(), (uint) 42); // Int64 v = extra["int64"]; QCOMPARE(v.type(), QVariant::LongLong); QCOMPARE(v.value(), (long) -42); // UInt64 v = extra["uint64"]; QCOMPARE(v.type(), QVariant::ULongLong); QCOMPARE(v.value(), (ulong) 42); // Double v = extra["double"]; QCOMPARE(v.type(), QVariant::Double); QCOMPARE(v.toDouble(), 42.42); // String v = extra["string"]; QCOMPARE(v.type(), QVariant::String); QCOMPARE(v.toString(), QString("42")); // Map v = extra["map"]; QVariantMap map; map.insert("int64", QVariant::fromValue(-42)); map.insert("string", "42"); map.insert("double", 42.42); QCOMPARE(v.type(), QVariant::Map); QCOMPARE(v.toMap(), map); // Utf8 v = extra["utf8"]; QCOMPARE(v.type(), QVariant::String); QCOMPARE(v.toString(), QString("dança")); // Tuple v = extra["tuple"]; QVariantList lst; lst << "1" << 2 << 3.3; QCOMPARE(v.type(), QVariant::List); QCOMPARE(v.toList(), lst); } /* * Test if model is destroyed without crash */ void testDestroyModel() { // Make menu available m_script.publishMenu(); m_script.run(); // create a new model QDBusMenuModel *model = new QDBusMenuModel(); model->setBusType(DBusEnums::SessionBus); model->setBusName(MENU_SERVICE_NAME); model->setObjectPath(MENU_OBJECT_PATH); model->start(); // Wait for dbus sync QTest::qWait(500); delete model; } }; QTEST_MAIN(ModelTest) #include "modeltest.moc" qmenumodel-0.9.1/tests/client/qmlfiles.h.in0000644000000000000000000000027314200511032015555 0ustar #include const QString QML_BASE_DIR = QStringLiteral("@libqmenumodel_BINARY_DIR@"); const QString IMPORTTEST_QML = QStringLiteral("@CMAKE_CURRENT_SOURCE_DIR@/importtest.qml"); qmenumodel-0.9.1/tests/client/qmltest.cpp0000644000000000000000000000403214200511032015355 0ustar /* * Copyright 2012 Canonical Ltd. * Copyright 2021 UBports Foundation * * 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 * Dalton Durst */ #include #include #include "qdbusmenumodel.h" #include "dbusmenuscript.h" #include "qmlfiles.h" #include #include #include #include #include #include #include #include class QMLTest : public QObject { Q_OBJECT private: DBusMenuScript m_script; std::shared_ptr loadView(const QString baseDir, const QString fileLocation) { std::shared_ptr view = std::make_shared(); view->engine()->addImportPath(baseDir); view->setSource(QUrl::fromLocalFile(fileLocation)); return view; } private Q_SLOTS: void initTestCase() { QVERIFY(m_script.connect()); } void cleanupTestCase() { m_script.quit(); } void init() { } void cleanup() { m_script.unpublishMenu(); } /* * Test if importtest.qml can be loaded successfully */ void importtest() { std::shared_ptr view = loadView(QML_BASE_DIR, IMPORTTEST_QML); QTRY_VERIFY2((view->status() == QQuickView::Ready), "loadmodel.qml view never became ready"); } }; QTEST_MAIN(QMLTest) #include "qmltest.moc" qmenumodel-0.9.1/tests/client/script_actiongrouptest.py0000755000000000000000000000205514200511032020356 0ustar #!/usr/bin/env python3 # Copyright 2013 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 . import time from gi.repository import GLib from menuscript import Script, ActionList, MENU_OBJECT_PATH from gi._gi import variant_type_from_string al = ActionList(MENU_OBJECT_PATH) al.appendItem("Menu0", "Menu0Act", actionStateType=variant_type_from_string('s')) al.appendItem("Menu1", "Menu1Act", actionStateType=variant_type_from_string('s')) al.removeItem("1", "Menu1Act") t = Script.create(al) t.run() qmenumodel-0.9.1/tests/client/script_menuchangestest.py0000755000000000000000000000163714200511032020326 0ustar #!/usr/bin/env python3 # Copyright 2013 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 . 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() qmenumodel-0.9.1/tests/client/script_modeltest.py0000755000000000000000000000470714200511032017132 0ustar #!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright 2013 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 . # import time from gi.repository import GLib from menuscript import Script, ActionList, MENU_OBJECT_PATH al = ActionList(MENU_OBJECT_PATH) # create map pmap = {'int64' : GLib.Variant('x', -42), 'string': GLib.Variant('s', '42'), 'double': GLib.Variant('d', 42.42)} 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', '42'), 'x-utf8' : GLib.Variant('s', 'dança'), 'x-map' : GLib.Variant('a{sv}', pmap), 'x-tuple' : GLib.Variant('(sid)', ("1", 2, 3.3)), }) 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() qmenumodel-0.9.1/tests/client/script_qmltest.py0000755000000000000000000000154114200511032016614 0ustar #!/usr/bin/env python3 # Copyright 2013 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 . # 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() qmenumodel-0.9.1/tests/client/script_servicetest.py0000755000000000000000000000154114200511032017463 0ustar #!/usr/bin/env python3 # Copyright 2013 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 . # 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() qmenumodel-0.9.1/tests/client/servicetest.cpp0000644000000000000000000000552014200511032016227 0ustar /* * 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 "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(DBusEnums::SessionBus); model->setBusName(MENU_SERVICE_NAME); model->setObjectPath(MENU_OBJECT_PATH); } private Q_SLOTS: void initTestCase() { QVERIFY(m_script.connect()); } void cleanupTestCase() { m_script.quit(); } void init() { m_model.stop(); m_model.setBusType(DBusEnums::SessionBus); m_model.setBusName(MENU_SERVICE_NAME); m_model.setObjectPath(MENU_OBJECT_PATH); } void cleanup() { m_script.unpublishMenu(); } void testMenuStartStopWithNoService() { m_model.start(); QCOMPARE(m_model.status(), DBusEnums::Connecting); m_model.stop(); QCOMPARE(m_model.status(), DBusEnums::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(), DBusEnums::Connected); // Diconnect model m_model.stop(); QCOMPARE(m_model.status(), DBusEnums::Disconnected); } void testMenuServiceAppearAndDissapear() { m_model.start(); QCOMPARE(m_model.status(), DBusEnums::Connecting); QSignalSpy spy(&m_model, SIGNAL(statusChanged(DBusEnums::ConnectionStatus))); // Make menu available m_script.publishMenu(); // singal changed to connected QCOMPARE(spy.count(), 1); QCOMPARE(m_model.status(), DBusEnums::Connected); // remove menu service m_script.unpublishMenu(); // signal changed to connecting QCOMPARE(spy.count(), 2); QCOMPARE(m_model.status(), DBusEnums::Connecting); } }; QTEST_MAIN(ServiceTest) #include "servicetest.moc" qmenumodel-0.9.1/tests/client/treetest.cpp0000644000000000000000000000701014200511032015522 0ustar /* * 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" #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 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.model()->index(0, 0, row3); QVERIFY(row4.isValid()); QCOMPARE(menu.rowCount(row4), 0); QCOMPARE(menu.data(row4, QMenuModel::Depth).toInt(), 1); QCOMPARE(menu.data(row4, QMenuModel::Label).toString(), QString("menu4")); QModelIndex row5 = row3.model()->index(1, 0, row3); QVERIFY(row5.isValid()); QCOMPARE(menu.rowCount(row5), 2); QCOMPARE(menu.data(row5, QMenuModel::Depth).toInt(), 1); QCOMPARE(menu.data(row5, QMenuModel::Label).toString(), QString("menu5")); QModelIndex row6 = row5.model()->index(0, 0, row5); QVERIFY(row6.isValid()); QCOMPARE(menu.rowCount(row6), 0); QCOMPARE(menu.data(row6, QMenuModel::Depth).toInt(), 2); QCOMPARE(menu.data(row6, QMenuModel::Label).toString(), QString("menu6")); QModelIndex row7 = row5.model()->index(1, 0, row5); QVERIFY(row7.isValid()); QCOMPARE(menu.rowCount(row7), 0); QCOMPARE(menu.data(row7, QMenuModel::Depth).toInt(), 2); 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::Depth).toInt(), 1); QCOMPARE(menu.data(parent_6, QMenuModel::Label).toString(), QString("menu5")); } }; QTEST_MAIN(TreeTest) #include "treetest.moc" qmenumodel-0.9.1/tests/CMakeLists.txt0000644000000000000000000000027214200511032014444 0ustar find_package(Qt5Quick REQUIRED) find_package(Qt5Test REQUIRED) find_package(Qt5Widgets REQUIRED) find_package(Qt5DBus REQUIRED) add_subdirectory(script) add_subdirectory(client) qmenumodel-0.9.1/tests/script/CMakeLists.txt0000644000000000000000000000033314200511032015746 0ustar project(dbusmenuscript) add_library(dbusmenuscript STATIC dbusmenuscript.cpp) set_target_properties(dbusmenuscript PROPERTIES COMPILE_FLAGS -fPIC) target_link_libraries(dbusmenuscript Qt5::Core Qt5::DBus Qt5::Test) qmenumodel-0.9.1/tests/script/dbusmenuscript.cpp0000644000000000000000000000514214200511032016764 0ustar /* * 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 "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); } } QPair DBusMenuScript::popActivatedAction() { if (m_script) { QDBusMessage reply = m_script->call("popActivatedAction"); if (reply.arguments().count() > 0) { QVariant value; QString name = reply.arguments()[0].toString(); if (reply.arguments().count() > 1) { value = reply.arguments()[1]; } return qMakePair(name, value); } } return qMakePair(QString(), QVariant()); } qmenumodel-0.9.1/tests/script/dbusmenuscript.h0000644000000000000000000000275414200511032016437 0ustar /* * Copyright 2012 Canonical Ltd. * Copyright 2022 Robert Tari * * 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 * Robert Tari */ #ifndef DBUSMENUSCRIPT_H #define DBUSMENUSCRIPT_H #include #include #include #define SCRIPT_SERVICE_NAME "org.ayatana.test" #define SCRIPT_OBJECT_PATH "/org/ayatana/test/menuscript" #define SCRIPT_INTERFACE_NAME "org.ayatana.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(); QPair popActivatedAction(); private: QDBusInterface *m_script; }; #endif qmenumodel-0.9.1/tests/script/menuscript.py0000644000000000000000000001735014200511032015760 0ustar # Copyright 2013 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 . # 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 = "org.ayatana.test" INTERFACE_NAME = "org.ayatana.test.menuscript" OBJECT_PATH = "/org/ayatana/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 """ TODO: We only support string states for now """ @dbus.service.method(dbus_interface=INTERFACE_NAME, in_signature='', out_signature='ss') def popActivatedAction(self): return self._list._activatedActions.pop(0) @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) # Action act = Gio.SimpleAction.new(self._kargs['actionName'], self._kargs['actionStateType']) act.connect('activate', self._list._onActionActivated) self._list._rootAction.insert(act) 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) if self._kargs['actionName']: # Remove action self._list._rootAction.remove(self._kargs['actionName']) 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._actions_bk = [] self._objectPath = objectPath self._bus = None self._exportMenuID = None self._exportActionID = None self._ownNameID = None self._root = None self._rootAction = None self._activatedActions = [] def appendItem(self, label, actionName, link=None, parentId=None, properties=None, actionStateType=None): self._actions.append(Action(self, 'append', parentId=parentId, label=label, actionName=actionName, link=link, properties=properties, actionStateType=actionStateType)) def removeItem(self, menuId, actionName=None): self._actions.append(Action(self, 'remove', menuId=menuId, actionName=actionName)) def _save(self): self._actions_bk = [] self._actions_bk.extend(self._actions) def _restore(self): if len(self._actions_bk): self._actions = [] self._actions.extend(self._actions_bk) 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._rootAction = Gio.SimpleActionGroup() self._bus = connection self._exportMenuID = connection.export_menu_model(MENU_OBJECT_PATH, self._root) self._exportActionID = connection.export_action_group(MENU_OBJECT_PATH, self._rootAction) def start(self): self._save() self._ownNameID = Gio.bus_own_name(2, MENU_SERVICE_NAME, 0, self._exportService, None, None) def stop(self): if self._exportMenuID: self._bus.unexport_menu_model(self._exportMenuID) self._exportMenuID = None if self._exportActionID: self._bus.unexport_action_group(self._exportActionID) self._exportActionID = None if self._ownNameID: Gio.bus_unown_name(self._ownNameID) self._ownNameID = None self._root = None self._rootAction = None self._restore() def _onActionActivated(self, action, parameter): self._activatedActions.append((action.get_name(), parameter.get_string())) qmenumodel-0.9.1/TODO0000644000000000000000000000060414200511032011231 0ustar = TODO = • Use PIMPLs to clean up the headers • Add a server-side API to allow applications to export their menus on the bus • [optional] Add a ProjectConfig.cmake file to allow other projects using cmake to express a dependency on libqmenumodel without requesting pkgconfig (see http://www.vtk.org/Wiki/CMake/Tutorials/How_to_create_a_ProjectConfig.cmake_file) • … qmenumodel-0.9.1/.travis.yml0000644000000000000000000000170614200511032012656 0ustar # vim: set ts=2 sts=2 sw=2 expandtab : dist: focal language: shell os: linux arch: - amd64 # - ppc64le services: - docker addons: apt: packages: - python3-pip - python3-setuptools before_install: # let's use the MATE project's docker build script... - curl -Ls -o docker-build https://github.com/AyatanaIndicators/ayatana-dev-scripts/raw/main/travis/docker-build - chmod +x docker-build install: - pip3 install wheel - pip3 install PyGithub - ./docker-build --name ${DISTRO} --config .build.yml --install script: - ./docker-build --name ${DISTRO} --verbose --config .build.yml --build scripts env: # temp disable of archlinux builds, see https://gitlab.archlinux.org/archlinux/archlinux-docker/-/issues/56 # - DISTRO="archlinux:latest" - DISTRO="debian:testing" - DISTRO="debian:stable" # - DISTRO="ubuntu:rolling" - DISTRO="ubuntu:focal" jobs: exclude: - env: DISTRO="archlinux:latest" arch: ppc64le