--- /dev/null
+project(dbusmenu-qt)
+cmake_minimum_required(VERSION 2.8.11)
+set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/modules")
+
+# Build options
+option(WITH_DOC "Build documentation (requires Doxygen)" ON)
+
+# Versions
+## Package version
+set(dbusmenu_qt_VERSION_MAJOR 0)
+set(dbusmenu_qt_VERSION_MINOR 9)
+set(dbusmenu_qt_VERSION_PATCH 2)
+set(dbusmenu_qt_VERSION ${dbusmenu_qt_VERSION_MAJOR}.${dbusmenu_qt_VERSION_MINOR}.${dbusmenu_qt_VERSION_PATCH})
+
+## Lib version
+### Bump this one when a binary-incompatible change is introduced
+set(dbusmenu_qt_lib_SOVERSION 2)
+
+### Bump this one when the API is extended in a binary-compatible way
+set(dbusmenu_qt_lib_API_VERSION 6)
+
+### Bump this one when changes do not extend the API
+set(dbusmenu_qt_lib_PATCH_VERSION 0)
+
+set(dbusmenu_qt_lib_VERSION ${dbusmenu_qt_lib_SOVERSION}.${dbusmenu_qt_lib_API_VERSION}.${dbusmenu_qt_lib_PATCH_VERSION})
+
+# Check if we want to explicitly select the Qt version to be used or autodetect
+if (NOT USE_QT4 AND NOT USE_QT5)
+ # Autodetect, prefering Qt5
+ message(STATUS "Autodetecting Qt version to use")
+ find_package(Qt5Widgets QUIET)
+ if (Qt5Widgets_FOUND)
+ set(USE_QT5 TRUE)
+ endif()
+endif()
+
+# Detect for which Qt version we're building
+if (USE_QT5)
+ find_package(Qt5Widgets REQUIRED)
+ find_package(Qt5DBus REQUIRED)
+ include_directories(${Qt5Widgets_INCLUDE_DIRS} ${Qt5DBus_INCLUDE_DIRS})
+ find_package(Qt5Core REQUIRED)
+ set(CMAKE_AUTOMOC ON)
+ set(CMAKE_AUTOMOC_RELAXED_MODE ON)
+ set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
+ set(QT_SUFFIX "qt5")
+else()
+ find_package(Qt4 REQUIRED)
+ include_directories(
+ ${QT_INCLUDE_DIR}
+ ${QT_QTCORE_INCLUDE_DIR}
+ ${QT_QTDBUS_INCLUDE_DIR}
+ ${QT_QTGUI_INCLUDE_DIR}
+ )
+
+ set(QT_SUFFIX "qt")
+endif()
+
+include (CheckCXXCompilerFlag)
+# Check some compiler flags
+check_cxx_compiler_flag(-fvisibility=hidden __DBUSMENU_HAVE_GCC_VISIBILITY)
+if (__DBUSMENU_HAVE_GCC_VISIBILITY AND NOT WIN32)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
+endif (__DBUSMENU_HAVE_GCC_VISIBILITY AND NOT WIN32)
+
+check_cxx_compiler_flag(-Woverloaded-virtual __DBUSMENU_HAVE_W_OVERLOADED_VIRTUAL)
+if (__DBUSMENU_HAVE_W_OVERLOADED_VIRTUAL)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Woverloaded-virtual")
+endif (__DBUSMENU_HAVE_W_OVERLOADED_VIRTUAL)
+
+check_cxx_compiler_flag(-std=c++11 __DBUSMENU_HAVE_CXX11)
+if (__DBUSMENU_HAVE_CXX11)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+endif (__DBUSMENU_HAVE_CXX11)
+
+include(CMakePackageConfigHelpers)
+include(GNUInstallDirs)
+set(LIB_DESTINATION "${CMAKE_INSTALL_LIBDIR}")
+set(CMAKECONFIG_INSTALL_DIR "${LIB_DESTINATION}/cmake/dbusmenu-${QT_SUFFIX}")
+set(INCLUDE_INSTALL_DIR "include/dbusmenu-${QT_SUFFIX}")
+
+# dist targets
+set(ARCHIVE_NAME libdbusmenu-${QT_SUFFIX}-${dbusmenu_qt_VERSION})
+add_custom_target(dist
+ COMMAND bzr export --root=${ARCHIVE_NAME} ${CMAKE_BINARY_DIR}/${ARCHIVE_NAME}.tar.bz2
+ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+ )
+
+add_custom_target(distcheck
+ COMMAND cd ${CMAKE_BINARY_DIR}
+ && rm -rf ${ARCHIVE_NAME}
+ && tar xf ${ARCHIVE_NAME}.tar.bz2
+ && mkdir ${ARCHIVE_NAME}/build
+ && cd ${ARCHIVE_NAME}/build
+ && cmake -DCMAKE_INSTALL_PREFIX=../install ..
+ && make
+ && make install
+ && make check
+ )
+add_dependencies(distcheck dist)
+
+configure_file(dbusmenu-qt.pc.in ${CMAKE_BINARY_DIR}/dbusmenu-${QT_SUFFIX}.pc @ONLY)
+
+install(FILES ${CMAKE_BINARY_DIR}/dbusmenu-${QT_SUFFIX}.pc
+ DESTINATION ${LIB_DESTINATION}/pkgconfig
+ )
+
+add_subdirectory(src)
+add_subdirectory(tests)
+add_subdirectory(tools)
+
+if(WITH_DOC)
+ configure_file(Doxyfile.in ${CMAKE_BINARY_DIR}/Doxyfile @ONLY)
+
+ add_custom_target(doc ALL doxygen
+ WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
+ )
+
+ install(DIRECTORY ${CMAKE_BINARY_DIR}/html/
+ DESTINATION share/doc/libdbusmenu-${QT_SUFFIX}-doc
+ )
+endif(WITH_DOC)
+
+# Generate dbusmenu-qt-config* files
+configure_package_config_file(
+ dbusmenu-qt-config.cmake.in
+ ${CMAKE_BINARY_DIR}/dbusmenu-${QT_SUFFIX}-config.cmake
+ INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}
+ PATH_VARS INCLUDE_INSTALL_DIR
+ )
+
+write_basic_package_version_file(
+ ${CMAKE_BINARY_DIR}/dbusmenu-${QT_SUFFIX}-config-version.cmake
+ VERSION ${dbusmenu_qt_VERSION}
+ COMPATIBILITY SameMajorVersion
+ )
+
+# Install dbusmenu-qt-config* files
+install(FILES
+ ${CMAKE_BINARY_DIR}/dbusmenu-${QT_SUFFIX}-config.cmake
+ ${CMAKE_BINARY_DIR}/dbusmenu-${QT_SUFFIX}-config-version.cmake
+ DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
+ COMPONENT Devel
+ )
--- /dev/null
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1991 Free Software Foundation, Inc.
+ 59 Temple Place - Suite 330
+ Boston, MA 02111-1307, USA.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+[This is the first released version of the library GPL. It is
+ numbered 2 because it goes with version 2 of the ordinary GPL.]
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+Licenses are intended to guarantee your freedom to share and change
+free software--to make sure the software is free for all its users.
+
+ This license, the Library General Public License, applies to some
+specially designated Free Software Foundation software, and to any
+other libraries whose authors decide to use it. You can use it for
+your libraries, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if
+you distribute copies of the library, or if you modify it.
+
+ For example, if you distribute copies of the library, whether gratis
+or for a fee, you must give the recipients all the rights that we gave
+you. You must make sure that they, too, receive or can get the source
+code. If you link a program with the library, you must provide
+complete object files to the recipients so that they can relink them
+with the library, after making changes to the library and recompiling
+it. And you must show them these terms so they know their rights.
+
+ Our method of protecting your rights has two steps: (1) copyright
+the library, and (2) offer you this license which gives you legal
+permission to copy, distribute and/or modify the library.
+
+ Also, for each distributor's protection, we want to make certain
+that everyone understands that there is no warranty for this free
+library. If the library is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original
+version, so that any problems introduced by others will not reflect on
+the original authors' reputations.
+\f
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that companies distributing free
+software will individually obtain patent licenses, thus in effect
+transforming the program into proprietary software. To prevent this,
+we have made it clear that any patent must be licensed for everyone's
+free use or not licensed at all.
+
+ Most GNU software, including some libraries, is covered by the ordinary
+GNU General Public License, which was designed for utility programs. This
+license, the GNU Library General Public License, applies to certain
+designated libraries. This license is quite different from the ordinary
+one; be sure to read it in full, and don't assume that anything in it is
+the same as in the ordinary license.
+
+ The reason we have a separate public license for some libraries is that
+they blur the distinction we usually make between modifying or adding to a
+program and simply using it. Linking a program with a library, without
+changing the library, is in some sense simply using the library, and is
+analogous to running a utility program or application program. However, in
+a textual and legal sense, the linked executable is a combined work, a
+derivative of the original library, and the ordinary General Public License
+treats it as such.
+
+ Because of this blurred distinction, using the ordinary General
+Public License for libraries did not effectively promote software
+sharing, because most developers did not use the libraries. We
+concluded that weaker conditions might promote sharing better.
+
+ However, unrestricted linking of non-free programs would deprive the
+users of those programs of all benefit from the free status of the
+libraries themselves. This Library General Public License is intended to
+permit developers of non-free programs to use free libraries, while
+preserving your freedom as a user of such programs to change the free
+libraries that are incorporated in them. (We have not seen how to achieve
+this as regards changes in header files, but we have achieved it as regards
+changes in the actual functions of the Library.) The hope is that this
+will lead to faster development of free libraries.
+
+ The precise terms and conditions for copying, distribution and
+modification follow. Pay close attention to the difference between a
+"work based on the library" and a "work that uses the library". The
+former contains code derived from the library, while the latter only
+works together with the library.
+
+ Note that it is possible for a library to be covered by the ordinary
+General Public License rather than by this special one.
+\f
+ GNU LIBRARY GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License Agreement applies to any software library which
+contains a notice placed by the copyright holder or other authorized
+party saying it may be distributed under the terms of this Library
+General Public License (also called "this License"). Each licensee is
+addressed as "you".
+
+ A "library" means a collection of software functions and/or data
+prepared so as to be conveniently linked with application programs
+(which use some of those functions and data) to form executables.
+
+ The "Library", below, refers to any such software library or work
+which has been distributed under these terms. A "work based on the
+Library" means either the Library or any derivative work under
+copyright law: that is to say, a work containing the Library or a
+portion of it, either verbatim or with modifications and/or translated
+straightforwardly into another language. (Hereinafter, translation is
+included without limitation in the term "modification".)
+
+ "Source code" for a work means the preferred form of the work for
+making modifications to it. For a library, complete source code means
+all the source code for all modules it contains, plus any associated
+interface definition files, plus the scripts used to control compilation
+and installation of the library.
+
+ Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running a program using the Library is not restricted, and output from
+such a program is covered only if its contents constitute a work based
+on the Library (independent of the use of the Library in a tool for
+writing it). Whether that is true depends on what the Library does
+and what the program that uses the Library does.
+
+ 1. You may copy and distribute verbatim copies of the Library's
+complete source code as you receive it, in any medium, provided that
+you conspicuously and appropriately publish on each copy an
+appropriate copyright notice and disclaimer of warranty; keep intact
+all the notices that refer to this License and to the absence of any
+warranty; and distribute a copy of this License along with the
+Library.
+
+ You may charge a fee for the physical act of transferring a copy,
+and you may at your option offer warranty protection in exchange for a
+fee.
+\f
+ 2. You may modify your copy or copies of the Library or any portion
+of it, thus forming a work based on the Library, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) The modified work must itself be a software library.
+
+ b) You must cause the files modified to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ c) You must cause the whole of the work to be licensed at no
+ charge to all third parties under the terms of this License.
+
+ d) If a facility in the modified Library refers to a function or a
+ table of data to be supplied by an application program that uses
+ the facility, other than as an argument passed when the facility
+ is invoked, then you must make a good faith effort to ensure that,
+ in the event an application does not supply such function or
+ table, the facility still operates, and performs whatever part of
+ its purpose remains meaningful.
+
+ (For example, a function in a library to compute square roots has
+ a purpose that is entirely well-defined independent of the
+ application. Therefore, Subsection 2d requires that any
+ application-supplied function or table used by this function must
+ be optional: if the application does not supply it, the square
+ root function must still compute square roots.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Library,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Library, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote
+it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Library.
+
+In addition, mere aggregation of another work not based on the Library
+with the Library (or with a work based on the Library) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may opt to apply the terms of the ordinary GNU General Public
+License instead of this License to a given copy of the Library. To do
+this, you must alter all the notices that refer to this License, so
+that they refer to the ordinary GNU General Public License, version 2,
+instead of to this License. (If a newer version than version 2 of the
+ordinary GNU General Public License has appeared, then you can specify
+that version instead if you wish.) Do not make any other change in
+these notices.
+\f
+ Once this change is made in a given copy, it is irreversible for
+that copy, so the ordinary GNU General Public License applies to all
+subsequent copies and derivative works made from that copy.
+
+ This option is useful when you wish to copy part of the code of
+the Library into a program that is not a library.
+
+ 4. You may copy and distribute the Library (or a portion or
+derivative of it, under Section 2) in object code or executable form
+under the terms of Sections 1 and 2 above provided that you accompany
+it with the complete corresponding machine-readable source code, which
+must be distributed under the terms of Sections 1 and 2 above on a
+medium customarily used for software interchange.
+
+ If distribution of object code is made by offering access to copy
+from a designated place, then offering equivalent access to copy the
+source code from the same place satisfies the requirement to
+distribute the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 5. A program that contains no derivative of any portion of the
+Library, but is designed to work with the Library by being compiled or
+linked with it, is called a "work that uses the Library". Such a
+work, in isolation, is not a derivative work of the Library, and
+therefore falls outside the scope of this License.
+
+ However, linking a "work that uses the Library" with the Library
+creates an executable that is a derivative of the Library (because it
+contains portions of the Library), rather than a "work that uses the
+library". The executable is therefore covered by this License.
+Section 6 states terms for distribution of such executables.
+
+ When a "work that uses the Library" uses material from a header file
+that is part of the Library, the object code for the work may be a
+derivative work of the Library even though the source code is not.
+Whether this is true is especially significant if the work can be
+linked without the Library, or if the work is itself a library. The
+threshold for this to be true is not precisely defined by law.
+
+ If such an object file uses only numerical parameters, data
+structure layouts and accessors, and small macros and small inline
+functions (ten lines or less in length), then the use of the object
+file is unrestricted, regardless of whether it is legally a derivative
+work. (Executables containing this object code plus portions of the
+Library will still fall under Section 6.)
+
+ Otherwise, if the work is a derivative of the Library, you may
+distribute the object code for the work under the terms of Section 6.
+Any executables containing that work also fall under Section 6,
+whether or not they are linked directly with the Library itself.
+\f
+ 6. As an exception to the Sections above, you may also compile or
+link a "work that uses the Library" with the Library to produce a
+work containing portions of the Library, and distribute that work
+under terms of your choice, provided that the terms permit
+modification of the work for the customer's own use and reverse
+engineering for debugging such modifications.
+
+ You must give prominent notice with each copy of the work that the
+Library is used in it and that the Library and its use are covered by
+this License. You must supply a copy of this License. If the work
+during execution displays copyright notices, you must include the
+copyright notice for the Library among them, as well as a reference
+directing the user to the copy of this License. Also, you must do one
+of these things:
+
+ a) Accompany the work with the complete corresponding
+ machine-readable source code for the Library including whatever
+ changes were used in the work (which must be distributed under
+ Sections 1 and 2 above); and, if the work is an executable linked
+ with the Library, with the complete machine-readable "work that
+ uses the Library", as object code and/or source code, so that the
+ user can modify the Library and then relink to produce a modified
+ executable containing the modified Library. (It is understood
+ that the user who changes the contents of definitions files in the
+ Library will not necessarily be able to recompile the application
+ to use the modified definitions.)
+
+ b) Accompany the work with a written offer, valid for at
+ least three years, to give the same user the materials
+ specified in Subsection 6a, above, for a charge no more
+ than the cost of performing this distribution.
+
+ c) If distribution of the work is made by offering access to copy
+ from a designated place, offer equivalent access to copy the above
+ specified materials from the same place.
+
+ d) Verify that the user has already received a copy of these
+ materials or that you have already sent this user a copy.
+
+ For an executable, the required form of the "work that uses the
+Library" must include any data and utility programs needed for
+reproducing the executable from it. However, as a special exception,
+the source code distributed need not include anything that is normally
+distributed (in either source or binary form) with the major
+components (compiler, kernel, and so on) of the operating system on
+which the executable runs, unless that component itself accompanies
+the executable.
+
+ It may happen that this requirement contradicts the license
+restrictions of other proprietary libraries that do not normally
+accompany the operating system. Such a contradiction means you cannot
+use both them and the Library together in an executable that you
+distribute.
+\f
+ 7. 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 not covered by this License, and distribute such a combined
+library, provided that the separate distribution of the work based on
+the Library and of the other library facilities is otherwise
+permitted, and provided that you do these two things:
+
+ a) Accompany the combined library with a copy of the same work
+ based on the Library, uncombined with any other library
+ facilities. This must be distributed under the terms of the
+ Sections above.
+
+ b) Give prominent notice with the combined library of the fact
+ that part of it is a work based on the Library, and explaining
+ where to find the accompanying uncombined form of the same work.
+
+ 8. You may not copy, modify, sublicense, link with, or distribute
+the Library except as expressly provided under this License. Any
+attempt otherwise to copy, modify, sublicense, link with, or
+distribute the Library is void, and will automatically terminate your
+rights under this License. However, parties who have received copies,
+or rights, from you under this License will not have their licenses
+terminated so long as such parties remain in full compliance.
+
+ 9. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Library or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Library (or any work based on the
+Library), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Library or works based on it.
+
+ 10. Each time you redistribute the Library (or any work based on the
+Library), the recipient automatically receives a license from the
+original licensor to copy, distribute, link with or modify the Library
+subject to these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+\f
+ 11. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Library at all. For example, if a patent
+license would not permit royalty-free redistribution of the Library by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Library.
+
+If any portion of this section is held invalid or unenforceable under any
+particular circumstance, the balance of the section is intended to apply,
+and the section as a whole is intended to apply in other circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 12. If the distribution and/or use of the Library is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Library under this License may add
+an explicit geographical distribution limitation excluding those countries,
+so that distribution is permitted only in or among countries not thus
+excluded. In such case, this License incorporates the limitation as if
+written in the body of this License.
+
+ 13. The Free Software Foundation may publish revised and/or new
+versions of the Library 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
+specifies a version number of this License which applies to it and
+"any later version", you have the option of following the terms and
+conditions either of that version or of any later version published by
+the Free Software Foundation. If the Library does not specify a
+license version number, you may choose any version ever published by
+the Free Software Foundation.
+\f
+ 14. If you wish to incorporate parts of the Library into other free
+programs whose distribution conditions are incompatible with these,
+write to the author to ask for permission. For software which is
+copyrighted by the Free Software Foundation, write to the Free
+Software Foundation; we sometimes make exceptions for this. Our
+decision will be guided by the two goals of preserving the free status
+of all derivatives of our free software and of promoting the sharing
+and reuse of software generally.
+
+ NO WARRANTY
+
+ 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO
+WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW.
+EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR
+OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY
+KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE
+LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME
+THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+ 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN
+WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY
+AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU
+FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR
+CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE
+LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING
+RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A
+FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF
+SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+ How to Apply These Terms to Your New Libraries
+
+ If you develop a new library, and you want it to be of the greatest
+possible use to the public, we recommend making it free software that
+everyone can redistribute and change. You can do so by permitting
+redistribution under these terms (or, alternatively, under the terms of the
+ordinary General Public License).
+
+ To apply these terms, attach the following notices to the library. It is
+safest to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least the
+"copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the library's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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 License, 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
+
+Also add information on how to contact you by electronic and paper mail.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the library, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the
+ library `Frob' (a library for tweaking knobs) written by James Random Hacker.
+
+ <signature of Ty Coon>, 1 April 1990
+ Ty Coon, President of Vice
+
+That's all there is to it!
--- /dev/null
+# Doxyfile 1.7.3
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+DOXYFILE_ENCODING = UTF-8
+PROJECT_NAME = dbusmenu-qt
+PROJECT_NUMBER = @dbusmenu_qt_VERSION@
+PROJECT_BRIEF =
+PROJECT_LOGO =
+OUTPUT_DIRECTORY =
+CREATE_SUBDIRS = NO
+OUTPUT_LANGUAGE = English
+BRIEF_MEMBER_DESC = YES
+REPEAT_BRIEF = YES
+ABBREVIATE_BRIEF =
+ALWAYS_DETAILED_SEC = NO
+INLINE_INHERITED_MEMB = NO
+FULL_PATH_NAMES = YES
+STRIP_FROM_PATH =
+STRIP_FROM_INC_PATH =
+SHORT_NAMES = NO
+JAVADOC_AUTOBRIEF = NO
+QT_AUTOBRIEF = NO
+MULTILINE_CPP_IS_BRIEF = NO
+INHERIT_DOCS = YES
+SEPARATE_MEMBER_PAGES = NO
+TAB_SIZE = 4
+ALIASES =
+OPTIMIZE_OUTPUT_FOR_C = NO
+OPTIMIZE_OUTPUT_JAVA = NO
+OPTIMIZE_FOR_FORTRAN = NO
+OPTIMIZE_OUTPUT_VHDL = NO
+EXTENSION_MAPPING =
+BUILTIN_STL_SUPPORT = NO
+CPP_CLI_SUPPORT = NO
+SIP_SUPPORT = NO
+IDL_PROPERTY_SUPPORT = YES
+DISTRIBUTE_GROUP_DOC = NO
+SUBGROUPING = YES
+TYPEDEF_HIDES_STRUCT = NO
+SYMBOL_CACHE_SIZE = 0
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+EXTRACT_ALL = NO
+EXTRACT_PRIVATE = NO
+EXTRACT_STATIC = NO
+EXTRACT_LOCAL_CLASSES = YES
+EXTRACT_LOCAL_METHODS = NO
+EXTRACT_ANON_NSPACES = NO
+HIDE_UNDOC_MEMBERS = NO
+HIDE_UNDOC_CLASSES = NO
+HIDE_FRIEND_COMPOUNDS = NO
+HIDE_IN_BODY_DOCS = NO
+INTERNAL_DOCS = NO
+CASE_SENSE_NAMES = YES
+HIDE_SCOPE_NAMES = NO
+SHOW_INCLUDE_FILES = YES
+FORCE_LOCAL_INCLUDES = NO
+INLINE_INFO = YES
+SORT_MEMBER_DOCS = YES
+SORT_BRIEF_DOCS = NO
+SORT_MEMBERS_CTORS_1ST = NO
+SORT_GROUP_NAMES = NO
+SORT_BY_SCOPE_NAME = NO
+STRICT_PROTO_MATCHING = NO
+GENERATE_TODOLIST = YES
+GENERATE_TESTLIST = YES
+GENERATE_BUGLIST = YES
+GENERATE_DEPRECATEDLIST= YES
+ENABLED_SECTIONS =
+MAX_INITIALIZER_LINES = 30
+SHOW_USED_FILES = YES
+SHOW_DIRECTORIES = NO
+SHOW_FILES = YES
+SHOW_NAMESPACES = YES
+FILE_VERSION_FILTER =
+LAYOUT_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+QUIET = YES
+WARNINGS = YES
+WARN_IF_UNDOCUMENTED = YES
+WARN_IF_DOC_ERROR = YES
+WARN_NO_PARAMDOC = NO
+WARN_FORMAT = "$file:$line: $text"
+WARN_LOGFILE =
+#---------------------------------------------------------------------------
+# configuration options related to the input files
+#---------------------------------------------------------------------------
+INPUT = @CMAKE_SOURCE_DIR@/src
+INPUT_ENCODING = UTF-8
+FILE_PATTERNS = *.h
+RECURSIVE = NO
+EXCLUDE =
+EXCLUDE_SYMLINKS = NO
+EXCLUDE_PATTERNS = *_p.*
+EXCLUDE_SYMBOLS =
+EXAMPLE_PATH =
+EXAMPLE_PATTERNS =
+EXAMPLE_RECURSIVE = NO
+IMAGE_PATH =
+INPUT_FILTER =
+FILTER_PATTERNS =
+FILTER_SOURCE_FILES = NO
+FILTER_SOURCE_PATTERNS =
+#---------------------------------------------------------------------------
+# configuration options related to source browsing
+#---------------------------------------------------------------------------
+SOURCE_BROWSER = NO
+INLINE_SOURCES = NO
+STRIP_CODE_COMMENTS = YES
+REFERENCED_BY_RELATION = NO
+REFERENCES_RELATION = NO
+REFERENCES_LINK_SOURCE = YES
+USE_HTAGS = NO
+VERBATIM_HEADERS = YES
+#---------------------------------------------------------------------------
+# configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+ALPHABETICAL_INDEX = YES
+COLS_IN_ALPHA_INDEX = 5
+IGNORE_PREFIX =
+#---------------------------------------------------------------------------
+# configuration options related to the HTML output
+#---------------------------------------------------------------------------
+GENERATE_HTML = YES
+HTML_OUTPUT = html
+HTML_FILE_EXTENSION = .html
+HTML_HEADER =
+HTML_FOOTER =
+HTML_STYLESHEET =
+HTML_COLORSTYLE_HUE = 220
+HTML_COLORSTYLE_SAT = 100
+HTML_COLORSTYLE_GAMMA = 80
+HTML_TIMESTAMP = YES
+HTML_ALIGN_MEMBERS = YES
+HTML_DYNAMIC_SECTIONS = NO
+GENERATE_DOCSET = NO
+DOCSET_FEEDNAME = "Doxygen generated docs"
+DOCSET_BUNDLE_ID = org.doxygen.Project
+DOCSET_PUBLISHER_ID = org.doxygen.Publisher
+DOCSET_PUBLISHER_NAME = Publisher
+GENERATE_HTMLHELP = NO
+CHM_FILE =
+HHC_LOCATION =
+GENERATE_CHI = NO
+CHM_INDEX_ENCODING =
+BINARY_TOC = NO
+TOC_EXPAND = NO
+GENERATE_QHP = NO
+QCH_FILE =
+QHP_NAMESPACE = org.doxygen.Project
+QHP_VIRTUAL_FOLDER = doc
+QHP_CUST_FILTER_NAME =
+QHP_CUST_FILTER_ATTRS =
+QHP_SECT_FILTER_ATTRS =
+QHG_LOCATION =
+GENERATE_ECLIPSEHELP = NO
+ECLIPSE_DOC_ID = org.doxygen.Project
+DISABLE_INDEX = NO
+ENUM_VALUES_PER_LINE = 4
+GENERATE_TREEVIEW = NO
+USE_INLINE_TREES = NO
+TREEVIEW_WIDTH = 250
+EXT_LINKS_IN_WINDOW = NO
+FORMULA_FONTSIZE = 10
+FORMULA_TRANSPARENT = YES
+USE_MATHJAX = NO
+MATHJAX_RELPATH = http://www.mathjax.org/mathjax
+SEARCHENGINE = YES
+SERVER_BASED_SEARCH = NO
+#---------------------------------------------------------------------------
+# configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+GENERATE_LATEX = NO
+LATEX_OUTPUT = latex
+LATEX_CMD_NAME = latex
+MAKEINDEX_CMD_NAME = makeindex
+COMPACT_LATEX = NO
+PAPER_TYPE = a4
+EXTRA_PACKAGES =
+LATEX_HEADER =
+PDF_HYPERLINKS = YES
+USE_PDFLATEX = YES
+LATEX_BATCHMODE = NO
+LATEX_HIDE_INDICES = NO
+LATEX_SOURCE_CODE = NO
+#---------------------------------------------------------------------------
+# configuration options related to the RTF output
+#---------------------------------------------------------------------------
+GENERATE_RTF = NO
+RTF_OUTPUT = rtf
+COMPACT_RTF = NO
+RTF_HYPERLINKS = NO
+RTF_STYLESHEET_FILE =
+RTF_EXTENSIONS_FILE =
+#---------------------------------------------------------------------------
+# configuration options related to the man page output
+#---------------------------------------------------------------------------
+GENERATE_MAN = NO
+MAN_OUTPUT = man
+MAN_EXTENSION = .3
+MAN_LINKS = NO
+#---------------------------------------------------------------------------
+# configuration options related to the XML output
+#---------------------------------------------------------------------------
+GENERATE_XML = NO
+XML_OUTPUT = xml
+XML_SCHEMA =
+XML_DTD =
+XML_PROGRAMLISTING = YES
+#---------------------------------------------------------------------------
+# configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+GENERATE_AUTOGEN_DEF = NO
+#---------------------------------------------------------------------------
+# configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+GENERATE_PERLMOD = NO
+PERLMOD_LATEX = NO
+PERLMOD_PRETTY = YES
+PERLMOD_MAKEVAR_PREFIX =
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+ENABLE_PREPROCESSING = YES
+MACRO_EXPANSION = NO
+EXPAND_ONLY_PREDEF = NO
+SEARCH_INCLUDES = YES
+INCLUDE_PATH =
+INCLUDE_FILE_PATTERNS =
+PREDEFINED =
+EXPAND_AS_DEFINED =
+SKIP_FUNCTION_MACROS = YES
+#---------------------------------------------------------------------------
+# Configuration::additions related to external references
+#---------------------------------------------------------------------------
+TAGFILES =
+GENERATE_TAGFILE =
+ALLEXTERNALS = NO
+EXTERNAL_GROUPS = YES
+PERL_PATH = /usr/bin/perl
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+CLASS_DIAGRAMS = YES
+MSCGEN_PATH =
+HIDE_UNDOC_RELATIONS = YES
+HAVE_DOT = NO
+DOT_NUM_THREADS = 0
+DOT_FONTNAME = Helvetica
+DOT_FONTSIZE = 10
+DOT_FONTPATH =
+CLASS_GRAPH = YES
+COLLABORATION_GRAPH = YES
+GROUP_GRAPHS = YES
+UML_LOOK = NO
+TEMPLATE_RELATIONS = NO
+INCLUDE_GRAPH = YES
+INCLUDED_BY_GRAPH = YES
+CALL_GRAPH = NO
+CALLER_GRAPH = NO
+GRAPHICAL_HIERARCHY = YES
+DIRECTORY_GRAPH = YES
+DOT_IMAGE_FORMAT = png
+DOT_PATH =
+DOTFILE_DIRS =
+MSCFILE_DIRS =
+DOT_GRAPH_MAX_NODES = 50
+MAX_DOT_GRAPH_DEPTH = 0
+DOT_TRANSPARENT = NO
+DOT_MULTI_TARGETS = YES
+GENERATE_LEGEND = YES
+DOT_CLEANUP = YES
--- /dev/null
+# 0.9.2 - 2012.03.29
+- Fix disabling and hiding actions (Aurelien Gateau)
+- Avoid spamming dbus at startup (Aurelien Gateau)
+- Do not print warnings when not necessary (Aurelien Gateau)
+
+# 0.9.1 - 2012.03.26
+- Add support for "opened" and "closed" events (Aurelien Gateau)
+- Add support for icon-data (LP BUG 633339) (Christoph Spielmann)
+
+# 0.9.0 - 2011.08.30
+- Add support for the "Status" dbusmenu property. Will be used by appmenu-qt for LP BUG 737419 (Aurelien Gateau)
+- Collapse multiple separators, get rid of starting and trailing separators (LP BUG 793339) (Aurelien Gateau)
+
+# 0.8.3 - 2011.06.21
+- If DBusMenuExporter is deleted, delete all DBusMenu instances which were working with it (Aurelien Gateau)
+- Only show icons in menu if the platform allows them (Michael Terry)
+
+# 0.8.2 - 2011.04.12
+- Shortcut handling: Translate "+" into "plus" and "-" into "minus" (LP BUG 712565) (Aurelien Gateau)
+
+# 0.8.1 - 2011.03.24
+- Added target to build documentation with Doxygen (Aurelien Gateau)
+
+# 0.8.0 - 2011.02.24
+- Implements version 2 of the dbusmenu protocol (Aurelien Gateau)
+- Merged support for KMenu titles (Aurelien Gateau)
+
+# 0.7.0 - 2011.13.01
+- Switched DBus domain from org.ayatana to com.canonical (Aurelien Gateau)
+
+# 0.6.6 - 2010.12.08
+- Properly increase version numbers (Aurelien Gateau)
+
+# 0.6.5 - 2010.12.07
+- Avoid false warnings (Aurelien Gateau)
+- Make sure we don't track actions twice (KDE BUG 254066) (Aurelien Gateau)
+- CMake-parser-friendly of dbusmenu_version.h (Aurelien Gateau)
+
+# 0.6.4 - 2010.09.23
+- Trigger action asynchronously when the "clicked" event is received (LP BUG 643393) (Aurelien Gateau)
+- Fixed copyright headers (Aurelien Gateau)
+
+# 0.6.3 - 2010.09.16
+- Moved to LP (Aurelien Gateau)
+- Removed all code which did not belong to Canonical. Hopefully we get this
+ code back in soon (Aurelien Gateau)
+
+# 0.6.2 - 2010.09.09
+- Fix some memory leaks (Aurelien Gateau)
+- Do not keep dangling pointers to deleted actions (LP BUG 624964) (Aurelien Gateau)
+- Updated documentation of iconNameForAction() (Aurelien Gateau)
+
+# 0.6.1 - 2010.09.02
+- Fix some memory leaks (Aurelien Gateau)
+
+# 0.6.0 - 2010.08.19
+- Added the DBusMenuImporter::actionActivationRequested(QAction*) signal (Aurelien Gateau)
+- Fix hardcoded libdir in pkgconfig file (LP BUG 610633) (oneforall)
+
+# 0.5.2 - 2010.08.05
+- Fix implementation of GetGroupProperties() (Aurelien Gateau)
+- Fix detection of QIcon::name() with gold (Aurelien Gateau)
+
+# 0.5.1 - 2010.07.01
+- Add support for KMenu titles (Christoph Feck)
+
+# 0.5.0 - 2010.06.28
+- Queue calls to refresh() because we may be spammed with many LayoutUpdated()
+ signals at once (Aurelien Gateau)
+- Turned DBusMenuImporter::updateMenu() into a slot (Aurelien Gateau)
+
+# 0.4.0 - 2010.06.24
+- Introduce a dbusmenu_version.h file (Aurelien Gateau)
+- Introduce updateMenu() and menuUpdated(), deprecate menuReadyToBeShown() (Aurelien Gateau)
+- Better build check for QIcon::name() (LP BUG 597975) (Aurelien Gateau)
+
+# 0.3.5 - 2010.06.17
+- Rework the way menuReadyToBeShown() is emitted (Aurelien Gateau)
+- Queue LayoutUpdated() signal to avoid emitting it too often (Aurelien Gateau)
+- Increase timeouts: prefer slow but complete menus to fast but incomplete (Aurelien Gateau)
+- Use QIcon::name() to return the icon name, when built with Qt 4.7 (Aurelien Gateau)
+- Correctly handle non-exclusive action groups (Aurelien Gateau)
+
+# 0.3.4 - 2010.06.10
+- Fixed KDE bug #237156 (Aurelien Gateau)
+- Added support for shortcuts (Aurelien Gateau)
+- Make the connection to LayoutUpdated() work :/ (Aurelien Gateau)
+
+# 0.3.3 - 2010.05.19
+- Introduce a qt minimum version. Qt 4.5 doesn't work. (Michael Jansen)
+- Use the FindQjson.cmake file made by pinotree for chokoq because it works.
+ The old one didn't (for me). (Michael Jansen)
+- Refresh after LayoutUpdated signal (Marco Martin)
+- Test items added to an existing menu are properly imported (Aurelien Gateau)
+- Allow notification of the menu being filled, don't call aboutToShow more than
+ once per actual menu showing (Aaron Seigo)
+- Win32 fixes from Ralf Habacker (Patrick Spendrin)
+- Added option to disable tests (Alex Elsayed)
+
+# 0.3.2 - 2010.04.02
+- Fix some weird positioning of menus and submenus.
+- Handle ItemPropertyUpdated() signal.
+- Correctly handle properties which are not part of the returned property map
+ because they are set to their default value.
+- Export "visible" property of action.
+
+# 0.3.1 - 2010.04.01
+- Updated to the latest protocol change: 0 is no longer the default value of
+ the "toggle-state" property.
+- Make it build without QJson again.
+
+# 0.3.0 - 2010.03.09
+- Moved the DBus side of DBusMenuExporter to a separate class, hiding it from
+ the outside world.
+- Cleaned the API of DBusMenuExporter and DBusMenuImporter.
+- Implemented AboutToShow method from the DBusMenu spec.
+
+# 0.2.2 - 2010.02.17
+- Fixed crash if action is removed from menu after exporter is deleted
+ (LP BUG 521011).
+- Introduced a Qt equivalent of the test application used by dbusmenu-bench in
+ libdbusmenu-glib.
+- Added distcheck target.
+
+# 0.2.1 - 2010.02.04
+- Export KDE titles as disabled standard items.
+- Added temporary workaround to get more dynamic menus to work on GNOME.
+
+# 0.2.0 - 2010.02.03
+- Make it possible to define the object-path used by DBusMenuExporter.
+- Unit tests.
+- Follow new DBusMenu spec.
+
+# 0.1.0 - 2010.01.05
+- First release.
--- /dev/null
+# Summary
+
+This library provides a Qt implementation of the DBusMenu protocol.
+
+The DBusMenu protocol makes it possible for applications to export and import
+their menus over DBus.
+
+# Author
+
+Canonical DX Team
+Maintainer: Renato Filho <renato.filho@canonical.com>
+Former maintainer: Aurélien Gâteau
+
+# Documentation
+
+By default documentation is generated with Doxygen. You can disable
+documentation generation by passing -DWITH_DOC=OFF to cmake.
+
+# Links
+
+## Source code, bugtracker and tarball hosts
+
+https://launchpad.net/libdbusmenu-qt
+
+## KDE developers mirror
+
+http://gitorious.org/dbusmenu/dbusmenu-qt
+
+## Spec
+
+http://people.canonical.com/~agateau/dbusmenu/spec/index.html
--- /dev/null
+- Verify copy is clean and up to date
+ bzr st
+ bzr pull
+- Update NEWS
+ r!bzr log --line -r tag:x.y.z-1..
+- Bump version number in CMakeLists.txt
+- Bump library version number in CMakeLists.txt
+- Commit
+- Create tarball
+- Unpack tarball, build and run tests
+- Test with KDE trunk
+- If ok, create tag
+ tag=x.y.z
+ bzr tag $tag
+- Push
+ bzr push
+- Upload tarball
+ lp-project-upload libdbusmenu-qt $tag libdbusmenu-qt-$tag.tar.bz2
--- /dev/null
+# Find QJSON - JSON handling library for Qt
+#
+# This module defines
+# QJSON_FOUND - whether the qsjon library was found
+# QJSON_LIBRARIES - the qjson library
+# QJSON_INCLUDE_DIR - the include path of the qjson library
+#
+
+if (QJSON_INCLUDE_DIR AND QJSON_LIBRARIES)
+
+ # Already in cache
+ set (QJSON_FOUND TRUE)
+
+else (QJSON_INCLUDE_DIR AND QJSON_LIBRARIES)
+
+ if (NOT WIN32)
+ # use pkg-config to get the values of QJSON_INCLUDE_DIRS
+ # and QJSON_LIBRARY_DIRS to add as hints to the find commands.
+ include (FindPkgConfig)
+ pkg_check_modules (PC_QJSON QJson>=0.5)
+ endif (NOT WIN32)
+
+ find_library (QJSON_LIBRARIES
+ NAMES
+ qjson
+ PATHS
+ ${PC_QJSON_LIBRARY_DIRS}
+ ${LIB_INSTALL_DIR}
+ ${KDE4_LIB_DIR}
+ )
+
+ find_path (QJSON_INCLUDE_DIR
+ NAMES
+ qjson/parser.h
+ PATHS
+ ${PC_QJSON_INCLUDE_DIRS}
+ ${INCLUDE_INSTALL_DIR}
+ ${KDE4_INCLUDE_DIR}
+ )
+
+ include(FindPackageHandleStandardArgs)
+ find_package_handle_standard_args(QJSON DEFAULT_MSG QJSON_LIBRARIES QJSON_INCLUDE_DIR)
+
+endif (QJSON_INCLUDE_DIR AND QJSON_LIBRARIES)
--- /dev/null
+@PACKAGE_INIT@
+
+include("${CMAKE_CURRENT_LIST_DIR}/dbusmenu-@QT_SUFFIX@-targets.cmake")
+
+set_and_check(dbusmenu-@QT_SUFFIX@_INCLUDE_DIRS "@PACKAGE_INCLUDE_INSTALL_DIR@")
--- /dev/null
+prefix=@CMAKE_INSTALL_PREFIX@
+exec_prefix=@CMAKE_INSTALL_PREFIX@
+libdir=@CMAKE_INSTALL_PREFIX@/lib
+includedir=@CMAKE_INSTALL_PREFIX@/include/dbusmenu-@QT_SUFFIX@
+
+Name: libdbusmenu-@QT_SUFFIX@
+Description: Qt implementation of dbusmenu spec
+Version: @dbusmenu_qt_VERSION@
+Libs: -L${libdir} -ldbusmenu-@QT_SUFFIX@
+Cflags: -I${includedir}
--- /dev/null
+libdbusmenu-qt (0.9.3+16.04.20160218-0ubuntu1) xenial; urgency=medium
+
+ [ Nick Dedekind ]
+ * Ported tests to Qt5.
+
+ -- Pete Woods <ci-train-bot@canonical.com> Thu, 18 Feb 2016 10:14:02 +0000
+
+libdbusmenu-qt (0.9.3+15.10.20150604-0ubuntu1) wily; urgency=medium
+
+ [ David Edmundson ]
+ * fix leaks
+ * fix leaks
+
+ -- CI Train Bot <ci-train-bot@canonical.com> Thu, 04 Jun 2015 23:13:02 +0000
+
+libdbusmenu-qt (0.9.3+14.10.20140619-0ubuntu1) utopic; urgency=low
+
+ [ Aurélien Gâteau ]
+ * Fix build with Clang
+ * With this change, users of dbusmenu-qt no longer need to call
+ include_directories(${dbusmenu-qt5_INCLUDE_DIRS}). Simply adding
+ dbusmenu-qt5 to the target_link_libraries() call takes care of
+ defining the include directory. This requires CMake 2.8.11, so I
+ bumped the minimum version numbers accordingly.
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 19 Jun 2014 09:07:18 +0000
+
+libdbusmenu-qt (0.9.3+14.04.20140314-0ubuntu1) trusty; urgency=medium
+
+ [ Pete Woods ]
+ * Add importer parameter to allow control over DBus behavior.
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 14 Mar 2014 16:41:54 +0000
+
+libdbusmenu-qt (0.9.2+14.04.20140305-0ubuntu1) trusty; urgency=low
+
+ * New rebuild forced
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Wed, 05 Mar 2014 09:29:14 +0000
+
+libdbusmenu-qt (0.9.2+14.04.20140218.2-0ubuntu1) trusty; urgency=low
+
+ [ Pete Woods ]
+ * Remove busy watcher (LP: #1280372)
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Tue, 18 Feb 2014 14:33:02 +0000
+
+libdbusmenu-qt (0.9.2+14.04.20140217.1-0ubuntu1) trusty; urgency=low
+
+ [ Pete Woods ]
+ * Fix compile warning when building with -Wpedantic (extra semicolon).
+
+ [ Ted Gould ]
+ * Flushing trunk with a release
+
+ [ Timo Jyrinki ]
+ * Drop reference to libqt5core5 which will get renamed. (LP:
+ #1271058). (LP: #1271058)
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 17 Feb 2014 18:03:31 +0000
+
+libdbusmenu-qt (0.9.2+14.04.20131209-0ubuntu1) trusty; urgency=low
+
+ [ Marcus Tomlinson ]
+ * When adding a new submenu action, refresh() that action to ensure
+ full menu hierarchy is built.
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 253
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 09 Dec 2013 02:29:59 +0000
+
+libdbusmenu-qt (0.9.2+14.04.20131125-0ubuntu1) trusty; urgency=low
+
+ [ Aurélien Gâteau ]
+ * This change install CMake config files for dbusmenu-qt and dbusmenu-
+ qt5. This makes it easy for other projects to use the library with
+ find(dbusmenu-qt) or find(dbusmenu-qt5) without having to ship a
+ FindDBusMenuQt.cmake file. (More about this topic here:
+ http://www.cmake.org/Wiki/CMake/Tutorials/Packaging ). Test programs
+ available here: http://agateau.com/tmp/dmqt-samples.tar.bz2.
+
+ [ Marcus Tomlinson ]
+ * Destructors of classes intended to be base classes updated to
+ virtual.
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 251
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 25 Nov 2013 03:56:49 +0000
+
+libdbusmenu-qt (0.9.2+13.10.20130826-0ubuntu1) saucy; urgency=low
+
+ [ Joe Yasi ]
+ * This fixes bug #1035755, [firefox] Extension causes context/drop
+ down menus to disappear. The patch uses the correct X11 event
+ timestamp instead of the system time. (LP: #1035755)
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 248
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Mon, 26 Aug 2013 10:07:36 +0000
+
+libdbusmenu-qt (0.9.2+13.10.20130628-0ubuntu1) saucy; urgency=low
+
+ [ Łukasz 'sil2100' Zemczak ]
+ * Fix the pkg-config file for the libdbusmenu-qt5 case, as the
+ includedir was missing the correct QT_SUFFIX.
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 246
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Fri, 28 Jun 2013 02:03:12 +0000
+
+libdbusmenu-qt (0.9.2daily13.05.02-0ubuntu1) saucy; urgency=low
+
+ [ Łukasz 'sil2100' Zemczak ]
+ * debian/control, debian/rules:
+ - Modifications related to compliance with our packaging standards
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 244
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 02 May 2013 22:59:30 +0000
+
+libdbusmenu-qt (0.9.2daily13.03.28-0ubuntu1) raring; urgency=low
+
+ [ Łukasz 'sil2100' Zemczak ]
+ * Add inline packaging metadata.
+ * debian/control,
+ debian/libdbusmenu-qt5.install,
+ debian/libdbusmenu-qt5-dev.install,
+ debian/libdbusmenu-qt5-doc.install:
+ - Add the -qt5 package versions of all libdbusmenu packages
+ * debian/rules:
+ - Enable a double build - first build a Qt4 version of libdbusmenu for
+ libdbusmenu-qt2 and then a Qt5 version for libdbusmenu-qt5
+
+ [ Mathieu Trudel-Lapierre ]
+ * debian/copyright: fix copyright stanza for LGPL 2.
+ * debian/control: bump debhelper Build-Depends to 9.
+ * debian/source.lintian-overrides: drop the override.
+ * debian/watch: dropped; no longer needed with inline packaging.
+ * debian/control:
+ - bump Standards-Version to 3.9.4.
+ - add Vcs-Bzr/Vcs-Browser.
+ - add comments for developers.
+ * Automatic snapshot from revision 240 (bootstrap). (LP: #1126205)
+
+ [ Ubuntu daily release ]
+ * Automatic snapshot from revision 241
+
+ -- Ubuntu daily release <ps-jenkins@lists.canonical.com> Thu, 28 Mar 2013 20:27:01 +0000
+
+libdbusmenu-qt (0.9.2-0ubuntu4) raring; urgency=low
+
+ * Mark two library symbols as optional, fixing build failure with GCC 4.8.
+
+ -- Matthias Klose <doko@ubuntu.com> Tue, 26 Feb 2013 10:02:51 +0100
+
+libdbusmenu-qt (0.9.2-0ubuntu3) quantal; urgency=low
+
+ * Update symbols file again for powerpc and armel to fix FTBFS on those
+ archs
+
+ -- Scott Kitterman <scott@kitterman.com> Mon, 24 Sep 2012 11:56:51 -0400
+
+libdbusmenu-qt (0.9.2-0ubuntu2) quantal; urgency=low
+
+ * Update symbols file to fix FTBFS
+
+ -- Scott Kitterman <scott@kitterman.com> Mon, 24 Sep 2012 09:58:44 -0400
+
+libdbusmenu-qt (0.9.2-0ubuntu1) precise; urgency=low
+
+ * New upstream release.
+
+ -- Aurélien Gâteau <aurelien.gateau@ubuntu.com> Thu, 29 Mar 2012 17:51:21 +0200
+
+libdbusmenu-qt (0.9.1-0ubuntu1) precise; urgency=low
+
+ * New upstream release.
+
+ -- Aurélien Gâteau <aurelien.gateau@ubuntu.com> Mon, 26 Mar 2012 15:36:30 +0200
+
+libdbusmenu-qt (0.9.0-2ubuntu1) precise; urgency=low
+
+ * Merge from Debian git, remaining changes:
+ - patches/002-use-multiarch-lib-paths: fixed author
+ - copyright: fixed "Source" field
+
+ -- Aurélien Gâteau <aurelien.gateau@ubuntu.com> Thu, 24 Nov 2011 16:32:30 +0100
+
+libdbusmenu-qt (0.9.0-2) UNRELEASED; urgency=low
+
+ * update description for -dev package too. (Closes: #640251)
+ * Implement multiarch.
+ - bump cmake build dependency to 2.8.5.
+ - bump debhelper build dependency to 8.1.3.
+ * debian/rules: move --parallel after $@
+ * New binary package libdbusmenu-qt-doc.
+ * Remove embedded jquery and depend on libjs-jquery.
+
+ -- Praveen Arimbrathodiyil <pravi.a@gmail.com> Thu, 01 Sep 2011 19:29:20 +0530
+
+libdbusmenu-qt (0.9.0-1) unstable; urgency=low
+
+ * New upstream release.
+ * Add doxygen as build dependency.
+ * Minor fixes to description, thanks to Filipus Klutiero. (Closes: #630193)
+ * Update symbols file.
+
+ -- Praveen Arimbrathodiyil <pravi.a@gmail.com> Thu, 01 Sep 2011 12:57:01 +0530
+
+libdbusmenu-qt (0.9.0-0ubuntu2) oneiric; urgency=low
+
+ * Remove build-dependency on libqjson-dev.
+ * Convert to multiarch. (LP: #838470)
+ * debian/patches/kubuntu_03_multiarch_support.diff:
+ - add multiarch support to upstream CMake
+
+ -- Aurélien Gâteau <aurelien.gateau@canonical.com> Wed, 07 Sep 2011 13:05:40 +0200
+
+libdbusmenu-qt (0.9.0-0ubuntu1) oneiric; urgency=low
+
+ * New upstream release.
+ * debian/control:
+ - Update Vcs-Bzr to current kubuntu-packagers location
+ * debian/libdbusmenu-qt2.symbols:
+ - updated
+
+ -- Didier Roche <didrocks@ubuntu.com> Fri, 02 Sep 2011 13:50:43 +0200
+
+libdbusmenu-qt (0.8.3-0ubuntu1) oneiric; urgency=low
+
+ * New upstream release
+ * debian/control:
+ - Update Vcs-Bzr to current kubuntu-packagers location
+ - Change HomePage to launchpad page
+ - Update Standards-Version
+ * debian/copyright, debian/watch:
+ - point now to launchpad
+ * remove debian/patches/kubuntu_03_dont-show-more-icons-than-desired.diff:
+ upstreamed
+ * debian/libdbusmenu-qt2.symbols:
+ - updated symbols
+
+ -- Didier Roche <didrocks@ubuntu.com> Wed, 22 Jun 2011 11:13:07 +0200
+
+libdbusmenu-qt (0.8.2-0ubuntu2) natty; urgency=low
+
+ * Add kubuntu_03_dont-show-more-icons-than-desired.diff by Michael Terry
+ https://code.launchpad.net/~mterry/libdbusmenu-qt/dont-show-more-
+ icons-than-desired/+merge/58387 'Respect QAction's setting for
+ whether an icon should be shown in the menu or not.'
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Wed, 20 Apr 2011 13:47:45 +0100
+
+libdbusmenu-qt (0.8.2-0ubuntu1) natty; urgency=low
+
+ * New upstream release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Fri, 15 Apr 2011 12:07:57 +0100
+
+libdbusmenu-qt (0.8.1-0ubuntu1) natty; urgency=low
+
+ * New upstream release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Thu, 24 Mar 2011 16:40:25 +0000
+
+libdbusmenu-qt (0.8.0-3) unstable; urgency=low
+
+ * gcc seems to still emit some random symbols on some arches. Mark them as
+ optional and arch specific. (Closes: #628745)
+
+ -- Modestas Vainius <modax@debian.org> Wed, 01 Jun 2011 13:24:35 +0300
+
+libdbusmenu-qt (0.8.0-2) unstable; urgency=low
+
+ * Fix symbol files to build against gcc 4.6. (Closes: #628745)
+ * Bump Standards-Version to 3.9.2: no changes needed.
+ * Add myself to Uploaders.
+
+ -- Modestas Vainius <modax@debian.org> Wed, 01 Jun 2011 11:23:55 +0300
+
+libdbusmenu-qt (0.8.0-1) unstable; urgency=low
+
+ * New upstream release (3 private symbols dropped).
+ * Symbols file updated, missing symbols removed.
+ * Thanks to Shravan Aras for helping me with cscope.
+
+ -- Praveen Arimbrathodiyil <pravi.a@gmail.com> Sat, 02 Apr 2011 23:22:22 +0530
+
+libdbusmenu-qt (0.8.0-0ubuntu1) natty; urgency=low
+
+ * New upstream release
+ * Remove kubuntu_00_external_contributions.diff no longer required
+ * Update kubuntu_01_no_test.diff
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Fri, 25 Feb 2011 10:39:05 +0000
+
+libdbusmenu-qt (0.7.0-1) unstable; urgency=low
+
+ * New upstream release.
+ * DM upload is allowed.
+ * adding libqjson-dev to build dependencies (for testapp).
+
+ -- Praveen Arimbrathodiyil <pravi.a@gmail.com> Sat, 19 Mar 2011 11:03:22 +0530
+
+libdbusmenu-qt (0.7.0-0ubuntu1) natty; urgency=low
+
+ * New upstream release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Fri, 14 Jan 2011 15:56:38 +0000
+
+libdbusmenu-qt (0.6.6-1) unstable; urgency=low
+
+ * New upstream release. (Closes: #606199)
+
+ [ Praveen Arimbrathodiyil ]
+ * debian/rules: added support for parallel building.
+ * debian/copyright: cleared copyright info of removed patch.
+ * debian/control: changed homepage to page on launchpad.net.
+ * debian/control: updated vcs from svn to git.
+ * debian/control: updated policy to 3.9.1
+
+ [ José Manuel Santamaría Lema ]
+ * Update symbols file.
+
+ -- Praveen Arimbrathodiyil <pravi.a@gmail.com> Sun, 27 Feb 2011 07:05:33 +0530
+
+libdbusmenu-qt (0.6.6-0ubuntu1) natty; urgency=low
+
+ * New upstream bugfix release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Thu, 09 Dec 2010 11:59:53 +0000
+
+libdbusmenu-qt (0.6.4-0ubuntu1) maverick; urgency=low
+
+ * New upstream bugfix release
+ * Refresh kubuntu_00_external_contributions.diff
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Thu, 23 Sep 2010 13:45:17 +0100
+
+libdbusmenu-qt (0.6.3-0ubuntu1) maverick; urgency=low
+
+ * New upstream release
+ * Add kubuntu_00_external_contributions.diff
+ * Remove kubuntu_02_unbreak_kde_titles.diff
+
+ -- Aurélien Gâteau <aurelien.gateau@canonical.com> Thu, 16 Sep 2010 17:01:41 +0200
+
+libdbusmenu-qt (0.6.2-0ubuntu1) maverick; urgency=low
+
+ * New upstream release, memory leak and crash fixes
+ * Add kubuntu_02_unbreak_kde_titles.diff from
+ http://gitorious.org/dbusmenu/dbusmenu-
+ qt/commit/71851809ef7e109f02635877ead1dbac48a2e64e
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Thu, 09 Sep 2010 16:42:01 +0100
+
+libdbusmenu-qt (0.6.1-0ubuntu1) maverick; urgency=low
+
+ * New upstream release, fixes memory leaks
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Fri, 03 Sep 2010 17:14:45 +0100
+
+libdbusmenu-qt (0.6.0-0ubuntu1) maverick; urgency=low
+
+ * New upstream release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Thu, 19 Aug 2010 14:58:03 +0100
+
+libdbusmenu-qt (0.5.2-0ubuntu1) maverick; urgency=low
+
+ * New upstream release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Thu, 05 Aug 2010 16:08:10 +0100
+
+libdbusmenu-qt (0.5.1-1) unstable; urgency=low
+
+ * New upstream release. (Closes: #587546)
+ * debian/watch: changed release url to new location.
+ * debian/copyright: made it machine readable.
+ * debian/copyright: changed debian packaging to LGPL-2+ in sync with
+ upstream license (Thanks to Sune Vuorela)
+ * debian/control: updated policy to 3.9.0
+ * debian/libdbusmenu-qt2.symbols: new symbols added.
+ * debian/control: dev & lib package clarification added to descriptions.
+
+ -- Praveen Arimbrathodiyil <pravi.a@gmail.com> Fri, 02 Jul 2010 01:30:47 +0530
+
+libdbusmenu-qt (0.5.1-0ubuntu1) maverick; urgency=low
+
+ * New upstream release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Thu, 08 Jul 2010 21:07:52 +0100
+
+libdbusmenu-qt (0.5.0-0ubuntu1) maverick; urgency=low
+
+ * New upstream release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Mon, 28 Jun 2010 16:54:14 +0100
+
+libdbusmenu-qt (0.4.0-0ubuntu1) maverick; urgency=low
+
+ * New upstream release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Fri, 25 Jun 2010 14:20:55 +0100
+
+libdbusmenu-qt (0.3.5-0ubuntu1) maverick; urgency=low
+
+ * New upstream release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Thu, 17 Jun 2010 14:30:00 +0100
+
+libdbusmenu-qt (0.3.4-0ubuntu1) maverick; urgency=low
+
+ * New upstream release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Thu, 10 Jun 2010 23:45:12 +0100
+
+libdbusmenu-qt (0.3.3-1) unstable; urgency=low
+
+ * New upstream release.
+ * added libdbusmenu-qt2.symbols file.
+
+ -- Praveen Arimbrathodiyil <pravi.a@gmail.com> Fri, 11 Jun 2010 17:13:07 +0530
+
+libdbusmenu-qt (0.3.3-0ubuntu1) maverick; urgency=low
+
+ * New upstream release:
+ - Update kubuntu_01_no_test.diff
+
+ -- Jonathan Thomas <echidnaman@kubuntu.org> Mon, 07 Jun 2010 22:36:42 -0400
+
+libdbusmenu-qt (0.3.2-1) unstable; urgency=low
+
+ * Initial release. (Closes: #579677)
+
+ -- Praveen Arimbrathodiyil <pravi.a@gmail.com> Tue, 04 May 2010 13:31:09 +0530
+
+libdbusmenu-qt (0.3.2-0ubuntu1) lucid; urgency=low
+
+ * New upstream release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Thu, 08 Apr 2010 14:23:59 +0100
+
+libdbusmenu-qt (0.3.0-0ubuntu1) lucid; urgency=low
+
+ * New upstream release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Wed, 10 Mar 2010 12:41:06 +0000
+
+libdbusmenu-qt (0.2.2-0ubuntu2) lucid; urgency=low
+
+ * Add build dep on qjson and pkg-config
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Wed, 17 Feb 2010 17:02:07 +0000
+
+libdbusmenu-qt (0.2.2-0ubuntu1) lucid; urgency=low
+
+ * New upstream release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Wed, 17 Feb 2010 15:13:29 +0000
+
+libdbusmenu-qt (0.2.1-0ubuntu2) lucid; urgency=low
+
+ * libdbusmenu-qt-dev depends on libdbusmenu-qt1 not 0
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Thu, 04 Feb 2010 20:21:41 +0000
+
+libdbusmenu-qt (0.2.1-0ubuntu1) lucid; urgency=low
+
+ * New upstream release
+ * Switch to source format 3.0
+ * New SO version, switch package name to libdbusmenu-qt1
+ * Add kubuntu_01_no_test.diff, tests don't work without X
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Thu, 04 Feb 2010 18:13:44 +0000
+
+libdbusmenu-qt (0.1.0-0ubuntu1) lucid; urgency=low
+
+ * Initial Release
+
+ -- Jonathan Riddell <jriddell@ubuntu.com> Sun, 03 Jan 2010 23:21:24 +0100
+
--- /dev/null
+Source: libdbusmenu-qt
+Section: libs
+Priority: optional
+Build-Depends: cmake (>= 2.8.11),
+ debhelper (>= 9),
+ doxygen,
+ libqjson-dev,
+ libqt4-dev,
+ qtbase5-dev,
+Maintainer: Kubuntu Developers <kubuntu-devel@lists.ubuntu.com>
+XSBC-Original-Maintainer: Jonathan Riddell <jriddell@ubuntu.com>
+Standards-Version: 3.9.4
+Homepage: https://launchpad.net/libdbusmenu-qt
+# If you aren't ~dbusmenu-team but need to upload packaging changes,
+# just go ahead. ~dbusmenu-team will notice and sync up the code again.
+Vcs-Bzr: https://code.launchpad.net/~dbusmenu-team/libdbusmenu-qt/trunk
+Vcs-Browser: https://bazaar.launchpad.net/~dbusmenu-team/libdbusmenu-qt/trunk/files
+
+Package: libdbusmenu-qt2
+Architecture: any
+Pre-Depends: ${misc:Pre-Depends},
+Depends: ${misc:Depends},
+ ${shlibs:Depends},
+Multi-Arch: same
+Description: Qt implementation of the DBusMenu protocol
+ This library provides a Qt implementation of the DBusMenu protocol.
+ .
+ The DBusMenu protocol makes it possible for applications to export
+ and import their menus over DBus.
+ .
+ This package provides shared libraries.
+
+Package: libdbusmenu-qt5
+Architecture: any
+Pre-Depends: ${misc:Pre-Depends},
+Depends: ${misc:Depends},
+ ${shlibs:Depends},
+Suggests: libqt5dbus5,
+ libqt5gui5,
+ libqt5widgets5,
+Multi-Arch: same
+Description: Qt5 implementation of the DBusMenu protocol
+ This library provides a Qt5 implementation of the DBusMenu protocol.
+ .
+ The DBusMenu protocol makes it possible for applications to export
+ and import their menus over DBus.
+ .
+ This package provides shared libraries.
+
+Package: libdbusmenu-qt-dev
+Section: libdevel
+Architecture: any
+Depends: libdbusmenu-qt2 (= ${binary:Version}),
+ libqt4-dev,
+ ${misc:Depends},
+Description: Qt implementation of the DBusMenu protocol (development)
+ This library provides a Qt implementation of the DBusMenu protocol.
+ .
+ The DBusMenu protocol makes it possible for applications to export
+ and import their menus over DBus.
+ .
+ This package provides header files for development.
+
+Package: libdbusmenu-qt5-dev
+Section: libdevel
+Architecture: any
+Depends: libdbusmenu-qt5 (= ${binary:Version}),
+ qtbase5-dev,
+ ${misc:Depends},
+Description: Qt5 implementation of the DBusMenu protocol (development)
+ This library provides a Qt5 implementation of the DBusMenu protocol.
+ .
+ The DBusMenu protocol makes it possible for applications to export
+ and import their menus over DBus.
+ .
+ This package provides header files for development.
+
+Package: libdbusmenu-qt-doc
+Section: doc
+Architecture: all
+Depends: libjs-jquery,
+ ${misc:Depends},
+Description: Qt implementation of the DBusMenu protocol (documentation)
+ This library provides a Qt implementation of the DBusMenu protocol.
+ .
+ The DBusMenu protocol makes it possible for applications to export
+ and import their menus over DBus.
+ .
+ This package provides API documentation in html format.
+
+Package: libdbusmenu-qt5-doc
+Section: doc
+Architecture: all
+Depends: libjs-jquery,
+ ${misc:Depends},
+Description: Qt5 implementation of the DBusMenu protocol (documentation)
+ This library provides a Qt5 implementation of the DBusMenu protocol.
+ .
+ The DBusMenu protocol makes it possible for applications to export
+ and import their menus over DBus.
+ .
+ This package provides API documentation in html format.
--- /dev/null
+Format: http://dep.debian.net/deps/dep5/
+Upstream-Name: libdbusmenu-qt
+Upstream-Contact: Aurelien Gateau <aurelien.gateau@canonical.com>
+Source: https://launchpad.net/libdbusmenu-qt
+
+Files: src/*
+Copyright: 2009-2010, Canonical
+Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+License: LGPL-2
+
+Files: debian/*
+Copyright: 2010, Praveen Arimbrathodiyil <pravi.a@gmail.com>
+License: LGPL-2
+
+License: LGPL-2
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL), version 2 as published by the Free Software
+ Foundation.
+ .
+ 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
+ Library General Public License for more details.
+ .
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+ .
+ On Debian systems, the complete text of the GNU Library General Public
+ License, version 2, can be found in /usr/share/common-licenses/LGPL-2.
--- /dev/null
+NEWS
+README
--- /dev/null
+usr/include/dbusmenu-qt/*
+usr/lib/*/lib*-qt.so
+usr/lib/*/pkgconfig/dbusmenu-qt.pc
+usr/lib/*/cmake/dbusmenu-qt/*
--- /dev/null
+usr/share/doc/libdbusmenu-qt-doc/
--- /dev/null
+usr/lib/*/libdbusmenu-qt.so.2*
--- /dev/null
+usr/include/dbusmenu-qt5/*
+usr/lib/*/lib*-qt5.so
+usr/lib/*/pkgconfig/dbusmenu-qt5.pc
+usr/lib/*/cmake/dbusmenu-qt5/*
--- /dev/null
+usr/share/doc/libdbusmenu-qt5-doc/
--- /dev/null
+usr/lib/*/libdbusmenu-qt5.so.2*
--- /dev/null
+#!/usr/bin/make -f
+
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
+
+export DPKG_GENSYMBOLS_CHECK_LEVEL=4
+
+%:
+ dh $@ --parallel --buildsystem=cmake
+
+override_dh_auto_configure:
+ mkdir qt4
+ mkdir qt5
+ cd qt4 && QT_SELECT=qt4 cmake -DCMAKE_INSTALL_PREFIX=/usr -DUSE_QT4=true -DCMAKE_BUILD_TYPE=RelWithDebInfo ../
+ cd qt5 && QT_SELECT=qt5 cmake -DCMAKE_INSTALL_PREFIX=/usr -DUSE_QT5=true -DCMAKE_BUILD_TYPE=RelWithDebInfo ../
+
+override_dh_auto_build:
+ cd qt4 && make
+ cd qt5 && make
+
+override_dh_clean:
+ dh_clean
+ rm -rf qt4
+ rm -rf qt5
+
+override_dh_auto_install:
+ cd qt4 && make DESTDIR=../debian/tmp install
+ cd qt5 && make DESTDIR=../debian/tmp install
+ echo "Removing embedded jquery javascript library..."
+ rm debian/tmp/usr/share/doc/libdbusmenu-qt-doc/jquery.js
+ rm debian/tmp/usr/share/doc/libdbusmenu-qt5-doc/jquery.js
+
+override_dh_install:
+ dh_install --fail-missing
+
+override_dh_auto_test:
+ echo "Skipping tests (can't test inside chroot)..."
+
+override_dh_gencontrol:
+ # Ugly hack, since we don't want to have Qt5 as our depends, we prefer
+ # those as Suggests for now
+ sed -i '/^shlibs/s/libqt5[^,]*, //g' debian/libdbusmenu-qt5.substvars
+ sed -i '/^shlibs/s/,[^,]* libqt5.*$$//' debian/libdbusmenu-qt5.substvars
+
+ dh_gencontrol
--- /dev/null
+include(CheckCXXSourceCompiles)
+
+check_cxx_compiler_flag(-Wall __DBUSMENU_HAVE_W_ALL)
+if (__DBUSMENU_HAVE_W_ALL)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
+endif (__DBUSMENU_HAVE_W_ALL)
+
+# Check some compiler flags
+check_cxx_compiler_flag(-fvisibility=hidden __DBUSMENU_HAVE_GCC_VISIBILITY)
+if (__DBUSMENU_HAVE_GCC_VISIBILITY AND NOT WIN32)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fvisibility=hidden")
+endif (__DBUSMENU_HAVE_GCC_VISIBILITY AND NOT WIN32)
+
+check_cxx_compiler_flag(-Woverloaded-virtual __DBUSMENU_HAVE_W_OVERLOADED_VIRTUAL)
+if (__DBUSMENU_HAVE_W_OVERLOADED_VIRTUAL)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Woverloaded-virtual")
+endif (__DBUSMENU_HAVE_W_OVERLOADED_VIRTUAL)
+
+check_cxx_compiler_flag(-Wall __DBUSMENU_HAVE_W_ALL)
+if (__DBUSMENU_HAVE_W_ALL)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
+endif (__DBUSMENU_HAVE_W_ALL)
+
+check_cxx_compiler_flag(-std=c++11 __DBUSMENU_HAVE_CXX11)
+if (__DBUSMENU_HAVE_CXX11)
+ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11")
+endif (__DBUSMENU_HAVE_CXX11)
+
+# Check whether QIcon::name() exists. It was added in late Qt 4.7 cycle, and is
+# not present in betas.
+
+if (NOT USE_QT5)
+ set(CMAKE_REQUIRED_INCLUDES "${QT_INCLUDE_DIR}")
+ set(CMAKE_REQUIRED_LIBRARIES "${QT_QTGUI_LIBRARIES};${QT_QTCORE_LIBRARIES}")
+else()
+ set(CMAKE_REQUIRED_INCLUDES "${Qt5Gui_INCLUDE_DIRS};${Qt5Core_INCLUDE_DIRS}")
+ set(CMAKE_REQUIRED_LIBRARIES "${Qt5Gui_LIBRARIES};${Qt5Core_LIBRARIES}")
+endif()
+check_cxx_source_compiles("
+#include <QtGui/QIcon>
+int main() {
+ QIcon icon;
+ icon.name();
+ return 0;
+}
+" HAVE_QICON_NAME)
+if (NOT HAVE_QICON_NAME)
+ message(STATUS "QIcon::name() does not exist, DBusMenuExporter will not export icon names by itself")
+endif()
+configure_file(dbusmenu_config.h.in ${CMAKE_CURRENT_BINARY_DIR}/dbusmenu_config.h @ONLY)
+
+set(dbusmenu_qt_SRCS
+ dbusmenu_p.cpp
+ dbusmenuexporter.cpp
+ dbusmenuexporterdbus_p.cpp
+ dbusmenuimporter.cpp
+ dbusmenutypes_p.cpp
+ dbusmenushortcut_p.cpp
+ utils.cpp
+ )
+
+include_directories(
+ ${CMAKE_SOURCE_DIR}/src
+ ${CMAKE_BINARY_DIR}/src
+ )
+
+if (NOT USE_QT5)
+ qt4_automoc(${dbusmenu_qt_SRCS})
+ qt4_add_dbus_adaptor(dbusmenu_qt_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.dbusmenu.xml
+ ${CMAKE_CURRENT_SOURCE_DIR}/dbusmenuexporterdbus_p.h DBusMenuExporterDBus
+ )
+else()
+ qt5_add_dbus_adaptor(dbusmenu_qt_SRCS
+ ${CMAKE_CURRENT_SOURCE_DIR}/com.canonical.dbusmenu.xml
+ ${CMAKE_CURRENT_SOURCE_DIR}/dbusmenuexporterdbus_p.h DBusMenuExporterDBus
+ )
+endif()
+
+configure_file(dbusmenu_version.h.in
+ ${CMAKE_CURRENT_BINARY_DIR}/dbusmenu_version.h
+ )
+
+add_library(dbusmenu-${QT_SUFFIX} SHARED ${dbusmenu_qt_SRCS})
+set_target_properties(dbusmenu-${QT_SUFFIX} PROPERTIES
+ VERSION ${dbusmenu_qt_lib_VERSION}
+ SOVERSION ${dbusmenu_qt_lib_SOVERSION}
+ )
+
+
+if (NOT USE_QT5)
+ target_link_libraries(dbusmenu-${QT_SUFFIX}
+ ${QT_QTGUI_LIBRARIES}
+ ${QT_QTDBUS_LIBRARIES}
+ ${QT_QTCORE_LIBRARIES}
+ )
+else()
+ target_link_libraries(dbusmenu-${QT_SUFFIX}
+ ${Qt5Gui_LIBRARIES}
+ ${Qt5Core_LIBRARIES}
+ ${Qt5DBus_LIBRARIES}
+ ${Qt5Widgets_LIBRARIES}
+ )
+endif()
+
+# Make sure linking to the target adds dbusmenu-qt install directory
+target_include_directories(dbusmenu-${QT_SUFFIX}
+ INTERFACE "$<INSTALL_INTERFACE:${INCLUDE_INSTALL_DIR}>")
+
+install(TARGETS dbusmenu-${QT_SUFFIX}
+ EXPORT dbusmenu-${QT_SUFFIX}-targets
+ LIBRARY DESTINATION ${LIB_DESTINATION}
+ RUNTIME DESTINATION bin
+ )
+
+install(EXPORT dbusmenu-${QT_SUFFIX}-targets
+ DESTINATION ${CMAKECONFIG_INSTALL_DIR})
+
+install(DIRECTORY .
+ DESTINATION ${INCLUDE_INSTALL_DIR}
+ FILES_MATCHING PATTERN "*.h"
+ PATTERN "*_p.h" EXCLUDE
+ )
+
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/dbusmenu_version.h
+ DESTINATION ${INCLUDE_INSTALL_DIR}
+ )
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+A library to allow applications to provide simple indications of
+information to be displayed to users of the application through the
+interface shell.
+
+Copyright 2009 Canonical Ltd.
+
+Authors:
+ Ted Gould <ted@canonical.com>
+ Aurélien Gâteau <aurelien.gateau@canonical.com>
+
+This program is free software: you can redistribute it and/or modify it
+under the terms of either or both of the following licenses:
+
+1) the GNU Lesser General Public License version 3, as published by the
+Free Software Foundation; and/or
+2) the GNU Lesser General Public License version 2.1, as published by
+the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranties of
+MERCHANTABILITY, SATISFACTORY QUALITY or FITNESS FOR A PARTICULAR
+PURPOSE. See the applicable version of the GNU Lesser General Public
+License for more details.
+
+You should have received a copy of both the GNU Lesser General Public
+License version 3 and version 2.1 along with this program. If not, see
+<http://www.gnu.org/licenses/>
+-->
+<node name="/" xmlns:dox="http://www.canonical.com/dbus/dox.dtd">
+ <dox:d><![CDATA[
+ @mainpage
+
+ The goal of DBusMenu is to expose menus on DBus.
+
+ Main interface is documented here: @ref com::canonical::dbusmenu
+ ]]></dox:d>
+ <interface name="com.canonical.dbusmenu">
+ <dox:d><![CDATA[
+ A DBus interface to expose menus on DBus.
+
+ Menu items are represented with a unique numeric id and a dictionary of
+ properties.
+
+ To reduce the amount of DBus traffic, a property should only be returned
+ if its value is not the default value.
+
+ Available properties are:
+
+ <table>
+ <tr>
+ <th>Name</th>
+ <th>Type</th>
+ <th>Description</th>
+ <th>Default Value</th>
+ </tr>
+ <tr>
+ <td>type</td>
+ <td>String</td>
+ <td>Can be one of:
+ - "standard": an item which can be clicked to trigger an action or
+ show another menu
+ - "separator": a separator
+
+ Vendor specific types can be added by prefixing them with
+ "x-<vendor>-".
+ </td>
+ <td>"standard"</td>
+ </tr>
+ <tr>
+ <td>label</td>
+ <td>string</td>
+ <td>Text of the item, except that:
+ -# two consecutive underscore characters "__" are displayed as a
+ single underscore,
+ -# any remaining underscore characters are not displayed at all,
+ -# the first of those remaining underscore characters (unless it is
+ the last character in the string) indicates that the following
+ character is the access key.
+ </td>
+ <td>""</td>
+ </tr>
+ <tr>
+ <td>enabled</td>
+ <td>boolean</td>
+ <td>Whether the item can be activated or not.</td>
+ <td>true</td>
+ </tr>
+ <tr>
+ <td>visible</td>
+ <td>boolean</td>
+ <td>True if the item is visible in the menu.</td>
+ <td>true</td>
+ </tr>
+ <tr>
+ <td>icon-name</td>
+ <td>string</td>
+ <td>Icon name of the item, following the freedesktop.org icon spec.</td>
+ <td>""</td>
+ </tr>
+ <tr>
+ <td>icon-data</td>
+ <td>binary</td>
+ <td>PNG data of the icon.</td>
+ <td>Empty</td>
+ </tr>
+ <tr>
+ <td>shortcut</td>
+ <td>array of arrays of strings</td>
+ <td>The shortcut of the item. Each array represents the key press
+ in the list of keypresses. Each list of strings contains a list of
+ modifiers and then the key that is used. The modifier strings
+ allowed are: "Control", "Alt", "Shift" and "Super".
+
+ - A simple shortcut like Ctrl+S is represented as:
+ [["Control", "S"]]
+ - A complex shortcut like Ctrl+Q, Alt+X is represented as:
+ [["Control", "Q"], ["Alt", "X"]]</td>
+ <td>Empty</td>
+ </tr>
+ <tr>
+ <td>toggle-type</td>
+ <td>string</td>
+ <td>
+ If the item can be toggled, this property should be set to:
+ - "checkmark": Item is an independent togglable item
+ - "radio": Item is part of a group where only one item can be
+ toggled at a time
+ - "": Item cannot be toggled
+ </td>
+ <td>""</td>
+ </tr>
+ <tr>
+ <td>toggle-state</td>
+ <td>int</td>
+ <td>
+ Describe the current state of a "togglable" item. Can be one of:
+ - 0 = off
+ - 1 = on
+ - anything else = indeterminate
+
+ Note:
+ The implementation does not itself handle ensuring that only one
+ item in a radio group is set to "on", or that a group does not have
+ "on" and "indeterminate" items simultaneously; maintaining this
+ policy is up to the toolkit wrappers.
+ </td>
+ <td>-1</td>
+ </tr>
+ <tr>
+ <td>children-display</td>
+ <td>string</td>
+ <td>
+ If the menu item has children this property should be set to
+ "submenu".
+ </td>
+ <td>""</td>
+ </tr>
+ </table>
+
+ Vendor specific properties can be added by prefixing them with
+ "x-<vendor>-".
+ ]]></dox:d>
+
+<!-- Properties -->
+ <property name="Version" type="u" access="read">
+ <dox:d>
+ Provides the version of the DBusmenu API that this API is
+ implementing.
+ </dox:d>
+ </property>
+
+ <property name="Status" type="s" access="read">
+ <dox:d>
+ Tells if the menus are in a normal state or they believe that they
+ could use some attention. Cases for showing them would be if help
+ were referring to them or they accessors were being highlighted.
+ This property can have two values: "normal" in almost all cases and
+ "notice" when they should have a higher priority to be shown.
+ </dox:d>
+ </property>
+
+<!-- Functions -->
+
+ <method name="GetLayout">
+ <annotation name="com.trolltech.QtDBus.QtTypeName.Out1" value="DBusMenuLayoutItem"/>
+ <dox:d>
+ Provides the layout and propertiers that are attached to the entries
+ that are in the layout. It only gives the items that are children
+ of the item that is specified in @a parentId. It will return all of the
+ properties or specific ones depending of the value in @a propertyNames.
+
+ The format is recursive, where the second 'v' is in the same format
+ as the original 'a(ia{sv}av)'. Its content depends on the value
+ of @a recursionDepth.
+ </dox:d>
+ <arg type="i" name="parentId" direction="in">
+ <dox:d>The ID of the parent node for the layout. For
+ grabbing the layout from the root node use zero.</dox:d>
+ </arg>
+ <arg type="i" name="recursionDepth" direction="in">
+ <dox:d>
+ The amount of levels of recursion to use. This affects the
+ content of the second variant array.
+ - -1: deliver all the items under the @a parentId.
+ - 0: no recursion, the array will be empty.
+ - n: array will contains items up to 'n' level depth.
+ </dox:d>
+ </arg>
+ <arg type="as" name="propertyNames" direction="in" >
+ <dox:d>
+ The list of item properties we are
+ interested in. If there are no entries in the list all of
+ the properties will be sent.
+ </dox:d>
+ </arg>
+ <arg type="u" name="revision" direction="out">
+ <dox:d>The revision number of the layout. For matching
+ with layoutUpdated signals.</dox:d>
+ </arg>
+ <arg type="(ia{sv}av)" name="layout" direction="out">
+ <dox:d>The layout, as a recursive structure.</dox:d>
+ </arg>
+ </method>
+
+ <method name="GetGroupProperties">
+ <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="QList<int>"/>
+ <annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="DBusMenuItemList"/>
+ <dox:d>
+ Returns the list of items which are children of @a parentId.
+ </dox:d>
+ <arg type="ai" name="ids" direction="in" >
+ <dox:d>
+ A list of ids that we should be finding the properties
+ on. If the list is empty, all menu items should be sent.
+ </dox:d>
+ </arg>
+ <arg type="as" name="propertyNames" direction="in" >
+ <dox:d>
+ The list of item properties we are
+ interested in. If there are no entries in the list all of
+ the properties will be sent.
+ </dox:d>
+ </arg>
+ <arg type="a(ia{sv})" name="properties" direction="out" >
+ <dox:d>
+ An array of property values.
+ An item in this area is represented as a struct following
+ this format:
+ @li id unsigned the item id
+ @li properties map(string => variant) the requested item properties
+ </dox:d>
+ </arg>
+ </method>
+
+ <method name="GetProperty">
+ <dox:d>
+ Get a signal property on a single item. This is not useful if you're
+ going to implement this interface, it should only be used if you're
+ debugging via a commandline tool.
+ </dox:d>
+ <arg type="i" name="id" direction="in">
+ <dox:d>the id of the item which received the event</dox:d>
+ </arg>
+ <arg type="s" name="name" direction="in">
+ <dox:d>the name of the property to get</dox:d>
+ </arg>
+ <arg type="v" name="value" direction="out">
+ <dox:d>the value of the property</dox:d>
+ </arg>
+ </method>
+
+ <method name="Event">
+ <dox:d><![CDATA[
+ This is called by the applet to notify the application an event happened on a
+ menu item.
+
+ @a type can be one of the following:
+
+ @li "clicked"
+ @li "hovered"
+
+ Vendor specific events can be added by prefixing them with "x-<vendor>-"
+ ]]></dox:d>
+ <arg type="i" name="id" direction="in" >
+ <dox:d>the id of the item which received the event</dox:d>
+ </arg>
+ <arg type="s" name="eventId" direction="in" >
+ <dox:d>the type of event</dox:d>
+ </arg>
+ <arg type="v" name="data" direction="in" >
+ <dox:d>event-specific data</dox:d>
+ </arg>
+ <arg type="u" name="timestamp" direction="in" >
+ <dox:d>The time that the event occured if available or the time the message was sent if not</dox:d>
+ </arg>
+ </method>
+
+ <method name="AboutToShow">
+ <dox:d>
+ This is called by the applet to notify the application that it is about
+ to show the menu under the specified item.
+ </dox:d>
+ <arg type="i" name="id" direction="in">
+ <dox:d>
+ Which menu item represents the parent of the item about to be shown.
+ </dox:d>
+ </arg>
+ <arg type="b" name="needUpdate" direction="out">
+ <dox:d>
+ Whether this AboutToShow event should result in the menu being updated.
+ </dox:d>
+ </arg>
+ </method>
+
+<!-- Signals -->
+ <signal name="ItemsPropertiesUpdated">
+ <annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="DBusMenuItemList"/>
+ <annotation name="com.trolltech.QtDBus.QtTypeName.In1" value="DBusMenuItemKeysList"/>
+ <dox:d>
+ Triggered when there are lots of property updates across many items
+ so they all get grouped into a single dbus message. The format is
+ the ID of the item with a hashtable of names and values for those
+ properties.
+ </dox:d>
+ <arg type="a(ia{sv})" name="updatedProps" direction="out" />
+ <arg type="a(ias)" name="removedProps" direction="out" />
+ </signal>
+
+ <signal name="LayoutUpdated">
+ <dox:d>
+ Triggered by the application to notify display of a layout update, up to
+ revision
+ </dox:d>
+ <arg type="u" name="revision" direction="out" >
+ <dox:d>The revision of the layout that we're currently on</dox:d>
+ </arg>
+ <arg type="i" name="parent" direction="out" >
+ <dox:d>
+ If the layout update is only of a subtree, this is the
+ parent item for the entries that have changed. It is zero if
+ the whole layout should be considered invalid.
+ </dox:d>
+ </arg>
+ </signal>
+ <signal name="ItemActivationRequested">
+ <dox:d>
+ The server is requesting that all clients displaying this
+ menu open it to the user. This would be for things like
+ hotkeys that when the user presses them the menu should
+ open and display itself to the user.
+ </dox:d>
+ <arg type="i" name="id" direction="out" >
+ <dox:d>ID of the menu that should be activated</dox:d>
+ </arg>
+ <arg type="u" name="timestamp" direction="out" >
+ <dox:d>The time that the event occured</dox:d>
+ </arg>
+ </signal>
+
+<!-- End of interesting stuff -->
+
+ </interface>
+</node>
--- /dev/null
+/* Whether QIcon::name() exists */
+#cmakedefine HAVE_QICON_NAME
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENU_EXPORT_H
+#define DBUSMENU_EXPORT_H
+
+// Include this file from here to make transition from version 0.3.5 and
+// earlier easier (no need to conditionally include a file)
+#include <dbusmenu_version.h>
+
+// Qt
+#include <QtCore/QtGlobal>
+
+#ifdef dbusmenu_qt_EXPORTS
+#define DBUSMENU_EXPORT Q_DECL_EXPORT
+#else
+#define DBUSMENU_EXPORT Q_DECL_IMPORT
+#endif
+
+#endif /* DBUSMENU_EXPORT_H */
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include "dbusmenu_p.h"
+
+// Qt
+#include <QAction>
+#include <QActionEvent>
+#include <QMenu>
+
+// Local
+#include "dbusmenuexporter.h"
+#include "dbusmenuexporterprivate_p.h"
+#include "debug_p.h"
+
+DBusMenu::DBusMenu(QMenu *menu, DBusMenuExporter *exporter, int parentId)
+: QObject(menu)
+, m_exporter(exporter)
+, m_parentId(parentId)
+{
+ menu->installEventFilter(this);
+ connect(m_exporter, SIGNAL(destroyed(QObject*)), SLOT(deleteMe()));
+}
+
+DBusMenu::~DBusMenu()
+{
+}
+
+bool DBusMenu::eventFilter(QObject *, QEvent *event)
+{
+ QActionEvent *actionEvent = 0;
+ switch (event->type()) {
+ case QEvent::ActionAdded:
+ case QEvent::ActionChanged:
+ case QEvent::ActionRemoved:
+ actionEvent = static_cast<QActionEvent *>(event);
+ break;
+ default:
+ return false;
+ }
+ switch (event->type()) {
+ case QEvent::ActionAdded:
+ addAction(actionEvent->action());
+ break;
+ case QEvent::ActionChanged:
+ updateAction(actionEvent->action());
+ break;
+ case QEvent::ActionRemoved:
+ removeAction(actionEvent->action());
+ break;
+ default:
+ break;
+ }
+ return false;
+}
+
+void DBusMenu::addAction(QAction *action)
+{
+ m_exporter->d->addAction(action, m_parentId);
+}
+
+void DBusMenu::updateAction(QAction *action)
+{
+ m_exporter->d->updateAction(action);
+}
+
+void DBusMenu::removeAction(QAction *action)
+{
+ m_exporter->d->removeAction(action, m_parentId);
+}
+
+void DBusMenu::deleteMe()
+{
+ delete this;
+}
+
+#include "dbusmenu_p.moc"
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENU_H
+#define DBUSMENU_H
+
+#include <QEvent>
+#include <QObject>
+
+class QAction;
+class QMenu;
+
+class DBusMenuExporter;
+
+/**
+ * Internal class responsible for tracking changes in a menu and reporting them
+ * through DBusMenuExporter
+ * @internal
+ */
+class DBusMenu : public QObject
+{
+ Q_OBJECT
+public:
+ DBusMenu(QMenu *menu, DBusMenuExporter *exporter, int parentId);
+ virtual ~DBusMenu();
+
+protected:
+ virtual bool eventFilter(QObject *obj, QEvent *event);
+
+private Q_SLOTS:
+ void deleteMe();
+
+private:
+ void addAction(QAction *action);
+ void updateAction(QAction *action);
+ void removeAction(QAction *action);
+
+ DBusMenuExporter* m_exporter;
+ int m_parentId;
+};
+
+#endif /* DBUSMENU_H */
--- /dev/null
+/* This file is part of the KDE libraries
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENUQT_VERSION_H
+#define DBUSMENUQT_VERSION_H
+
+#define DBUSMENUQT_VERSION_MAJOR @dbusmenu_qt_VERSION_MAJOR@
+#define DBUSMENUQT_VERSION_MINOR @dbusmenu_qt_VERSION_MINOR@
+#define DBUSMENUQT_VERSION_PATCH @dbusmenu_qt_VERSION_PATCH@
+
+#define DBUSMENUQT_MAKE_VERSION(a, b, c) (((a) << 16) | ((b) << 8) | (c))
+
+#define DBUSMENUQT_VERSION DBUSMENUQT_MAKE_VERSION( \
+ DBUSMENUQT_VERSION_MAJOR, \
+ DBUSMENUQT_VERSION_MINOR, \
+ DBUSMENUQT_VERSION_PATCH)
+
+// Use this macro to add code which depends on a minimum version of dbusmenu-qt
+#define DBUSMENUQT_IS_VERSION(a, b, c) \
+ (DBUSMENUQT_VERSION >= DBUSMENUQT_MAKE_VERSION(a, b, c))
+
+#endif /*DBUSMENUQT_VERSION_H */
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include "dbusmenuexporter.h"
+
+// Qt
+#include <QBuffer>
+#include <QDateTime>
+#include <QMap>
+#include <QMenu>
+#include <QSet>
+#include <QTimer>
+#include <QToolButton>
+#include <QWidgetAction>
+
+// Local
+#include "dbusmenu_config.h"
+#include "dbusmenu_p.h"
+#include "dbusmenuexporterdbus_p.h"
+#include "dbusmenuexporterprivate_p.h"
+#include "dbusmenutypes_p.h"
+#include "dbusmenushortcut_p.h"
+#include "debug_p.h"
+#include "utils_p.h"
+
+static const char *KMENU_TITLE = "kmenu_title";
+
+//-------------------------------------------------
+//
+// DBusMenuExporterPrivate
+//
+//-------------------------------------------------
+int DBusMenuExporterPrivate::idForAction(QAction *action) const
+{
+ DMRETURN_VALUE_IF_FAIL(action, -1);
+ return m_idForAction.value(action, -2);
+}
+
+void DBusMenuExporterPrivate::addMenu(QMenu *menu, int parentId)
+{
+ if (menu->findChild<DBusMenu *>()) {
+ // This can happen if a menu is removed from its parent and added back
+ // See KDE bug 254066
+ return;
+ }
+ new DBusMenu(menu, q, parentId);
+ Q_FOREACH(QAction *action, menu->actions()) {
+ addAction(action, parentId);
+ }
+}
+
+QVariantMap DBusMenuExporterPrivate::propertiesForAction(QAction *action) const
+{
+ DMRETURN_VALUE_IF_FAIL(action, QVariantMap());
+
+ if (action->objectName() == KMENU_TITLE) {
+ // Hack: Support for KDE menu titles in a Qt-only library...
+ return propertiesForKMenuTitleAction(action);
+ } else if (action->isSeparator()) {
+ return propertiesForSeparatorAction(action);
+ } else {
+ return propertiesForStandardAction(action);
+ }
+}
+
+QVariantMap DBusMenuExporterPrivate::propertiesForKMenuTitleAction(QAction *action_) const
+{
+ QVariantMap map;
+ // In case the other side does not know about x-kde-title, show a disabled item
+ map.insert("enabled", false);
+ map.insert("x-kde-title", true);
+
+ const QWidgetAction *widgetAction = qobject_cast<const QWidgetAction *>(action_);
+ DMRETURN_VALUE_IF_FAIL(widgetAction, map);
+ QToolButton *button = qobject_cast<QToolButton *>(widgetAction->defaultWidget());
+ DMRETURN_VALUE_IF_FAIL(button, map);
+ QAction *action = button->defaultAction();
+ DMRETURN_VALUE_IF_FAIL(action, map);
+
+ map.insert("label", swapMnemonicChar(action->text(), '&', '_'));
+ insertIconProperty(&map, action);
+ if (!action->isVisible()) {
+ map.insert("visible", false);
+ }
+ return map;
+}
+
+QVariantMap DBusMenuExporterPrivate::propertiesForSeparatorAction(QAction *action) const
+{
+ QVariantMap map;
+ map.insert("type", "separator");
+ if (!action->isVisible()) {
+ map.insert("visible", false);
+ }
+ return map;
+}
+
+QVariantMap DBusMenuExporterPrivate::propertiesForStandardAction(QAction *action) const
+{
+ QVariantMap map;
+ map.insert("label", swapMnemonicChar(action->text(), '&', '_'));
+ if (!action->isEnabled()) {
+ map.insert("enabled", false);
+ }
+ if (!action->isVisible()) {
+ map.insert("visible", false);
+ }
+ if (action->menu()) {
+ map.insert("children-display", "submenu");
+ }
+ if (action->isCheckable()) {
+ bool exclusive = action->actionGroup() && action->actionGroup()->isExclusive();
+ map.insert("toggle-type", exclusive ? "radio" : "checkmark");
+ map.insert("toggle-state", action->isChecked() ? 1 : 0);
+ }
+ insertIconProperty(&map, action);
+ QKeySequence keySequence = action->shortcut();
+ if (!keySequence.isEmpty()) {
+ DBusMenuShortcut shortcut = DBusMenuShortcut::fromKeySequence(keySequence);
+ map.insert("shortcut", QVariant::fromValue(shortcut));
+ }
+ return map;
+}
+
+QMenu *DBusMenuExporterPrivate::menuForId(int id) const
+{
+ if (id == 0) {
+ return m_rootMenu;
+ }
+ QAction *action = m_actionForId.value(id);
+ // Action may not be in m_actionForId if it has been deleted between the
+ // time it was announced by the exporter and the time the importer asks for
+ // it.
+ return action ? action->menu() : 0;
+}
+
+void DBusMenuExporterPrivate::fillLayoutItem(DBusMenuLayoutItem *item, QMenu *menu, int id, int depth, const QStringList &propertyNames)
+{
+ item->id = id;
+ item->properties = m_dbusObject->getProperties(id, propertyNames);
+
+ if (depth != 0 && menu) {
+ Q_FOREACH(QAction *action, menu->actions()) {
+ int actionId = m_idForAction.value(action, -1);
+ if (actionId == -1) {
+ DMWARNING << "No id for action";
+ continue;
+ }
+
+ DBusMenuLayoutItem child;
+ fillLayoutItem(&child, action->menu(), actionId, depth - 1, propertyNames);
+ item->children << child;
+ }
+ }
+}
+
+void DBusMenuExporterPrivate::updateAction(QAction *action)
+{
+ int id = idForAction(action);
+ if (m_itemUpdatedIds.contains(id)) {
+ return;
+ }
+ m_itemUpdatedIds << id;
+ m_itemUpdatedTimer->start();
+}
+
+void DBusMenuExporterPrivate::addAction(QAction *action, int parentId)
+{
+ int id = m_idForAction.value(action, -1);
+ if (id != -1) {
+ DMWARNING << "Already tracking action" << action->text() << "under id" << id;
+ return;
+ }
+ QVariantMap map = propertiesForAction(action);
+ id = m_nextId++;
+ QObject::connect(action, SIGNAL(destroyed(QObject*)), q, SLOT(slotActionDestroyed(QObject*)));
+ m_actionForId.insert(id, action);
+ m_idForAction.insert(action, id);
+ m_actionProperties.insert(action, map);
+ if (action->menu()) {
+ addMenu(action->menu(), id);
+ }
+ ++m_revision;
+ emitLayoutUpdated(parentId);
+}
+
+/**
+ * IMPORTANT: action might have already been destroyed when this method is
+ * called, so don't dereference the pointer (it is a QObject to avoid being
+ * tempted to dereference)
+ */
+void DBusMenuExporterPrivate::removeActionInternal(QObject *object)
+{
+ QAction* action = static_cast<QAction*>(object);
+ m_actionProperties.remove(action);
+ int id = m_idForAction.take(action);
+ m_actionForId.remove(id);
+}
+
+void DBusMenuExporterPrivate::removeAction(QAction *action, int parentId)
+{
+ removeActionInternal(action);
+ QObject::disconnect(action, SIGNAL(destroyed(QObject*)), q, SLOT(slotActionDestroyed(QObject*)));
+ ++m_revision;
+ emitLayoutUpdated(parentId);
+}
+
+void DBusMenuExporterPrivate::emitLayoutUpdated(int id)
+{
+ if (m_layoutUpdatedIds.contains(id)) {
+ return;
+ }
+ m_layoutUpdatedIds << id;
+ m_layoutUpdatedTimer->start();
+}
+
+void DBusMenuExporterPrivate::insertIconProperty(QVariantMap *map, QAction *action) const
+{
+ // provide the icon name for per-theme lookups
+ const QString iconName = q->iconNameForAction(action);
+ if (!iconName.isEmpty()) {
+ map->insert("icon-name", iconName);
+ }
+
+ // provide the serialized icon data in case the icon
+ // is unnamed or the name isn't supported by the theme
+ const QIcon icon = action->icon();
+ if (!icon.isNull()) {
+ QBuffer buffer;
+ icon.pixmap(16).save(&buffer, "PNG");
+ map->insert("icon-data", buffer.data());
+ }
+}
+
+static void collapseSeparator(QAction* action)
+{
+ action->setVisible(false);
+}
+
+// Unless the separatorsCollapsible property is set to false, Qt will get rid
+// of separators at the beginning and at the end of menus as well as collapse
+// multiple separators in the middle. For example, a menu like this:
+//
+// ---
+// Open
+// ---
+// ---
+// Quit
+// ---
+//
+// is displayed like this:
+//
+// Open
+// ---
+// Quit
+//
+// We fake this by setting separators invisible before exporting them.
+//
+// cf. https://bugs.launchpad.net/libdbusmenu-qt/+bug/793339
+void DBusMenuExporterPrivate::collapseSeparators(QMenu* menu)
+{
+ QList<QAction*> actions = menu->actions();
+ if (actions.isEmpty()) {
+ return;
+ }
+
+ QList<QAction*>::Iterator it, begin = actions.begin(), end = actions.end();
+
+ // Get rid of separators at end
+ it = end - 1;
+ for (; it != begin; --it) {
+ if ((*it)->isSeparator()) {
+ collapseSeparator(*it);
+ } else {
+ break;
+ }
+ }
+ // end now points after the last visible entry
+ end = it + 1;
+ it = begin;
+
+ // Get rid of separators at beginnning
+ for (; it != end; ++it) {
+ if ((*it)->isSeparator()) {
+ collapseSeparator(*it);
+ } else {
+ break;
+ }
+ }
+
+ // Collapse separators in between
+ bool previousWasSeparator = false;
+ for (; it != end; ++it) {
+ QAction* action = *it;
+ if (action->isSeparator()) {
+ if (previousWasSeparator) {
+ collapseSeparator(action);
+ } else {
+ previousWasSeparator = true;
+ }
+ } else {
+ previousWasSeparator = false;
+ }
+ }
+}
+
+//-------------------------------------------------
+//
+// DBusMenuExporter
+//
+//-------------------------------------------------
+DBusMenuExporter::DBusMenuExporter(const QString &objectPath, QMenu *menu, const QDBusConnection &_connection)
+: QObject(menu)
+, d(new DBusMenuExporterPrivate)
+{
+ d->q = this;
+ d->m_objectPath = objectPath;
+ d->m_rootMenu = menu;
+ d->m_nextId = 1;
+ d->m_revision = 1;
+ d->m_emittedLayoutUpdatedOnce = false;
+ d->m_itemUpdatedTimer = new QTimer(this);
+ d->m_layoutUpdatedTimer = new QTimer(this);
+ d->m_dbusObject = new DBusMenuExporterDBus(this);
+
+ d->addMenu(d->m_rootMenu, 0);
+
+ d->m_itemUpdatedTimer->setInterval(0);
+ d->m_itemUpdatedTimer->setSingleShot(true);
+ connect(d->m_itemUpdatedTimer, SIGNAL(timeout()), SLOT(doUpdateActions()));
+
+ d->m_layoutUpdatedTimer->setInterval(0);
+ d->m_layoutUpdatedTimer->setSingleShot(true);
+ connect(d->m_layoutUpdatedTimer, SIGNAL(timeout()), SLOT(doEmitLayoutUpdated()));
+
+ QDBusConnection connection(_connection);
+ connection.registerObject(objectPath, d->m_dbusObject, QDBusConnection::ExportAllContents);
+}
+
+DBusMenuExporter::~DBusMenuExporter()
+{
+ delete d;
+}
+
+void DBusMenuExporter::doUpdateActions()
+{
+ if (d->m_itemUpdatedIds.isEmpty()) {
+ return;
+ }
+ DBusMenuItemList updatedList;
+ DBusMenuItemKeysList removedList;
+
+ Q_FOREACH(int id, d->m_itemUpdatedIds) {
+ QAction *action = d->m_actionForId.value(id);
+ if (!action) {
+ // Action does not exist anymore
+ continue;
+ }
+
+ QVariantMap& oldProperties = d->m_actionProperties[action];
+ QVariantMap newProperties = d->propertiesForAction(action);
+ QVariantMap updatedProperties;
+ QStringList removedProperties;
+
+ // Find updated and removed properties
+ QVariantMap::ConstIterator newEnd = newProperties.constEnd();
+
+ QVariantMap::ConstIterator
+ oldIt = oldProperties.constBegin(),
+ oldEnd = oldProperties.constEnd();
+ for(; oldIt != oldEnd; ++oldIt) {
+ QString key = oldIt.key();
+ QVariantMap::ConstIterator newIt = newProperties.constFind(key);
+ if (newIt != newEnd) {
+ if (newIt.value() != oldIt.value()) {
+ updatedProperties.insert(key, newIt.value());
+ }
+ } else {
+ removedProperties << key;
+ }
+ }
+
+ // Find new properties (treat them as updated properties)
+ QVariantMap::ConstIterator newIt = newProperties.constBegin();
+ for (; newIt != newEnd; ++newIt) {
+ QString key = newIt.key();
+ oldIt = oldProperties.constFind(key);
+ if (oldIt == oldEnd) {
+ updatedProperties.insert(key, newIt.value());
+ }
+ }
+
+ // Update our data (oldProperties is a reference)
+ oldProperties = newProperties;
+ QMenu *menu = action->menu();
+ if (menu) {
+ d->addMenu(menu, id);
+ }
+
+ if (!updatedProperties.isEmpty()) {
+ DBusMenuItem item;
+ item.id = id;
+ item.properties = updatedProperties;
+ updatedList << item;
+ }
+ if (!removedProperties.isEmpty()) {
+ DBusMenuItemKeys itemKeys;
+ itemKeys.id = id;
+ itemKeys.properties = removedProperties;
+ removedList << itemKeys;
+ }
+ }
+ d->m_itemUpdatedIds.clear();
+ if (!d->m_emittedLayoutUpdatedOnce) {
+ // No need to tell the world about action changes: nobody knows the
+ // menu layout so nobody knows about the actions.
+ // Note: We can't stop in DBusMenuExporterPrivate::addAction(), we
+ // still need to reach this method because we want our properties to be
+ // updated, even if we don't announce changes.
+ return;
+ }
+ if (!updatedList.isEmpty() || !removedList.isEmpty()) {
+ d->m_dbusObject->ItemsPropertiesUpdated(updatedList, removedList);
+ }
+}
+
+void DBusMenuExporter::doEmitLayoutUpdated()
+{
+ // Collapse separators for all updated menus
+ Q_FOREACH(int id, d->m_layoutUpdatedIds) {
+ QMenu* menu = d->menuForId(id);
+ if (menu && menu->separatorsCollapsible()) {
+ d->collapseSeparators(menu);
+ }
+ }
+
+ // Tell the world about the update
+ if (d->m_emittedLayoutUpdatedOnce) {
+ Q_FOREACH(int id, d->m_layoutUpdatedIds) {
+ d->m_dbusObject->LayoutUpdated(d->m_revision, id);
+ }
+ } else {
+ // First time we emit LayoutUpdated, no need to emit several layout
+ // updates, signals the whole layout (id==0) has been updated
+ d->m_dbusObject->LayoutUpdated(d->m_revision, 0);
+ d->m_emittedLayoutUpdatedOnce = true;
+ }
+ d->m_layoutUpdatedIds.clear();
+}
+
+QString DBusMenuExporter::iconNameForAction(QAction *action)
+{
+ DMRETURN_VALUE_IF_FAIL(action, QString());
+#ifdef HAVE_QICON_NAME
+ QIcon icon = action->icon();
+ if (action->isIconVisibleInMenu() && !icon.isNull()) {
+ return icon.name();
+ } else {
+ return QString();
+ }
+#else
+ return QString();
+#endif
+}
+
+void DBusMenuExporter::activateAction(QAction *action)
+{
+ int id = d->idForAction(action);
+ DMRETURN_IF_FAIL(id >= 0);
+ uint timeStamp = QDateTime::currentDateTime().toTime_t();
+ d->m_dbusObject->ItemActivationRequested(id, timeStamp);
+}
+
+void DBusMenuExporter::slotActionDestroyed(QObject* object)
+{
+ d->removeActionInternal(object);
+}
+
+void DBusMenuExporter::setStatus(const QString& status)
+{
+ d->m_dbusObject->setStatus(status);
+}
+
+QString DBusMenuExporter::status() const
+{
+ return d->m_dbusObject->status();
+}
+
+#include "dbusmenuexporter.moc"
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENUEXPORTER_H
+#define DBUSMENUEXPORTER_H
+
+// Qt
+#include <QtCore/QObject>
+#include <QtDBus/QDBusConnection>
+
+// Local
+#include <dbusmenu_export.h>
+
+class QAction;
+class QMenu;
+
+class DBusMenuExporterPrivate;
+
+/**
+ * A DBusMenuExporter instance can serialize a menu over DBus
+ */
+class DBUSMENU_EXPORT DBusMenuExporter : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * Creates a DBusMenuExporter exporting menu at the dbus object path
+ * dbusObjectPath, using the given dbusConnection.
+ * The instance adds itself to the menu children.
+ */
+ DBusMenuExporter(const QString &dbusObjectPath, QMenu *menu, const QDBusConnection &dbusConnection = QDBusConnection::sessionBus());
+
+ virtual ~DBusMenuExporter();
+
+ /**
+ * Asks the matching DBusMenuImporter to activate @p action. For menus it
+ * means popup them, for items it means triggering the associated action.
+ */
+ void activateAction(QAction *action);
+
+ /**
+ * The status of the menu. Can be one of "normal" or "notice". This can be
+ * used to notify the other side the menu should be made more visible.
+ * For example, appmenu uses it to tell Unity panel to show/hide the menubar
+ * when the Alt modifier is pressed/released.
+ */
+ void setStatus(const QString &status);
+
+ /**
+ * Returns the status of the menu.
+ * @ref setStatus
+ */
+ QString status() const;
+
+protected:
+ /**
+ * Must extract the icon name for action. This is the name which will
+ * be used to present the icon over DBus.
+ * Default implementation returns action->icon().name() when built on Qt
+ * >= 4.7 and a null string otherwise.
+ */
+ virtual QString iconNameForAction(QAction *action);
+
+private Q_SLOTS:
+ void doUpdateActions();
+ void doEmitLayoutUpdated();
+ void slotActionDestroyed(QObject*);
+
+private:
+ Q_DISABLE_COPY(DBusMenuExporter)
+ DBusMenuExporterPrivate *const d;
+
+ friend class DBusMenuExporterPrivate;
+ friend class DBusMenuExporterDBus;
+ friend class DBusMenu;
+};
+
+#endif /* DBUSMENUEXPORTER_H */
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include "dbusmenuexporterdbus_p.h"
+
+// Qt
+#include <QDBusMessage>
+#include <QMenu>
+#include <QVariant>
+
+// Local
+#include "dbusmenuadaptor.h"
+#include "dbusmenuexporterprivate_p.h"
+#include "dbusmenushortcut_p.h"
+#include "debug_p.h"
+
+static const char *DBUSMENU_INTERFACE = "com.canonical.dbusmenu";
+static const char *FDO_PROPERTIES_INTERFACE = "org.freedesktop.DBus.Properties";
+
+DBusMenuExporterDBus::DBusMenuExporterDBus(DBusMenuExporter *exporter)
+: QObject(exporter)
+, m_exporter(exporter)
+, m_status("normal")
+{
+ DBusMenuTypes_register();
+ new DbusmenuAdaptor(this);
+}
+
+uint DBusMenuExporterDBus::GetLayout(int parentId, int recursionDepth, const QStringList &propertyNames, DBusMenuLayoutItem &item)
+{
+ QMenu *menu = m_exporter->d->menuForId(parentId);
+ DMRETURN_VALUE_IF_FAIL(menu, 0);
+
+ // Process pending actions, we need them *now*
+ QMetaObject::invokeMethod(m_exporter, "doUpdateActions");
+ m_exporter->d->fillLayoutItem(&item, menu, parentId, recursionDepth, propertyNames);
+
+ return m_exporter->d->m_revision;
+}
+
+void DBusMenuExporterDBus::Event(int id, const QString &eventType, const QDBusVariant &/*data*/, uint /*timestamp*/)
+{
+ if (eventType == "clicked") {
+ QAction *action = m_exporter->d->m_actionForId.value(id);
+ if (!action) {
+ return;
+ }
+ // dbusmenu-glib seems to ignore the Q_NOREPLY and blocks when calling
+ // Event(), so trigger the action asynchronously
+ QMetaObject::invokeMethod(action, "trigger", Qt::QueuedConnection);
+ } else if (eventType == "hovered") {
+ QMenu *menu = m_exporter->d->menuForId(id);
+ if (menu) {
+ QMetaObject::invokeMethod(menu, "aboutToShow");
+ }
+ }
+}
+
+QDBusVariant DBusMenuExporterDBus::GetProperty(int id, const QString &name)
+{
+ QAction *action = m_exporter->d->m_actionForId.value(id);
+ DMRETURN_VALUE_IF_FAIL(action, QDBusVariant());
+ return QDBusVariant(m_exporter->d->m_actionProperties.value(action).value(name));
+}
+
+QVariantMap DBusMenuExporterDBus::getProperties(int id, const QStringList &names) const
+{
+ if (id == 0) {
+ QVariantMap map;
+ map.insert("children-display", "submenu");
+ return map;
+ }
+ QAction *action = m_exporter->d->m_actionForId.value(id);
+ DMRETURN_VALUE_IF_FAIL(action, QVariantMap());
+ QVariantMap all = m_exporter->d->m_actionProperties.value(action);
+ if (names.isEmpty()) {
+ return all;
+ } else {
+ QVariantMap map;
+ Q_FOREACH(const QString &name, names) {
+ QVariant value = all.value(name);
+ if (value.isValid()) {
+ map.insert(name, value);
+ }
+ }
+ return map;
+ }
+}
+
+DBusMenuItemList DBusMenuExporterDBus::GetGroupProperties(const QList<int> &ids, const QStringList &names)
+{
+ DBusMenuItemList list;
+ Q_FOREACH(int id, ids) {
+ DBusMenuItem item;
+ item.id = id;
+ item.properties = getProperties(item.id, names);
+ list << item;
+ }
+ return list;
+}
+
+/**
+ * An helper class for ::AboutToShow, which sets mChanged to true if a menu
+ * changes after its aboutToShow() signal has been emitted.
+ */
+class ActionEventFilter: public QObject
+{
+public:
+ ActionEventFilter()
+ : mChanged(false)
+ {}
+
+ bool mChanged;
+protected:
+ bool eventFilter(QObject *object, QEvent *event)
+ {
+ switch (event->type()) {
+ case QEvent::ActionAdded:
+ case QEvent::ActionChanged:
+ case QEvent::ActionRemoved:
+ mChanged = true;
+ // We noticed a change, no need to filter anymore
+ object->removeEventFilter(this);
+ break;
+ default:
+ break;
+ }
+ return false;
+ }
+};
+
+bool DBusMenuExporterDBus::AboutToShow(int id)
+{
+ QMenu *menu = m_exporter->d->menuForId(id);
+ DMRETURN_VALUE_IF_FAIL(menu, false);
+
+ ActionEventFilter filter;
+ menu->installEventFilter(&filter);
+ QMetaObject::invokeMethod(menu, "aboutToShow");
+ return filter.mChanged;
+}
+
+void DBusMenuExporterDBus::setStatus(const QString& status)
+{
+ if (m_status == status) {
+ return;
+ }
+ m_status = status;
+
+ QVariantMap map;
+ map.insert("Status", QVariant(status));
+
+ QDBusMessage msg = QDBusMessage::createSignal(m_exporter->d->m_objectPath, FDO_PROPERTIES_INTERFACE, "PropertiesChanged");
+ QVariantList args = QVariantList()
+ << DBUSMENU_INTERFACE
+ << map
+ << QStringList() // New properties: none
+ ;
+ msg.setArguments(args);
+ QDBusConnection::sessionBus().send(msg);
+}
+
+QString DBusMenuExporterDBus::status() const
+{
+ return m_status;
+}
+
+
+#include "dbusmenuexporterdbus_p.moc"
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENUEXPORTERDBUS_P_H
+#define DBUSMENUEXPORTERDBUS_P_H
+
+// Local
+#include <dbusmenutypes_p.h>
+
+// Qt
+#include <QtCore/QObject>
+#include <QtCore/QVariant>
+#include <QtDBus/QDBusAbstractAdaptor>
+#include <QtDBus/QDBusVariant>
+
+class DBusMenuExporter;
+
+/**
+ * Internal class implementing the DBus side of DBusMenuExporter
+ * This avoid exposing the implementation of the DBusMenu spec to the outside
+ * world.
+ */
+class DBusMenuExporterDBus : public QObject
+{
+ Q_OBJECT
+ Q_CLASSINFO("D-Bus Interface", "com.canonical.dbusmenu")
+ Q_PROPERTY(uint Version READ Version)
+ Q_PROPERTY(QString Status READ status)
+public:
+ DBusMenuExporterDBus(DBusMenuExporter *m_exporter);
+
+ uint Version() const { return 2; }
+
+ QString status() const;
+ void setStatus(const QString &status);
+
+public Q_SLOTS:
+ Q_NOREPLY void Event(int id, const QString &eventId, const QDBusVariant &data, uint timestamp);
+ QDBusVariant GetProperty(int id, const QString &property);
+ uint GetLayout(int parentId, int recursionDepth, const QStringList &propertyNames, DBusMenuLayoutItem &item);
+ DBusMenuItemList GetGroupProperties(const QList<int> &ids, const QStringList &propertyNames);
+ bool AboutToShow(int id);
+
+Q_SIGNALS:
+ void ItemsPropertiesUpdated(DBusMenuItemList, DBusMenuItemKeysList);
+ void LayoutUpdated(uint revision, int parentId);
+ void ItemActivationRequested(int id, uint timeStamp);
+
+private:
+ DBusMenuExporter *m_exporter;
+ QString m_status;
+
+ friend class DBusMenuExporter;
+ friend class DBusMenuExporterPrivate;
+
+ QVariantMap getProperties(int id, const QStringList &names) const;
+};
+
+#endif /* DBUSMENUEXPORTERDBUS_P_H */
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENUEXPORTERPRIVATE_P_H
+#define DBUSMENUEXPORTERPRIVATE_P_H
+
+// Local
+#include "dbusmenuexporter.h"
+#include "dbusmenutypes_p.h"
+
+// Qt
+#include <QtCore/QHash>
+#include <QtCore/QMap>
+#include <QtCore/QSet>
+#include <QtCore/QVariant>
+
+class QMenu;
+
+class DBusMenuExporterDBus;
+
+class DBusMenuExporterPrivate
+{
+public:
+ DBusMenuExporter *q;
+
+ QString m_objectPath;
+
+ DBusMenuExporterDBus *m_dbusObject;
+
+ QMenu *m_rootMenu;
+ QHash<QAction *, QVariantMap> m_actionProperties;
+ QMap<int, QAction *> m_actionForId;
+ QMap<QAction *, int> m_idForAction;
+ int m_nextId;
+ uint m_revision;
+ bool m_emittedLayoutUpdatedOnce;
+
+ QSet<int> m_itemUpdatedIds;
+ QTimer *m_itemUpdatedTimer;
+
+ QSet<int> m_layoutUpdatedIds;
+ QTimer *m_layoutUpdatedTimer;
+
+ int idForAction(QAction *action) const;
+ void addMenu(QMenu *menu, int parentId);
+ QVariantMap propertiesForAction(QAction *action) const;
+ QVariantMap propertiesForKMenuTitleAction(QAction *action_) const;
+ QVariantMap propertiesForSeparatorAction(QAction *action) const;
+ QVariantMap propertiesForStandardAction(QAction *action) const;
+ QMenu *menuForId(int id) const;
+ void fillLayoutItem(DBusMenuLayoutItem *item, QMenu *menu, int id, int depth, const QStringList &propertyNames);
+
+ void addAction(QAction *action, int parentId);
+ void updateAction(QAction *action);
+ void removeAction(QAction *action, int parentId);
+ /**
+ * Removes any reference from action in the exporter, but do not notify the
+ * change outside. This is useful when a submenu is destroyed because we do
+ * not receive QEvent::ActionRemoved events for its actions.
+ * IMPORTANT: action might have already been destroyed when this method is
+ * called, so don't dereference the pointer (it is a QObject to avoid being
+ * tempted to dereference)
+ */
+ void removeActionInternal(QObject *action);
+
+ void emitLayoutUpdated(int id);
+
+ void insertIconProperty(QVariantMap* map, QAction *action) const;
+
+ void collapseSeparators(QMenu*);
+};
+
+
+#endif /* DBUSMENUEXPORTERPRIVATE_P_H */
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include "dbusmenuimporter.h"
+
+// Qt
+#include <QCoreApplication>
+#include <QDBusConnection>
+#include <QDBusInterface>
+#include <QDBusReply>
+#include <QDBusVariant>
+#include <QFont>
+#include <QMenu>
+#include <QPointer>
+#include <QSignalMapper>
+#include <QTime>
+#include <QTimer>
+#include <QToolButton>
+#include <QWidgetAction>
+
+// Local
+#include "dbusmenutypes_p.h"
+#include "dbusmenushortcut_p.h"
+#include "debug_p.h"
+#include "utils_p.h"
+
+//#define BENCHMARK
+#ifdef BENCHMARK
+#include <QTime>
+static QTime sChrono;
+#endif
+
+static const char *DBUSMENU_INTERFACE = "com.canonical.dbusmenu";
+
+static const int ABOUT_TO_SHOW_TIMEOUT = 3000;
+static const int REFRESH_TIMEOUT = 4000;
+
+static const char *DBUSMENU_PROPERTY_ID = "_dbusmenu_id";
+static const char *DBUSMENU_PROPERTY_ICON_NAME = "_dbusmenu_icon_name";
+static const char *DBUSMENU_PROPERTY_ICON_DATA_HASH = "_dbusmenu_icon_data_hash";
+
+static QAction *createKdeTitle(QAction *action, QWidget *parent)
+{
+ QToolButton *titleWidget = new QToolButton(0);
+ QFont font = titleWidget->font();
+ font.setBold(true);
+ titleWidget->setFont(font);
+ titleWidget->setIcon(action->icon());
+ titleWidget->setText(action->text());
+ titleWidget->setDown(true);
+ titleWidget->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
+
+ QWidgetAction *titleAction = new QWidgetAction(parent);
+ titleAction->setDefaultWidget(titleWidget);
+ return titleAction;
+}
+
+class DBusMenuImporterPrivate
+{
+public:
+ DBusMenuImporter *q;
+
+ QDBusAbstractInterface *m_interface;
+ QMenu *m_menu;
+ typedef QMap<int, QPointer<QAction> > ActionForId;
+ ActionForId m_actionForId;
+ QSignalMapper m_mapper;
+ QTimer *m_pendingLayoutUpdateTimer;
+
+ QSet<int> m_idsRefreshedByAboutToShow;
+ QSet<int> m_pendingLayoutUpdates;
+
+ bool m_mustEmitMenuUpdated;
+
+ DBusMenuImporterType m_type;
+
+ QDBusPendingCallWatcher *refresh(int id)
+ {
+ #ifdef BENCHMARK
+ DMDEBUG << "Starting refresh chrono for id" << id;
+ sChrono.start();
+ #endif
+ QDBusPendingCall call = m_interface->asyncCall("GetLayout", id, 1, QStringList());
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, q);
+ watcher->setProperty(DBUSMENU_PROPERTY_ID, id);
+ QObject::connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ q, SLOT(slotGetLayoutFinished(QDBusPendingCallWatcher*)));
+
+ return watcher;
+ }
+
+ QMenu *createMenu(QWidget *parent)
+ {
+ QMenu *menu = q->createMenu(parent);
+ QObject::connect(menu, SIGNAL(aboutToShow()),
+ q, SLOT(slotMenuAboutToShow()));
+ QObject::connect(menu, SIGNAL(aboutToHide()),
+ q, SLOT(slotMenuAboutToHide()));
+ return menu;
+ }
+
+ /**
+ * Init all the immutable action properties here
+ * TODO: Document immutable properties?
+ *
+ * Note: we remove properties we handle from the map (using QMap::take()
+ * instead of QMap::value()) to avoid warnings about these properties in
+ * updateAction()
+ */
+ QAction *createAction(int id, const QVariantMap &_map, QWidget *parent)
+ {
+ QVariantMap map = _map;
+ QAction *action = new QAction(parent);
+ action->setProperty(DBUSMENU_PROPERTY_ID, id);
+
+ QString type = map.take("type").toString();
+ if (type == "separator") {
+ action->setSeparator(true);
+ }
+
+ if (map.take("children-display").toString() == "submenu") {
+ QMenu *menu = createMenu(parent);
+ action->setMenu(menu);
+ }
+
+ QString toggleType = map.take("toggle-type").toString();
+ if (!toggleType.isEmpty()) {
+ action->setCheckable(true);
+ if (toggleType == "radio") {
+ QActionGroup *group = new QActionGroup(action);
+ group->addAction(action);
+ }
+ }
+
+ bool isKdeTitle = map.take("x-kde-title").toBool();
+ updateAction(action, map, map.keys());
+
+ if (isKdeTitle) {
+ action = createKdeTitle(action, parent);
+ }
+
+ return action;
+ }
+
+ /**
+ * Update mutable properties of an action. A property may be listed in
+ * requestedProperties but not in map, this means we should use the default value
+ * for this property.
+ *
+ * @param action the action to update
+ * @param map holds the property values
+ * @param requestedProperties which properties has been requested
+ */
+ void updateAction(QAction *action, const QVariantMap &map, const QStringList &requestedProperties)
+ {
+ Q_FOREACH(const QString &key, requestedProperties) {
+ updateActionProperty(action, key, map.value(key));
+ }
+ }
+
+ void updateActionProperty(QAction *action, const QString &key, const QVariant &value)
+ {
+ if (key == "label") {
+ updateActionLabel(action, value);
+ } else if (key == "enabled") {
+ updateActionEnabled(action, value);
+ } else if (key == "toggle-state") {
+ updateActionChecked(action, value);
+ } else if (key == "icon-name") {
+ updateActionIconByName(action, value);
+ } else if (key == "icon-data") {
+ updateActionIconByData(action, value);
+ } else if (key == "visible") {
+ updateActionVisible(action, value);
+ } else if (key == "shortcut") {
+ updateActionShortcut(action, value);
+ } else if (key == "children-display") {
+ } else {
+ DMWARNING << "Unhandled property update" << key;
+ }
+ }
+
+ void updateActionLabel(QAction *action, const QVariant &value)
+ {
+ QString text = swapMnemonicChar(value.toString(), '_', '&');
+ action->setText(text);
+ }
+
+ void updateActionEnabled(QAction *action, const QVariant &value)
+ {
+ action->setEnabled(value.isValid() ? value.toBool(): true);
+ }
+
+ void updateActionChecked(QAction *action, const QVariant &value)
+ {
+ if (action->isCheckable() && value.isValid()) {
+ action->setChecked(value.toInt() == 1);
+ }
+ }
+
+ void updateActionIconByName(QAction *action, const QVariant &value)
+ {
+ QString iconName = value.toString();
+ QString previous = action->property(DBUSMENU_PROPERTY_ICON_NAME).toString();
+ if (previous == iconName) {
+ return;
+ }
+ action->setProperty(DBUSMENU_PROPERTY_ICON_NAME, iconName);
+ if (iconName.isEmpty()) {
+ action->setIcon(QIcon());
+ return;
+ }
+ action->setIcon(q->iconForName(iconName));
+ }
+
+ void updateActionIconByData(QAction *action, const QVariant &value)
+ {
+ QByteArray data = value.toByteArray();
+ uint dataHash = qHash(data);
+ uint previousDataHash = action->property(DBUSMENU_PROPERTY_ICON_DATA_HASH).toUInt();
+ if (previousDataHash == dataHash) {
+ return;
+ }
+ action->setProperty(DBUSMENU_PROPERTY_ICON_DATA_HASH, dataHash);
+ QPixmap pix;
+ if (!pix.loadFromData(data)) {
+ DMWARNING << "Failed to decode icon-data property for action" << action->text();
+ action->setIcon(QIcon());
+ return;
+ }
+ action->setIcon(QIcon(pix));
+ }
+
+ void updateActionVisible(QAction *action, const QVariant &value)
+ {
+ action->setVisible(value.isValid() ? value.toBool() : true);
+ }
+
+ void updateActionShortcut(QAction *action, const QVariant &value)
+ {
+ QDBusArgument arg = value.value<QDBusArgument>();
+ DBusMenuShortcut dmShortcut;
+ arg >> dmShortcut;
+ QKeySequence keySequence = dmShortcut.toKeySequence();
+ action->setShortcut(keySequence);
+ }
+
+ QMenu *menuForId(int id) const
+ {
+ if (id == 0) {
+ return q->menu();
+ }
+ QAction *action = m_actionForId.value(id);
+ if (!action) {
+ return 0;
+ }
+ return action->menu();
+ }
+
+ void slotItemsPropertiesUpdated(const DBusMenuItemList &updatedList, const DBusMenuItemKeysList &removedList);
+
+ void sendEvent(int id, const QString &eventId)
+ {
+ QVariant empty = QVariant::fromValue(QDBusVariant(QString()));
+ m_interface->asyncCall("Event", id, eventId, empty, 0u);
+ }
+
+ bool waitForWatcher(QDBusPendingCallWatcher * _watcher, int maxWait)
+ {
+ QPointer<QDBusPendingCallWatcher> watcher(_watcher);
+
+ if(m_type == ASYNCHRONOUS) {
+ QTimer timer;
+ timer.setSingleShot(true);
+ QEventLoop loop;
+ loop.connect(&timer, SIGNAL(timeout()), SLOT(quit()));
+ loop.connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher *)), SLOT(quit()));
+ timer.start(maxWait);
+ loop.exec();
+ timer.stop();
+
+ if (!watcher) {
+ // Watcher died. This can happen if importer got deleted while we were
+ // waiting. See:
+ // https://bugs.kde.org/show_bug.cgi?id=237156
+ return false;
+ }
+
+ if(!watcher->isFinished()) {
+ // Timed out
+ return false;
+ }
+ } else {
+ watcher->waitForFinished();
+ }
+
+ if (watcher->isError()) {
+ DMWARNING << watcher->error().message();
+ return false;
+ }
+
+ return true;
+ }
+};
+
+DBusMenuImporter::DBusMenuImporter(const QString &service, const QString &path, QObject *parent)
+: DBusMenuImporter(service, path, ASYNCHRONOUS, parent)
+{
+}
+
+DBusMenuImporter::DBusMenuImporter(const QString &service, const QString &path, DBusMenuImporterType type, QObject *parent)
+: QObject(parent)
+, d(new DBusMenuImporterPrivate)
+{
+ DBusMenuTypes_register();
+
+ d->q = this;
+ d->m_interface = new QDBusInterface(service, path, DBUSMENU_INTERFACE, QDBusConnection::sessionBus(), this);
+ d->m_menu = 0;
+ d->m_mustEmitMenuUpdated = false;
+
+ d->m_type = type;
+
+ connect(&d->m_mapper, SIGNAL(mapped(int)), SLOT(sendClickedEvent(int)));
+
+ d->m_pendingLayoutUpdateTimer = new QTimer(this);
+ d->m_pendingLayoutUpdateTimer->setSingleShot(true);
+ connect(d->m_pendingLayoutUpdateTimer, SIGNAL(timeout()), SLOT(processPendingLayoutUpdates()));
+
+ // For some reason, using QObject::connect() does not work but
+ // QDBusConnect::connect() does
+ QDBusConnection::sessionBus().connect(service, path, DBUSMENU_INTERFACE, "LayoutUpdated", "ui",
+ this, SLOT(slotLayoutUpdated(uint, int)));
+ QDBusConnection::sessionBus().connect(service, path, DBUSMENU_INTERFACE, "ItemsPropertiesUpdated", "a(ia{sv})a(ias)",
+ this, SLOT(slotItemsPropertiesUpdated(DBusMenuItemList, DBusMenuItemKeysList)));
+ QDBusConnection::sessionBus().connect(service, path, DBUSMENU_INTERFACE, "ItemActivationRequested", "iu",
+ this, SLOT(slotItemActivationRequested(int, uint)));
+
+ d->refresh(0);
+}
+
+DBusMenuImporter::~DBusMenuImporter()
+{
+ // Do not use "delete d->m_menu": even if we are being deleted we should
+ // leave enough time for the menu to finish what it was doing, for example
+ // if it was being displayed.
+ d->m_menu->deleteLater();
+ delete d;
+}
+
+void DBusMenuImporter::slotLayoutUpdated(uint revision, int parentId)
+{
+ if (d->m_idsRefreshedByAboutToShow.remove(parentId)) {
+ return;
+ }
+ d->m_pendingLayoutUpdates << parentId;
+ if (!d->m_pendingLayoutUpdateTimer->isActive()) {
+ d->m_pendingLayoutUpdateTimer->start();
+ }
+}
+
+void DBusMenuImporter::processPendingLayoutUpdates()
+{
+ QSet<int> ids = d->m_pendingLayoutUpdates;
+ d->m_pendingLayoutUpdates.clear();
+ Q_FOREACH(int id, ids) {
+ d->refresh(id);
+ }
+}
+
+QMenu *DBusMenuImporter::menu() const
+{
+ if (!d->m_menu) {
+ d->m_menu = d->createMenu(0);
+ }
+ return d->m_menu;
+}
+
+void DBusMenuImporterPrivate::slotItemsPropertiesUpdated(const DBusMenuItemList &updatedList, const DBusMenuItemKeysList &removedList)
+{
+ Q_FOREACH(const DBusMenuItem &item, updatedList) {
+ QAction *action = m_actionForId.value(item.id);
+ if (!action) {
+ // We don't know this action. It probably is in a menu we haven't fetched yet.
+ continue;
+ }
+
+ QVariantMap::ConstIterator
+ it = item.properties.constBegin(),
+ end = item.properties.constEnd();
+ for(; it != end; ++it) {
+ updateActionProperty(action, it.key(), it.value());
+ }
+ }
+
+ Q_FOREACH(const DBusMenuItemKeys &item, removedList) {
+ QAction *action = m_actionForId.value(item.id);
+ if (!action) {
+ // We don't know this action. It probably is in a menu we haven't fetched yet.
+ continue;
+ }
+
+ Q_FOREACH(const QString &key, item.properties) {
+ updateActionProperty(action, key, QVariant());
+ }
+ }
+}
+
+void DBusMenuImporter::slotItemActivationRequested(int id, uint /*timestamp*/)
+{
+ QAction *action = d->m_actionForId.value(id);
+ DMRETURN_IF_FAIL(action);
+ actionActivationRequested(action);
+}
+
+void DBusMenuImporter::slotGetLayoutFinished(QDBusPendingCallWatcher *watcher)
+{
+ int parentId = watcher->property(DBUSMENU_PROPERTY_ID).toInt();
+ watcher->deleteLater();
+
+ QDBusPendingReply<uint, DBusMenuLayoutItem> reply = *watcher;
+ if (!reply.isValid()) {
+ DMWARNING << reply.error().message();
+ return;
+ }
+
+ #ifdef BENCHMARK
+ DMDEBUG << "- items received:" << sChrono.elapsed() << "ms";
+ #endif
+ DBusMenuLayoutItem rootItem = reply.argumentAt<1>();
+
+ QMenu *menu = d->menuForId(parentId);
+ if (!menu) {
+ DMWARNING << "No menu for id" << parentId;
+ return;
+ }
+
+ menu->clear();
+
+ Q_FOREACH(const DBusMenuLayoutItem &dbusMenuItem, rootItem.children) {
+ QAction *action = d->createAction(dbusMenuItem.id, dbusMenuItem.properties, menu);
+ DBusMenuImporterPrivate::ActionForId::Iterator it = d->m_actionForId.find(dbusMenuItem.id);
+ if (it == d->m_actionForId.end()) {
+ d->m_actionForId.insert(dbusMenuItem.id, action);
+ } else {
+ delete *it;
+ *it = action;
+ }
+ menu->addAction(action);
+
+ connect(action, SIGNAL(triggered()),
+ &d->m_mapper, SLOT(map()));
+ d->m_mapper.setMapping(action, dbusMenuItem.id);
+
+ if( action->menu() )
+ {
+ d->refresh( dbusMenuItem.id )->waitForFinished();
+ }
+ }
+ #ifdef BENCHMARK
+ DMDEBUG << "- Menu filled:" << sChrono.elapsed() << "ms";
+ #endif
+}
+
+void DBusMenuImporter::sendClickedEvent(int id)
+{
+ d->sendEvent(id, QString("clicked"));
+}
+
+void DBusMenuImporter::updateMenu()
+{
+ d->m_mustEmitMenuUpdated = true;
+ QMetaObject::invokeMethod(menu(), "aboutToShow");
+}
+
+void DBusMenuImporter::slotMenuAboutToShow()
+{
+ QMenu *menu = qobject_cast<QMenu*>(sender());
+ Q_ASSERT(menu);
+
+ QAction *action = menu->menuAction();
+ Q_ASSERT(action);
+
+ int id = action->property(DBUSMENU_PROPERTY_ID).toInt();
+
+ #ifdef BENCHMARK
+ QTime time;
+ time.start();
+ #endif
+
+ QDBusPendingCall call = d->m_interface->asyncCall("AboutToShow", id);
+ QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(call, this);
+ watcher->setProperty(DBUSMENU_PROPERTY_ID, id);
+ connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher*)),
+ SLOT(slotAboutToShowDBusCallFinished(QDBusPendingCallWatcher*)));
+
+ QPointer<QObject> guard(this);
+
+ if (!d->waitForWatcher(watcher, ABOUT_TO_SHOW_TIMEOUT)) {
+ DMWARNING << "Application did not answer to AboutToShow() before timeout";
+ }
+
+ #ifdef BENCHMARK
+ DMVAR(time.elapsed());
+ #endif
+ // "this" got deleted during the call to waitForWatcher(), get out
+ if (!guard) {
+ return;
+ }
+
+ if (menu == d->m_menu && d->m_mustEmitMenuUpdated) {
+ d->m_mustEmitMenuUpdated = false;
+ menuUpdated();
+ }
+ if (menu == d->m_menu) {
+ menuReadyToBeShown();
+ }
+
+ d->sendEvent(id, QString("opened"));
+}
+
+void DBusMenuImporter::slotAboutToShowDBusCallFinished(QDBusPendingCallWatcher *watcher)
+{
+ int id = watcher->property(DBUSMENU_PROPERTY_ID).toInt();
+ watcher->deleteLater();
+
+ QDBusPendingReply<bool> reply = *watcher;
+ if (reply.isError()) {
+ DMWARNING << "Call to AboutToShow() failed:" << reply.error().message();
+ return;
+ }
+ bool needRefresh = reply.argumentAt<0>();
+
+ QMenu *menu = d->menuForId(id);
+ DMRETURN_IF_FAIL(menu);
+
+ if (needRefresh || menu->actions().isEmpty()) {
+ d->m_idsRefreshedByAboutToShow << id;
+ QDBusPendingCallWatcher *watcher2 = d->refresh(id);
+ if (!d->waitForWatcher(watcher2, REFRESH_TIMEOUT)) {
+ DMWARNING << "Application did not refresh before timeout";
+ }
+ }
+}
+
+void DBusMenuImporter::slotMenuAboutToHide()
+{
+ QMenu *menu = qobject_cast<QMenu*>(sender());
+ Q_ASSERT(menu);
+
+ QAction *action = menu->menuAction();
+ Q_ASSERT(action);
+
+ int id = action->property(DBUSMENU_PROPERTY_ID).toInt();
+ d->sendEvent(id, QString("closed"));
+}
+
+QMenu *DBusMenuImporter::createMenu(QWidget *parent)
+{
+ return new QMenu(parent);
+}
+
+QIcon DBusMenuImporter::iconForName(const QString &/*name*/)
+{
+ return QIcon();
+}
+
+#include "dbusmenuimporter.moc"
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENUIMPORTER_H
+#define DBUSMENUIMPORTER_H
+
+// Qt
+#include <QtCore/QObject>
+
+// Local
+#include <dbusmenu_export.h>
+
+class QAction;
+class QDBusAbstractInterface;
+class QDBusPendingCallWatcher;
+class QDBusVariant;
+class QIcon;
+class QMenu;
+
+class DBusMenuImporterPrivate;
+
+/**
+ * Determine whether internal method calls should allow the Qt event loop
+ * to execute or not
+ */
+enum DBusMenuImporterType {
+ ASYNCHRONOUS,
+ SYNCHRONOUS
+};
+
+/**
+ * A DBusMenuImporter instance can recreate a menu serialized over DBus by
+ * DBusMenuExporter
+ */
+class DBUSMENU_EXPORT DBusMenuImporter : public QObject
+{
+ Q_OBJECT
+public:
+ /**
+ * Creates a DBusMenuImporter listening over DBus on service, path
+ */
+ DBusMenuImporter(const QString &service, const QString &path, QObject *parent = 0);
+
+ /**
+ * Creates a DBusMenuImporter listening over DBus on service, path, with either async
+ * or sync DBus calls
+ */
+ DBusMenuImporter(const QString &service, const QString &path, DBusMenuImporterType type, QObject *parent = 0);
+
+ virtual ~DBusMenuImporter();
+
+ /**
+ * The menu created from listening to the DBusMenuExporter over DBus
+ */
+ QMenu *menu() const;
+
+public Q_SLOTS:
+ /**
+ * Simulates a QMenu::aboutToShow() signal on the menu returned by menu(),
+ * ensuring it is up to date in case the menu is populated on the fly. It
+ * is not mandatory to call this method, showing the menu with
+ * QMenu::popup() or QMenu::exec() will generates an aboutToShow() signal,
+ * but calling it before ensures the size-hint of the menu is correct when
+ * it is time to show it, avoiding wrong positioning.
+ *
+ * menuUpdated() will be emitted when the menu is ready.
+ *
+ * Not that the aboutToShow() signal is only sent to the root menu, not to
+ * any submenu.
+ */
+ void updateMenu();
+
+Q_SIGNALS:
+ /**
+ * Emitted after a call to updateMenu().
+ * @see updateMenu()
+ */
+ void menuUpdated();
+
+ /**
+ * Emitted after every aboutToShow of the root menu.
+ * This signal is deprecated and only kept to keep compatibility with
+ * dbusmenu-qt 0.3.x. New code should use updateMenu() and menuUpdated()
+ *
+ * @deprecated
+ */
+ void menuReadyToBeShown();
+
+ /**
+ * Emitted when the exporter was asked to activate an action
+ */
+ void actionActivationRequested(QAction *);
+
+protected:
+ /**
+ * Must create a menu, may be customized to fit host appearance.
+ * Default implementation creates a simple QMenu.
+ */
+ virtual QMenu *createMenu(QWidget *parent);
+
+ /**
+ * Must convert a name into an icon.
+ * Default implementation returns a null icon.
+ */
+ virtual QIcon iconForName(const QString &);
+
+private Q_SLOTS:
+ void sendClickedEvent(int);
+ void slotMenuAboutToShow();
+ void slotMenuAboutToHide();
+ void slotAboutToShowDBusCallFinished(QDBusPendingCallWatcher *);
+ void slotItemActivationRequested(int id, uint timestamp);
+ void processPendingLayoutUpdates();
+ void slotLayoutUpdated(uint revision, int parentId);
+ void slotGetLayoutFinished(QDBusPendingCallWatcher *);
+
+private:
+ Q_DISABLE_COPY(DBusMenuImporter)
+ DBusMenuImporterPrivate *const d;
+ friend class DBusMenuImporterPrivate;
+
+ // Use Q_PRIVATE_SLOT to avoid exposing DBusMenuItemList
+ Q_PRIVATE_SLOT(d, void slotItemsPropertiesUpdated(const DBusMenuItemList &updatedList, const DBusMenuItemKeysList &removedList))
+};
+
+#endif /* DBUSMENUIMPORTER_H */
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include "dbusmenushortcut_p.h"
+
+// Qt
+#include <QtGui/QKeySequence>
+
+// Local
+#include "debug_p.h"
+
+static const int QT_COLUMN = 0;
+static const int DM_COLUMN = 1;
+
+static void processKeyTokens(QStringList* tokens, int srcCol, int dstCol)
+{
+ struct Row {
+ const char* zero;
+ const char* one;
+ const char* operator[](int col) const { return col == 0 ? zero : one; }
+ };
+ static const Row table[] =
+ { {"Meta", "Super"},
+ {"Ctrl", "Control"},
+ // Special cases for compatibility with libdbusmenu-glib which uses
+ // "plus" for "+" and "minus" for "-".
+ // cf https://bugs.launchpad.net/libdbusmenu-qt/+bug/712565
+ {"+", "plus"},
+ {"-", "minus"},
+ {0, 0}
+ };
+
+ const Row* ptr = table;
+ for (; ptr->zero != 0; ++ptr) {
+ const char* from = (*ptr)[srcCol];
+ const char* to = (*ptr)[dstCol];
+ tokens->replaceInStrings(from, to);
+ }
+}
+
+DBusMenuShortcut DBusMenuShortcut::fromKeySequence(const QKeySequence& sequence)
+{
+ QString string = sequence.toString();
+ DBusMenuShortcut shortcut;
+ QStringList tokens = string.split(", ");
+ Q_FOREACH(QString token, tokens) {
+ // Hack: Qt::CTRL | Qt::Key_Plus is turned into the string "Ctrl++",
+ // but we don't want the call to token.split() to consider the
+ // second '+' as a separator so we replace it with its final value.
+ token.replace("++", "+plus");
+ QStringList keyTokens = token.split('+');
+ processKeyTokens(&keyTokens, QT_COLUMN, DM_COLUMN);
+ shortcut << keyTokens;
+ }
+ return shortcut;
+}
+
+QKeySequence DBusMenuShortcut::toKeySequence() const
+{
+ QStringList tmp;
+ Q_FOREACH(const QStringList& keyTokens_, *this) {
+ QStringList keyTokens = keyTokens_;
+ processKeyTokens(&keyTokens, DM_COLUMN, QT_COLUMN);
+ tmp << keyTokens.join(QLatin1String("+"));
+ }
+ QString string = tmp.join(QLatin1String(", "));
+ return QKeySequence::fromString(string);
+}
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENUSHORTCUT_H
+#define DBUSMENUSHORTCUT_H
+
+// Qt
+#include <QtCore/QMetaType>
+#include <QtCore/QStringList>
+
+// Local
+#include <dbusmenu_export.h>
+
+
+class QKeySequence;
+
+class DBUSMENU_EXPORT DBusMenuShortcut : public QList<QStringList>
+{
+public:
+ QKeySequence toKeySequence() const;
+ static DBusMenuShortcut fromKeySequence(const QKeySequence&);
+};
+
+Q_DECLARE_METATYPE(DBusMenuShortcut)
+
+#endif /* DBUSMENUSHORTCUT_H */
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include "dbusmenutypes_p.h"
+
+// Local
+#include <dbusmenushortcut_p.h>
+#include <debug_p.h>
+
+// Qt
+#include <QDBusArgument>
+#include <QDBusMetaType>
+
+//// DBusMenuItem
+QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItem &obj)
+{
+ argument.beginStructure();
+ argument << obj.id << obj.properties;
+ argument.endStructure();
+ return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItem &obj)
+{
+ argument.beginStructure();
+ argument >> obj.id >> obj.properties;
+ argument.endStructure();
+ return argument;
+}
+
+//// DBusMenuItemKeys
+QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItemKeys &obj)
+{
+ argument.beginStructure();
+ argument << obj.id << obj.properties;
+ argument.endStructure();
+ return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItemKeys &obj)
+{
+ argument.beginStructure();
+ argument >> obj.id >> obj.properties;
+ argument.endStructure();
+ return argument;
+}
+
+//// DBusMenuLayoutItem
+QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuLayoutItem &obj)
+{
+ argument.beginStructure();
+ argument << obj.id << obj.properties;
+ argument.beginArray(qMetaTypeId<QDBusVariant>());
+ Q_FOREACH(const DBusMenuLayoutItem& child, obj.children) {
+ argument << QDBusVariant(QVariant::fromValue<DBusMenuLayoutItem>(child));
+ }
+ argument.endArray();
+ argument.endStructure();
+ return argument;
+}
+
+const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuLayoutItem &obj)
+{
+ argument.beginStructure();
+ argument >> obj.id >> obj.properties;
+ argument.beginArray();
+ while (!argument.atEnd()) {
+ QDBusVariant dbusVariant;
+ argument >> dbusVariant;
+ QDBusArgument childArgument = dbusVariant.variant().value<QDBusArgument>();
+
+ DBusMenuLayoutItem child;
+ childArgument >> child;
+ obj.children.append(child);
+ }
+ argument.endArray();
+ argument.endStructure();
+ return argument;
+}
+
+void DBusMenuTypes_register()
+{
+ static bool registered = false;
+ if (registered) {
+ return;
+ }
+ qDBusRegisterMetaType<DBusMenuItem>();
+ qDBusRegisterMetaType<DBusMenuItemList>();
+ qDBusRegisterMetaType<DBusMenuItemKeys>();
+ qDBusRegisterMetaType<DBusMenuItemKeysList>();
+ qDBusRegisterMetaType<DBusMenuLayoutItem>();
+ qDBusRegisterMetaType<DBusMenuLayoutItemList>();
+ qDBusRegisterMetaType<DBusMenuShortcut>();
+ registered = true;
+}
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENUTYPES_P_H
+#define DBUSMENUTYPES_P_H
+
+// Qt
+#include <QtCore/QList>
+#include <QtCore/QStringList>
+#include <QtCore/QVariant>
+
+// Local
+#include <dbusmenu_export.h>
+
+class QDBusArgument;
+
+//// DBusMenuItem
+/**
+ * Internal struct used to communicate on DBus
+ */
+struct DBUSMENU_EXPORT DBusMenuItem
+{
+ int id;
+ QVariantMap properties;
+};
+
+Q_DECLARE_METATYPE(DBusMenuItem)
+
+DBUSMENU_EXPORT QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItem &item);
+DBUSMENU_EXPORT const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItem &item);
+
+typedef QList<DBusMenuItem> DBusMenuItemList;
+
+Q_DECLARE_METATYPE(DBusMenuItemList)
+
+
+//// DBusMenuItemKeys
+/**
+ * Represents a list of keys for a menu item
+ */
+struct DBUSMENU_EXPORT DBusMenuItemKeys
+{
+ int id;
+ QStringList properties;
+};
+
+Q_DECLARE_METATYPE(DBusMenuItemKeys)
+
+DBUSMENU_EXPORT QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuItemKeys &);
+DBUSMENU_EXPORT const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuItemKeys &);
+
+typedef QList<DBusMenuItemKeys> DBusMenuItemKeysList;
+
+Q_DECLARE_METATYPE(DBusMenuItemKeysList)
+
+//// DBusMenuLayoutItem
+/**
+ * Represents an item with its children. GetLayout() returns a
+ * DBusMenuLayoutItemList.
+ */
+struct DBusMenuLayoutItem;
+struct DBUSMENU_EXPORT DBusMenuLayoutItem
+{
+ int id;
+ QVariantMap properties;
+ QList<DBusMenuLayoutItem> children;
+};
+
+Q_DECLARE_METATYPE(DBusMenuLayoutItem)
+
+DBUSMENU_EXPORT QDBusArgument &operator<<(QDBusArgument &argument, const DBusMenuLayoutItem &);
+DBUSMENU_EXPORT const QDBusArgument &operator>>(const QDBusArgument &argument, DBusMenuLayoutItem &);
+
+typedef QList<DBusMenuLayoutItem> DBusMenuLayoutItemList;
+
+Q_DECLARE_METATYPE(DBusMenuLayoutItemList)
+
+void DBusMenuTypes_register();
+#endif /* DBUSMENUTYPES_P_H */
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DEBUG_P_H
+#define DEBUG_P_H
+
+#include <QDebug>
+
+#define _DMBLUE "\033[34m"
+#define _DMRED "\033[31m"
+#define _DMRESET "\033[0m"
+#define _DMTRACE(level, color) (level().nospace() << color << __PRETTY_FUNCTION__ << _DMRESET ":").space()
+
+// Simple macros to get KDebug like support
+#define DMDEBUG _DMTRACE(qDebug, _DMBLUE)
+#define DMWARNING _DMTRACE(qWarning, _DMRED)
+
+// Log a variable name and value
+#define DMVAR(var) DMDEBUG << #var ":" << var
+
+#define DMRETURN_IF_FAIL(cond) if (!(cond)) { \
+ DMWARNING << "Condition failed: " #cond; \
+ return; \
+}
+
+#define DMRETURN_VALUE_IF_FAIL(cond, value) if (!(cond)) { \
+ DMWARNING << "Condition failed: " #cond; \
+ return (value); \
+}
+
+#endif /* DEBUG_P_H */
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include "utils_p.h"
+
+// Qt
+#include <QString>
+
+QString swapMnemonicChar(const QString &in, const char src, const char dst)
+{
+ QString out;
+ bool mnemonicFound = false;
+
+ for (int pos = 0; pos < in.length(); ) {
+ QChar ch = in[pos];
+ if (ch == src) {
+ if (pos == in.length() - 1) {
+ // 'src' at the end of string, skip it
+ ++pos;
+ } else {
+ if (in[pos + 1] == src) {
+ // A real 'src'
+ out += src;
+ pos += 2;
+ } else if (!mnemonicFound) {
+ // We found the mnemonic
+ mnemonicFound = true;
+ out += dst;
+ ++pos;
+ } else {
+ // We already have a mnemonic, just skip the char
+ ++pos;
+ }
+ }
+ } else if (ch == dst) {
+ // Escape 'dst'
+ out += dst;
+ out += dst;
+ ++pos;
+ } else {
+ out += ch;
+ ++pos;
+ }
+ }
+
+ return out;
+}
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef UTILS_P_H
+#define UTILS_P_H
+
+class QString;
+
+/**
+ * Swap mnemonic char: Qt uses '&', while dbusmenu uses '_'
+ */
+QString swapMnemonicChar(const QString &in, const char src, const char dst);
+
+#endif /* UTILS_P_H */
--- /dev/null
+if (NOT USE_QT5)
+ qt4_automoc(slowmenu.cpp)
+endif()
+add_executable(slowmenu slowmenu.cpp)
+
+if (NOT USE_QT5)
+ target_link_libraries(slowmenu
+ ${QT_QTGUI_LIBRARIES}
+ ${QT_QTDBUS_LIBRARIES}
+ ${QT_QTCORE_LIBRARIES}
+ dbusmenu-qt
+ )
+
+ set(test_LIBRARIES
+ ${QT_QTGUI_LIBRARY}
+ ${QT_QTCORE_LIBRARY}
+ ${QT_QTDBUS_LIBRARY}
+ ${QT_QTTEST_LIBRARY}
+ dbusmenu-qt
+ )
+
+ include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}/../src
+ ${CMAKE_CURRENT_BINARY_DIR}/../src
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${QT_QTTEST_INCLUDE_DIR}
+ ${QT_QTDBUS_INCLUDE_DIR}
+ )
+else()
+ find_package(Qt5Test REQUIRED)
+
+ target_link_libraries(slowmenu
+ ${Qt5Gui_LIBRARIES}
+ ${Qt5Core_LIBRARIES}
+ ${Qt5DBus_LIBRARIES}
+ dbusmenu-qt5
+ )
+
+ set(test_LIBRARIES
+ ${Qt5Gui_LIBRARIES}
+ ${Qt5Core_LIBRARIES}
+ ${Qt5DBus_LIBRARIES}
+ ${Qt5Test_LIBRARIES}
+ dbusmenu-qt5
+ )
+
+ include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}/../src
+ ${CMAKE_CURRENT_BINARY_DIR}/../src
+ ${CMAKE_CURRENT_BINARY_DIR}
+ ${Qt5Test_INCLUDE_DIRS}
+ ${Qt5DBus_INCLUDE_DIRS}
+ )
+endif()
+
+# Macros to create "check" target
+set(_test_executable_list "")
+
+macro(add_test_executable _executable)
+ add_test(${_executable} ${_executable})
+ set(_test_executable_list "${_test_executable_list};${_executable}")
+ add_executable(${_executable} ${ARGN})
+endmacro(add_test_executable)
+
+# Call this at the end
+macro(create_check_target)
+ add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND} --verbose
+ DEPENDS ${_test_executable_list})
+endmacro(create_check_target)
+
+enable_testing()
+
+
+# dbusmenuexportertest
+set(dbusmenuexportertest_SRCS
+ dbusmenuexportertest.cpp
+ testutils.cpp
+ )
+
+if (NOT USE_QT5)
+ qt4_automoc(${dbusmenuexportertest_SRCS})
+endif()
+
+add_test_executable(dbusmenuexportertest ${dbusmenuexportertest_SRCS})
+
+target_link_libraries(dbusmenuexportertest
+ ${test_LIBRARIES}
+ )
+
+
+# dbusmenuimportertest
+set(dbusmenuimportertest_SRCS
+ dbusmenuimportertest.cpp
+ testutils.cpp
+ )
+
+if (NOT USE_QT5)
+ qt4_automoc(${dbusmenuimportertest_SRCS})
+endif()
+
+add_test_executable(dbusmenuimportertest ${dbusmenuimportertest_SRCS})
+
+target_link_libraries(dbusmenuimportertest
+ ${test_LIBRARIES}
+ )
+
+
+# dbusmenushortcuttest
+set(dbusmenushortcuttest_SRCS
+ dbusmenushortcuttest.cpp
+ )
+
+if (NOT USE_QT5)
+ qt4_automoc(${dbusmenushortcuttest_SRCS})
+endif()
+
+add_test_executable(dbusmenushortcuttest ${dbusmenushortcuttest_SRCS})
+
+target_link_libraries(dbusmenushortcuttest
+ ${test_LIBRARIES}
+ )
+
+# Keep this at the end
+create_check_target()
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+// Self
+#include "dbusmenuexportertest.h"
+
+// Qt
+#include <QDBusConnection>
+#include <QDBusInterface>
+#include <QDBusReply>
+#include <QIcon>
+#include <QMenu>
+#include <QtTest>
+
+// DBusMenuQt
+#include <dbusmenuexporter.h>
+#include <dbusmenutypes_p.h>
+#include <dbusmenushortcut_p.h>
+#include <debug_p.h>
+
+// Local
+#include "testutils.h"
+
+QTEST_MAIN(DBusMenuExporterTest)
+
+static const char *TEST_SERVICE = "org.kde.dbusmenu-qt-test";
+static const char *TEST_OBJECT_PATH = "/TestMenuBar";
+
+Q_DECLARE_METATYPE(QList<int>)
+
+static DBusMenuLayoutItemList getChildren(QDBusAbstractInterface* iface, int parentId, const QStringList &propertyNames)
+{
+ QDBusPendingReply<uint, DBusMenuLayoutItem> reply = iface->call("GetLayout", parentId, /*recursionDepth=*/ 1, propertyNames);
+ reply.waitForFinished();
+ if (!reply.isValid()) {
+ qFatal("%s", qPrintable(reply.error().message()));
+ return DBusMenuLayoutItemList();
+ }
+
+ DBusMenuLayoutItem rootItem = reply.argumentAt<1>();
+ return rootItem.children;
+}
+
+void DBusMenuExporterTest::init()
+{
+ QVERIFY(QDBusConnection::sessionBus().registerService(TEST_SERVICE));
+ QCoreApplication::setAttribute(Qt::AA_DontShowIconsInMenus, false);
+}
+
+void DBusMenuExporterTest::cleanup()
+{
+ QVERIFY(QDBusConnection::sessionBus().unregisterService(TEST_SERVICE));
+}
+
+void DBusMenuExporterTest::testGetSomeProperties_data()
+{
+ QTest::addColumn<QString>("label");
+ QTest::addColumn<QString>("iconName");
+ QTest::addColumn<bool>("enabled");
+
+ QTest::newRow("label only") << "label" << QString() << true;
+ QTest::newRow("disabled, label only") << "label" << QString() << false;
+ QTest::newRow("icon name") << "label" << "edit-undo" << true;
+}
+
+void DBusMenuExporterTest::testGetSomeProperties()
+{
+ QFETCH(QString, label);
+ QFETCH(QString, iconName);
+ QFETCH(bool, enabled);
+
+ // Create an exporter for a menu with one action, defined by the test data
+ QMenu inputMenu;
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ QAction *action = new QAction(label, &inputMenu);
+ if (!iconName.isEmpty()) {
+ QIcon icon = QIcon::fromTheme(iconName);
+ QVERIFY(!icon.isNull());
+ action->setIcon(icon);
+ }
+ action->setEnabled(enabled);
+ inputMenu.addAction(action);
+
+ // Check out exporter is on DBus
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ QVERIFY2(iface.isValid(), qPrintable(iface.lastError().message()));
+
+ // Get exported menu info
+ QStringList propertyNames = QStringList() << "type" << "enabled" << "label" << "icon-name";
+ DBusMenuLayoutItemList list = getChildren(&iface, /*parentId=*/0, propertyNames);
+ DBusMenuLayoutItem item = list.first();
+ QVERIFY(item.id != 0);
+ QVERIFY(item.children.isEmpty());
+ QVERIFY(!item.properties.contains("type"));
+ QCOMPARE(item.properties.value("label").toString(), label);
+ if (enabled) {
+ QVERIFY(!item.properties.contains("enabled"));
+ } else {
+ QCOMPARE(item.properties.value("enabled").toBool(), false);
+ }
+ if (iconName.isEmpty()) {
+ QVERIFY(!item.properties.contains("icon-name"));
+ } else {
+ QCOMPARE(item.properties.value("icon-name").toString(), iconName);
+ }
+}
+
+void DBusMenuExporterTest::testGetAllProperties()
+{
+ // set of properties which must be returned because their values are not
+ // the default values
+ const QSet<QString> a1Properties = QSet<QString>()
+ << "label"
+ ;
+
+ const QSet<QString> separatorProperties = QSet<QString>()
+ << "type";
+
+ const QSet<QString> a2Properties = QSet<QString>()
+ << "label"
+ << "enabled"
+ << "icon-name"
+ << "icon-data" // Icon data is always provided if the icon is valid.
+ << "visible"
+ ;
+
+ // Create the menu items
+ QMenu inputMenu;
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ inputMenu.addAction("a1");
+
+ inputMenu.addSeparator();
+
+ QAction *a2 = new QAction("a2", &inputMenu);
+ a2->setEnabled(false);
+ QIcon icon = QIcon::fromTheme("edit-undo");
+ QVERIFY(!icon.isNull());
+ a2->setIcon(icon);
+ a2->setVisible(false);
+ inputMenu.addAction(a2);
+
+ // Export them
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ QVERIFY2(iface.isValid(), qPrintable(iface.lastError().message()));
+
+ // Get children
+ DBusMenuLayoutItemList list = getChildren(&iface, 0, QStringList());
+ QCOMPARE(list.count(), 3);
+
+ // Check we get the right properties
+ DBusMenuLayoutItem item = list.takeFirst();
+ QCOMPARE(QSet<QString>::fromList(item.properties.keys()), a1Properties);
+
+ item = list.takeFirst();
+ QCOMPARE(QSet<QString>::fromList(item.properties.keys()), separatorProperties);
+
+ item = list.takeFirst();
+ QCOMPARE(QSet<QString>::fromList(item.properties.keys()), a2Properties);
+}
+
+void DBusMenuExporterTest::testGetNonExistentProperty()
+{
+ const char* NON_EXISTENT_KEY = "i-do-not-exist";
+
+ QMenu inputMenu;
+ inputMenu.addAction("a1");
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ DBusMenuLayoutItemList list = getChildren(&iface, 0, QStringList() << NON_EXISTENT_KEY);
+ QCOMPARE(list.count(), 1);
+
+ DBusMenuLayoutItem item = list.takeFirst();
+ QVERIFY(!item.properties.contains(NON_EXISTENT_KEY));
+}
+
+void DBusMenuExporterTest::testClickedEvent()
+{
+ QMenu inputMenu;
+ QAction *action = inputMenu.addAction("a1");
+ QSignalSpy spy(action, SIGNAL(triggered()));
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ DBusMenuLayoutItemList list = getChildren(&iface, 0, QStringList());
+ QCOMPARE(list.count(), 1);
+ int id = list.first().id;
+
+ QVariant empty = QVariant::fromValue(QDBusVariant(QString()));
+ uint timestamp = QDateTime::currentDateTime().toTime_t();
+ iface.call("Event", id, "clicked", empty, timestamp);
+ QTest::qWait(500);
+
+ QCOMPARE(spy.count(), 1);
+}
+
+void DBusMenuExporterTest::testSubMenu()
+{
+ QMenu inputMenu;
+ QMenu *subMenu = inputMenu.addMenu("menu");
+ QAction *a1 = subMenu->addAction("a1");
+ QAction *a2 = subMenu->addAction("a2");
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ DBusMenuLayoutItemList list = getChildren(&iface, 0, QStringList());
+ QCOMPARE(list.count(), 1);
+ int id = list.first().id;
+
+ list = getChildren(&iface, id, QStringList());
+ QCOMPARE(list.count(), 2);
+
+ DBusMenuLayoutItem item = list.takeFirst();
+ QVERIFY(item.id != 0);
+ QCOMPARE(item.properties.value("label").toString(), a1->text());
+
+ item = list.takeFirst();
+ QCOMPARE(item.properties.value("label").toString(), a2->text());
+}
+
+void DBusMenuExporterTest::testDynamicSubMenu()
+{
+ // Track LayoutUpdated() signal: we don't want this signal to be emitted
+ // too often because it causes refreshes
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ ManualSignalSpy layoutUpdatedSpy;
+ QDBusConnection::sessionBus().connect(TEST_SERVICE, TEST_OBJECT_PATH, "com.canonical.dbusmenu", "LayoutUpdated", "ui", &layoutUpdatedSpy, SLOT(receiveCall(uint, int)));
+
+ // Create our test menu
+ QMenu inputMenu;
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+ QAction *action = inputMenu.addAction("menu");
+ QMenu *subMenu = new QMenu(&inputMenu);
+ action->setMenu(subMenu);
+ MenuFiller filler(subMenu);
+ filler.addAction(new QAction("a1", subMenu));
+ filler.addAction(new QAction("a2", subMenu));
+
+ // Get id of submenu
+ DBusMenuLayoutItemList list = getChildren(&iface, 0, QStringList());
+ QCOMPARE(list.count(), 1);
+ int id = list.first().id;
+
+ // Nothing for now
+ QCOMPARE(subMenu->actions().count(), 0);
+
+ // LayoutUpdated should be emitted once because inputMenu is filled
+ QTest::qWait(500);
+ QCOMPARE(layoutUpdatedSpy.count(), 1);
+ QCOMPARE(layoutUpdatedSpy.takeFirst().at(1).toInt(), 0);
+
+ // Pretend we show the menu
+ QDBusReply<bool> aboutToShowReply = iface.call("AboutToShow", id);
+ QVERIFY2(aboutToShowReply.isValid(), qPrintable(aboutToShowReply.error().message()));
+ QVERIFY(aboutToShowReply.value());
+ QTest::qWait(500);
+ QCOMPARE(layoutUpdatedSpy.count(), 1);
+ QCOMPARE(layoutUpdatedSpy.takeFirst().at(1).toInt(), id);
+
+ // Get submenu items
+ list = getChildren(&iface, id, QStringList());
+ QVERIFY(subMenu->actions().count() > 0);
+ QCOMPARE(list.count(), subMenu->actions().count());
+
+ for (int pos=0; pos< list.count(); ++pos) {
+ DBusMenuLayoutItem item = list.at(pos);
+ QVERIFY(item.id != 0);
+ QAction *action = subMenu->actions().at(pos);
+ QVERIFY(action);
+ QCOMPARE(item.properties.value("label").toString(), action->text());
+ }
+}
+
+void DBusMenuExporterTest::testRadioItems()
+{
+ DBusMenuLayoutItem item;
+ DBusMenuLayoutItemList list;
+ QMenu inputMenu;
+ QVERIFY(QDBusConnection::sessionBus().registerService(TEST_SERVICE));
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ // Create 2 radio items, check first one
+ QAction *a1 = inputMenu.addAction("a1");
+ a1->setCheckable(true);
+ QAction *a2 = inputMenu.addAction("a1");
+ a2->setCheckable(true);
+
+ QActionGroup group(0);
+ group.addAction(a1);
+ group.addAction(a2);
+ a1->setChecked(true);
+
+ QVERIFY(!a2->isChecked());
+
+ // Get item ids
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ list = getChildren(&iface, 0, QStringList());
+ QCOMPARE(list.count(), 2);
+
+ // Check items are radios and correctly toggled
+ item = list.takeFirst();
+ QCOMPARE(item.properties.value("toggle-type").toString(), QString("radio"));
+ QCOMPARE(item.properties.value("toggle-state").toInt(), 1);
+ int a1Id = item.id;
+ item = list.takeFirst();
+ QCOMPARE(item.properties.value("toggle-type").toString(), QString("radio"));
+ QCOMPARE(item.properties.value("toggle-state").toInt(), 0);
+ int a2Id = item.id;
+
+ // Click a2
+ ManualSignalSpy spy;
+ QDBusConnection::sessionBus().connect(TEST_SERVICE, TEST_OBJECT_PATH, "com.canonical.dbusmenu", "ItemsPropertiesUpdated", "a(ia{sv})a(ias)",
+ &spy, SLOT(receiveCall(DBusMenuItemList, DBusMenuItemKeysList)));
+ QVariant empty = QVariant::fromValue(QDBusVariant(QString()));
+ uint timestamp = QDateTime::currentDateTime().toTime_t();
+ iface.call("Event", a2Id, "clicked", empty, timestamp);
+ QTest::qWait(500);
+
+ // Check a1 is not checked, but a2 is
+ list = getChildren(&iface, 0, QStringList());
+ QCOMPARE(list.count(), 2);
+
+ item = list.takeFirst();
+ QCOMPARE(item.properties.value("toggle-state").toInt(), 0);
+
+ item = list.takeFirst();
+ QCOMPARE(item.properties.value("toggle-state").toInt(), 1);
+
+ // Did we get notified?
+ QCOMPARE(spy.count(), 1);
+ QSet<int> updatedIds;
+ {
+ QVariantList lst = spy.takeFirst().at(0).toList();
+ Q_FOREACH(QVariant variant, lst) {
+ updatedIds << variant.toInt();
+ }
+ }
+
+ QSet<int> expectedIds;
+ expectedIds << a1Id << a2Id;
+
+ QCOMPARE(updatedIds, expectedIds);
+}
+
+void DBusMenuExporterTest::testNonExclusiveActionGroup()
+{
+ DBusMenuLayoutItem item;
+ DBusMenuLayoutItemList list;
+ QMenu inputMenu;
+ QVERIFY(QDBusConnection::sessionBus().registerService(TEST_SERVICE));
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ // Create 2 checkable items
+ QAction *a1 = inputMenu.addAction("a1");
+ a1->setCheckable(true);
+ QAction *a2 = inputMenu.addAction("a1");
+ a2->setCheckable(true);
+
+ // Put them into a non exclusive group
+ QActionGroup group(0);
+ group.addAction(a1);
+ group.addAction(a2);
+ group.setExclusive(false);
+
+ // Get item ids
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ list = getChildren(&iface, 0, QStringList());
+ QCOMPARE(list.count(), 2);
+
+ // Check items are checkmark, not radio
+ item = list.takeFirst();
+ QCOMPARE(item.properties.value("toggle-type").toString(), QString("checkmark"));
+ int a1Id = item.id;
+ item = list.takeFirst();
+ QCOMPARE(item.properties.value("toggle-type").toString(), QString("checkmark"));
+ int a2Id = item.id;
+}
+
+void DBusMenuExporterTest::testClickDeletedAction()
+{
+ QMenu inputMenu;
+ QVERIFY(QDBusConnection::sessionBus().registerService(TEST_SERVICE));
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ QAction *a1 = inputMenu.addAction("a1");
+
+ // Get id
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ DBusMenuLayoutItemList list = getChildren(&iface, 0, QStringList());
+ QCOMPARE(list.count(), 1);
+ int id = list.takeFirst().id;
+
+ // Delete a1, it should not cause a crash when trying to trigger it
+ delete a1;
+
+ // Send a click to deleted a1
+ QVariant empty = QVariant::fromValue(QDBusVariant(QString()));
+ uint timestamp = QDateTime::currentDateTime().toTime_t();
+ iface.call("Event", id, "clicked", empty, timestamp);
+ QTest::qWait(500);
+}
+
+// Reproduce LP BUG 521011
+// https://bugs.launchpad.net/bugs/521011
+void DBusMenuExporterTest::testDeleteExporterBeforeMenu()
+{
+ QMenu inputMenu;
+ QVERIFY(QDBusConnection::sessionBus().registerService(TEST_SERVICE));
+ DBusMenuExporter *exporter = new DBusMenuExporter(TEST_OBJECT_PATH, &inputMenu);
+
+ QAction *a1 = inputMenu.addAction("a1");
+ delete exporter;
+ inputMenu.removeAction(a1);
+}
+
+void DBusMenuExporterTest::testUpdateAndDeleteSubMenu()
+{
+ // Create a menu with a submenu
+ QMenu inputMenu;
+ QMenu *subMenu = inputMenu.addMenu("menu");
+ QAction *a1 = subMenu->addAction("a1");
+
+ // Export it
+ QVERIFY(QDBusConnection::sessionBus().registerService(TEST_SERVICE));
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ // Update a1 (which is in subMenu) and delete subMenu right after that. If
+ // DBusMenuExporter is not careful it will crash in the qWait() because it
+ // tries to send itemUpdated() for a1.
+ a1->setText("Not a menu anymore");
+ delete subMenu;
+ QTest::qWait(500);
+}
+
+void DBusMenuExporterTest::testMenuShortcut()
+{
+ // Create a menu containing an action with a shortcut
+ QMenu inputMenu;
+ QVERIFY(QDBusConnection::sessionBus().registerService(TEST_SERVICE));
+ DBusMenuExporter *exporter = new DBusMenuExporter(TEST_OBJECT_PATH, &inputMenu);
+
+ QAction *a1 = inputMenu.addAction("a1");
+ a1->setShortcut(Qt::CTRL | Qt::Key_A);
+
+ QAction *a2 = inputMenu.addAction("a2");
+ a2->setShortcut(QKeySequence(Qt::CTRL | Qt::Key_A, Qt::ALT | Qt::Key_B));
+
+ // No shortcut, to test the property is not added in this case
+ QAction *a3 = inputMenu.addAction("a3");
+
+ QList<QAction*> actionList;
+ actionList << a1 << a2 << a3;
+
+ // Check out exporter is on DBus
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ QVERIFY2(iface.isValid(), qPrintable(iface.lastError().message()));
+
+ // Get exported menu info
+ QStringList propertyNames = QStringList() << "label" << "shortcut";
+ DBusMenuLayoutItemList list = getChildren(&iface, 0, propertyNames);
+ QCOMPARE(list.count(), actionList.count());
+
+ Q_FOREACH(const QAction* action, actionList) {
+ DBusMenuLayoutItem item = list.takeFirst();
+ if (action->shortcut().isEmpty()) {
+ QVERIFY(!item.properties.contains("shortcut"));
+ } else {
+ QVERIFY(item.properties.contains("shortcut"));
+ QDBusArgument arg = item.properties.value("shortcut").value<QDBusArgument>();
+ DBusMenuShortcut shortcut;
+ arg >> shortcut;
+ QCOMPARE(shortcut.toKeySequence(), action->shortcut());
+ }
+ }
+}
+
+void DBusMenuExporterTest::testGetGroupProperties()
+{
+ // Create a menu containing two actions
+ QMenu inputMenu;
+ QVERIFY(QDBusConnection::sessionBus().registerService(TEST_SERVICE));
+ DBusMenuExporter *exporter = new DBusMenuExporter(TEST_OBJECT_PATH, &inputMenu);
+
+ QAction *a1 = inputMenu.addAction("a1");
+ QAction *a2 = inputMenu.addAction("a2");
+
+ // Check exporter is on DBus
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ QVERIFY2(iface.isValid(), qPrintable(iface.lastError().message()));
+
+ // Get item ids
+ DBusMenuLayoutItemList list = getChildren(&iface, 0, QStringList());
+ QCOMPARE(list.count(), inputMenu.actions().count());
+
+ int id1 = list.at(0).id;
+ int id2 = list.at(1).id;
+
+ // Get group properties
+ QList<int> ids = QList<int>() << id1 << id2;
+ QDBusReply<DBusMenuItemList> reply = iface.call("GetGroupProperties", QVariant::fromValue(ids), QStringList());
+ QVERIFY2(reply.isValid(), qPrintable(reply.error().message()));
+ DBusMenuItemList groupPropertiesList = reply.value();
+
+ // Check the info we received
+ QCOMPARE(groupPropertiesList.count(), inputMenu.actions().count());
+
+ Q_FOREACH(const QAction* action, inputMenu.actions()) {
+ DBusMenuItem item = groupPropertiesList.takeFirst();
+ QCOMPARE(item.properties.value("label").toString(), action->text());
+ }
+}
+
+void DBusMenuExporterTest::testActivateAction()
+{
+ // Create a menu containing two actions
+ QMenu inputMenu;
+ QVERIFY(QDBusConnection::sessionBus().registerService(TEST_SERVICE));
+ DBusMenuExporter *exporter = new DBusMenuExporter(TEST_OBJECT_PATH, &inputMenu);
+
+ QAction *a1 = inputMenu.addAction("a1");
+ QAction *a2 = inputMenu.addAction("a2");
+
+ // Check exporter is on DBus
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ QVERIFY2(iface.isValid(), qPrintable(iface.lastError().message()));
+
+ ManualSignalSpy spy;
+ QDBusConnection::sessionBus().connect(TEST_SERVICE, TEST_OBJECT_PATH, "com.canonical.dbusmenu", "ItemActivationRequested", "iu", &spy, SLOT(receiveCall(int, uint)));
+
+ // Get item ids
+ DBusMenuLayoutItemList list = getChildren(&iface, 0, QStringList());
+ QCOMPARE(list.count(), inputMenu.actions().count());
+
+ int id1 = list.at(0).id;
+ int id2 = list.at(1).id;
+
+ // Trigger actions
+ exporter->activateAction(a1);
+ exporter->activateAction(a2);
+
+ // Check we received the signals in the correct order
+ QTest::qWait(500);
+ QCOMPARE(spy.count(), 2);
+ QCOMPARE(spy.takeFirst().at(0).toInt(), id1);
+ QCOMPARE(spy.takeFirst().at(0).toInt(), id2);
+}
+
+static int trackCount(QMenu* menu)
+{
+ QList<QObject*> lst = menu->findChildren<QObject*>();
+ int count = 0;
+ Q_FOREACH(QObject* child, lst) {
+ if (qstrcmp(child->metaObject()->className(), "DBusMenu") == 0) {
+ ++count;
+ }
+ }
+ return count;
+}
+
+// Check we do not create more than one DBusMenu object for each menu
+// See KDE bug 254066
+void DBusMenuExporterTest::testTrackActionsOnlyOnce()
+{
+ // Create a menu with a submenu, unplug the submenu and plug it back. The
+ // submenu should not have more than one DBusMenu child object.
+ QMenu mainMenu;
+ QVERIFY(QDBusConnection::sessionBus().registerService(TEST_SERVICE));
+ DBusMenuExporter *exporter = new DBusMenuExporter(TEST_OBJECT_PATH, &mainMenu);
+
+ QMenu* subMenu = new QMenu("File");
+ subMenu->addAction("a1");
+ mainMenu.addAction(subMenu->menuAction());
+
+ QTest::qWait(500);
+ QCOMPARE(trackCount(subMenu), 1);
+
+ mainMenu.removeAction(subMenu->menuAction());
+
+ mainMenu.addAction(subMenu->menuAction());
+
+ QTest::qWait(500);
+ QCOMPARE(trackCount(subMenu), 1);
+}
+
+// If desktop does not want icon in menus, check we do not export them
+void DBusMenuExporterTest::testHonorDontShowIconsInMenusAttribute()
+{
+ QCoreApplication::setAttribute(Qt::AA_DontShowIconsInMenus, true);
+ QMenu inputMenu;
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ QAction *action = new QAction("Undo", &inputMenu);
+ QIcon icon = QIcon::fromTheme("edit-undo");
+ QVERIFY(!icon.isNull());
+ action->setIcon(icon);
+ inputMenu.addAction(action);
+
+ // Check out exporter is on DBus
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ QVERIFY2(iface.isValid(), qPrintable(iface.lastError().message()));
+
+ // Get exported menu info
+ QStringList propertyNames = QStringList() << "icon-name";
+ DBusMenuLayoutItemList list = getChildren(&iface, /*parentId=*/0, propertyNames);
+ DBusMenuLayoutItem item = list.first();
+ QVERIFY(item.id != 0);
+ QVERIFY(!item.properties.contains("icon-name"));
+}
+
+static bool hasInternalDBusMenuObject(QMenu* menu)
+{
+ Q_FOREACH(QObject* obj, menu->children()) {
+ if (obj->inherits("DBusMenu")) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// DBusMenuExporter adds an instance of an internal class named "DBusMenu" to
+// any QMenu it tracks. Check they go away when the exporter is deleted.
+void DBusMenuExporterTest::testDBusMenuObjectIsDeletedWhenExporterIsDeleted()
+{
+ QMenu inputMenu;
+ QVERIFY(QDBusConnection::sessionBus().registerService(TEST_SERVICE));
+ DBusMenuExporter *exporter = new DBusMenuExporter(TEST_OBJECT_PATH, &inputMenu);
+
+ QAction *a1 = inputMenu.addAction("a1");
+ QVERIFY2(hasInternalDBusMenuObject(&inputMenu), "Test setup failed");
+ delete exporter;
+ QVERIFY(!hasInternalDBusMenuObject(&inputMenu));
+}
+
+void DBusMenuExporterTest::testSeparatorCollapsing_data()
+{
+ QTest::addColumn<QString>("input");
+ QTest::addColumn<QString>("expected");
+
+ QTest::newRow("one-separator") << "a-b" << "a-b";
+ QTest::newRow("two-separators") << "a-b-c" << "a-b-c";
+ QTest::newRow("middle-separators") << "a--b" << "a-b";
+ QTest::newRow("separators-at-begin") << "--a-b" << "a-b";
+ QTest::newRow("separators-at-end") << "a-b--" << "a-b";
+ QTest::newRow("separators-everywhere") << "--a---bc--d--" << "a-bc-d";
+ QTest::newRow("empty-menu") << "" << "";
+ QTest::newRow("separators-only") << "---" << "";
+}
+
+void DBusMenuExporterTest::testSeparatorCollapsing()
+{
+ QFETCH(QString, input);
+ QFETCH(QString, expected);
+
+ // Create menu from menu string
+ QMenu inputMenu;
+
+ QVERIFY(QDBusConnection::sessionBus().registerService(TEST_SERVICE));
+ DBusMenuExporter *exporter = new DBusMenuExporter(TEST_OBJECT_PATH, &inputMenu);
+
+ if (input.isEmpty()) {
+ // Pretend there was an action so that doEmitLayoutUpdated() is called
+ // even if the new menu is empty. If we don't do this we don't test
+ // DBusMenuExporterPrivate::collapseSeparators() for empty menus.
+ delete inputMenu.addAction("dummy");
+ }
+
+ Q_FOREACH(QChar ch, input) {
+ if (ch == '-') {
+ inputMenu.addSeparator();
+ } else {
+ inputMenu.addAction(ch);
+ }
+ }
+
+ QTest::qWait(500);
+
+ // Check out exporter is on DBus
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ QVERIFY2(iface.isValid(), qPrintable(iface.lastError().message()));
+
+ // Get exported menu info
+ QStringList propertyNames = QStringList();
+ DBusMenuLayoutItemList list = getChildren(&iface, /*parentId=*/0, propertyNames);
+
+ // Recreate a menu string from the item list
+ QString output;
+ Q_FOREACH(const DBusMenuLayoutItem& item, list) {
+ QVariantMap properties = item.properties;
+ if (properties.contains("visible") && !properties.value("visible").toBool()) {
+ continue;
+ }
+ QString type = properties.value("type").toString();
+ if (type == "separator") {
+ output += '-';
+ } else {
+ output += properties.value("label").toString();
+ }
+ }
+
+ // Check it matches
+ QCOMPARE(output, expected);
+}
+
+static void checkPropertiesChangedArgs(const QVariantList& args, const QString& name, const QVariant& value)
+{
+ QCOMPARE(args[0].toString(), QString("com.canonical.dbusmenu"));
+ QVariantMap map;
+ map.insert(name, value);
+ QCOMPARE(args[1].toMap(), map);
+ QCOMPARE(args[2].toStringList(), QStringList());
+}
+
+void DBusMenuExporterTest::testSetStatus()
+{
+ QMenu inputMenu;
+ QVERIFY(QDBusConnection::sessionBus().registerService(TEST_SERVICE));
+ DBusMenuExporter *exporter = new DBusMenuExporter(TEST_OBJECT_PATH, &inputMenu);
+ ManualSignalSpy spy;
+ QDBusConnection::sessionBus().connect(TEST_SERVICE, TEST_OBJECT_PATH, "org.freedesktop.DBus.Properties", "PropertiesChanged", "sa{sv}as", &spy, SLOT(receiveCall(QString, QVariantMap, QStringList)));
+
+ QTest::qWait(500);
+
+ // Check our exporter is on DBus
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ QVERIFY2(iface.isValid(), qPrintable(iface.lastError().message()));
+
+ QCOMPARE(exporter->status(), QString("normal"));
+
+ // Change status, a DBus signal should be emitted
+ exporter->setStatus("notice");
+ QCOMPARE(exporter->status(), QString("notice"));
+ QTest::qWait(500);
+ QCOMPARE(spy.count(), 1);
+ checkPropertiesChangedArgs(spy.takeFirst(), "Status", "notice");
+
+ // Same status => no signal
+ exporter->setStatus("notice");
+ QTest::qWait(500);
+ QCOMPARE(spy.count(), 0);
+
+ // Change status, a DBus signal should be emitted
+ exporter->setStatus("normal");
+ QTest::qWait(500);
+ QCOMPARE(spy.count(), 1);
+ checkPropertiesChangedArgs(spy.takeFirst(), "Status", "normal");
+}
+
+void DBusMenuExporterTest::testGetIconDataProperty()
+{
+ // Create an icon
+ QImage img(16, 16, QImage::Format_ARGB32);
+ {
+ QPainter painter(&img);
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ QRect rect = img.rect();
+ painter.fillRect(rect, Qt::transparent);
+ rect.adjust(2, 2, -2, -2);
+ painter.fillRect(rect, Qt::red);
+ rect.adjust(2, 2, -2, -2);
+ painter.fillRect(rect, Qt::green);
+ }
+
+ QIcon icon(QPixmap::fromImage(img));
+
+ // Create a menu with the icon and export it
+ QMenu inputMenu;
+ QAction* a1 = inputMenu.addAction("a1");
+ a1->setIcon(icon);
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ // Get properties
+ QDBusInterface iface(TEST_SERVICE, TEST_OBJECT_PATH);
+ DBusMenuLayoutItemList layoutItemlist = getChildren(&iface, 0, QStringList());
+ QCOMPARE(layoutItemlist.count(), 1);
+
+ QList<int> ids = QList<int>() << layoutItemlist[0].id;
+
+ QDBusReply<DBusMenuItemList> reply = iface.call("GetGroupProperties", QVariant::fromValue(ids), QStringList());
+
+ DBusMenuItemList itemlist = reply.value();
+ QCOMPARE(itemlist.count(), 1);
+
+ // Check we have the right property
+ DBusMenuItem item = itemlist.takeFirst();
+ QVERIFY(!item.properties.contains("icon-name"));
+ QVERIFY(item.properties.contains("icon-data"));
+
+ // Check saved image is the same
+ QByteArray data = item.properties.value("icon-data").toByteArray();
+ QVERIFY(!data.isEmpty());
+ QImage result;
+ QVERIFY(result.loadFromData(data, "PNG"));
+ QCOMPARE(result, img);
+}
+
+#include "dbusmenuexportertest.moc"
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2009 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENUEXPORTERTEST_H
+#define DBUSMENUEXPORTERTEST_H
+
+#define QT_GUI_LIB
+#include <QtGui>
+
+// Qt
+#include <QObject>
+
+// Local
+
+class DBusMenuExporterTest : public QObject
+{
+Q_OBJECT
+private Q_SLOTS:
+ void testGetSomeProperties();
+ void testGetSomeProperties_data();
+ void testGetAllProperties();
+ void testGetNonExistentProperty();
+ void testClickedEvent();
+ void testSubMenu();
+ void testDynamicSubMenu();
+ void testRadioItems();
+ void testNonExclusiveActionGroup();
+ void testClickDeletedAction();
+ void testDeleteExporterBeforeMenu();
+ void testUpdateAndDeleteSubMenu();
+ void testMenuShortcut();
+ void testGetGroupProperties();
+ void testActivateAction();
+ void testTrackActionsOnlyOnce();
+ void testHonorDontShowIconsInMenusAttribute();
+ void testDBusMenuObjectIsDeletedWhenExporterIsDeleted();
+ void testSeparatorCollapsing_data();
+ void testSeparatorCollapsing();
+ void testSetStatus();
+ void testGetIconDataProperty();
+
+ void init();
+ void cleanup();
+};
+
+#endif /* DBUSMENUEXPORTERTEST_H */
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+// Self
+#include "dbusmenuimportertest.h"
+
+// Qt
+#include <QDBusConnection>
+#include <QDBusInterface>
+#include <QDBusReply>
+#include <QIcon>
+#include <QMenu>
+#include <QtTest>
+
+// DBusMenuQt
+#include <dbusmenuexporter.h>
+#include <dbusmenuimporter.h>
+#include <debug_p.h>
+
+// Local
+#include "testutils.h"
+
+QTEST_MAIN(DBusMenuImporterTest)
+
+static const char *TEST_SERVICE = "com.canonical.dbusmenu-qt-test";
+static const char *TEST_OBJECT_PATH = "/TestMenuBar";
+
+Q_DECLARE_METATYPE(QAction*)
+
+void DBusMenuImporterTest::initTestCase()
+{
+ qRegisterMetaType<QAction*>("QAction*");
+ QVERIFY(QDBusConnection::sessionBus().registerService(TEST_SERVICE));
+}
+
+void DBusMenuImporterTest::cleanup()
+{
+ waitForDeferredDeletes();
+}
+
+void DBusMenuImporterTest::testStandardItem()
+{
+ QMenu inputMenu;
+ QAction *action = inputMenu.addAction("Test");
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ DBusMenuImporter importer(TEST_SERVICE, TEST_OBJECT_PATH);
+ QTest::qWait(500);
+
+ QMenu *outputMenu = importer.menu();
+ QCOMPARE(outputMenu->actions().count(), 1);
+ QAction *outputAction = outputMenu->actions().first();
+ QCOMPARE(outputAction->text(), QString("Test"));
+}
+
+void DBusMenuImporterTest::testAddingNewItem()
+{
+ QMenu inputMenu;
+ QAction *action = inputMenu.addAction("Test");
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ DBusMenuImporter importer(TEST_SERVICE, TEST_OBJECT_PATH);
+ QTest::qWait(500);
+ QMenu *outputMenu = importer.menu();
+ QCOMPARE(outputMenu->actions().count(), inputMenu.actions().count());
+
+ inputMenu.addAction("Test2");
+ QTest::qWait(500);
+ QCOMPARE(outputMenu->actions().count(), inputMenu.actions().count());
+}
+
+void DBusMenuImporterTest::testShortcut()
+{
+ QMenu inputMenu;
+ QAction *action = inputMenu.addAction("Test");
+ action->setShortcut(Qt::CTRL | Qt::Key_S);
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ DBusMenuImporter importer(TEST_SERVICE, TEST_OBJECT_PATH);
+ QTest::qWait(500);
+ QMenu *outputMenu = importer.menu();
+
+ QAction *outputAction = outputMenu->actions().at(0);
+ QCOMPARE(outputAction->shortcut(), action->shortcut());
+}
+
+void DBusMenuImporterTest::testDeletingImporterWhileWaitingForAboutToShow()
+{
+ // Start test program and wait for it to be ready
+ QProcess slowMenuProcess;
+ slowMenuProcess.start("./slowmenu");
+ QTest::qWait(500);
+
+ // Create importer and wait for the menu
+ DBusMenuImporter *importer = new DBusMenuImporter(TEST_SERVICE, TEST_OBJECT_PATH);
+ QTest::qWait(500);
+
+ QMenu *outputMenu = importer->menu();
+ QTimer::singleShot(100, importer, SLOT(deleteLater()));
+ outputMenu->popup(QPoint(0, 0));
+
+ // If it crashes, it will crash while waiting there
+ QTest::qWait(500);
+
+ // Done, stop our test program
+ slowMenuProcess.close();
+ slowMenuProcess.waitForFinished();
+}
+
+void DBusMenuImporterTest::testDynamicMenu()
+{
+ QMenu rootMenu;
+ QAction* a1 = new QAction("a1", &rootMenu);
+ QAction* a2 = new QAction("a2", &rootMenu);
+ MenuFiller rootMenuFiller(&rootMenu);
+ rootMenuFiller.addAction(a1);
+ rootMenuFiller.addAction(a2);
+
+ QMenu subMenu;
+ MenuFiller subMenuFiller(&subMenu);
+ subMenuFiller.addAction(new QAction("a3", &subMenu));
+
+ a1->setMenu(&subMenu);
+
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &rootMenu);
+
+ // Import this menu
+ DBusMenuImporter importer(TEST_SERVICE, TEST_OBJECT_PATH);
+ QTest::qWait(500);
+ QMenu *outputMenu = importer.menu();
+
+ // There should be no children for now
+ QCOMPARE(outputMenu->actions().count(), 0);
+
+ // Update menu, a1 and a2 should get added
+ QSignalSpy spy(&importer, SIGNAL(menuUpdated()));
+ QSignalSpy spyOld(&importer, SIGNAL(menuReadyToBeShown()));
+ importer.updateMenu();
+ while (spy.isEmpty()) {
+ QTest::qWait(500);
+ }
+
+ QCOMPARE(outputMenu->actions().count(), 2);
+ QTest::qWait(500);
+ QAction* a1Output = outputMenu->actions().first();
+
+ // a1Output should have an empty menu
+ QMenu* a1OutputMenu = a1Output->menu();
+ QVERIFY(a1OutputMenu);
+ QCOMPARE(a1OutputMenu->actions().count(), 0);
+
+ // Show a1OutputMenu, a3 should get added
+ QMetaObject::invokeMethod(a1OutputMenu, "aboutToShow");
+ QTest::qWait(500);
+
+ QCOMPARE(a1OutputMenu->actions().count(), 1);
+
+ // menuUpdated() and menuReadyToBeShown() should only have been emitted
+ // once
+ QCOMPARE(spy.count(), 1);
+ QCOMPARE(spyOld.count(), 1);
+}
+
+void DBusMenuImporterTest::testActionActivationRequested()
+{
+ // Export a menu
+ QMenu inputMenu;
+ QAction *inputA1 = inputMenu.addAction("a1");
+ QAction *inputA2 = inputMenu.addAction("a2");
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ // Import the menu
+ DBusMenuImporter importer(TEST_SERVICE, TEST_OBJECT_PATH);
+ QSignalSpy spy(&importer, SIGNAL(actionActivationRequested(QAction*)));
+
+ QTest::qWait(500);
+ QMenu *outputMenu = importer.menu();
+
+ // Get matching output actions
+ QCOMPARE(outputMenu->actions().count(), 2);
+ QAction *outputA1 = outputMenu->actions().at(0);
+ QAction *outputA2 = outputMenu->actions().at(1);
+
+ // Request activation
+ exporter.activateAction(inputA1);
+ exporter.activateAction(inputA2);
+
+ // Check we received the signal in the right order
+ QTest::qWait(500);
+ QCOMPARE(spy.count(), 2);
+ QCOMPARE(spy.takeFirst().at(0).value<QAction*>(), outputA1);
+ QCOMPARE(spy.takeFirst().at(0).value<QAction*>(), outputA2);
+}
+
+void DBusMenuImporterTest::testActionsAreDeletedWhenImporterIs()
+{
+ // Export a menu
+ QMenu inputMenu;
+ inputMenu.addAction("a1");
+ QMenu *inputSubMenu = inputMenu.addMenu("subMenu");
+ inputSubMenu->addAction("a2");
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ // Import the menu
+ DBusMenuImporter *importer = new DBusMenuImporter(TEST_SERVICE, TEST_OBJECT_PATH);
+ QTest::qWait(500);
+
+ // Put all items of the menu in a list of QPointers
+ QList< QPointer<QObject> > children;
+
+ QMenu *outputMenu = importer->menu();
+ QCOMPARE(outputMenu->actions().count(), 2);
+ QMenu *outputSubMenu = outputMenu->actions().at(1)->menu();
+ QVERIFY(outputSubMenu);
+ // Fake aboutToShow so that outputSubMenu is populated
+ QMetaObject::invokeMethod(outputSubMenu, "aboutToShow");
+ QCOMPARE(outputSubMenu->actions().count(), 1);
+
+ children << outputMenu->actions().at(0);
+ children << outputMenu->actions().at(1);
+ children << outputSubMenu;
+ children << outputSubMenu->actions().at(0);
+
+ delete importer;
+ waitForDeferredDeletes();
+
+ // There should be only invalid pointers in children
+ Q_FOREACH(QPointer<QObject> child, children) {
+ //qDebug() << child;
+ QVERIFY(child.isNull());
+ }
+}
+
+void DBusMenuImporterTest::testIconData()
+{
+ // Create an icon
+ QImage img(16, 16, QImage::Format_ARGB32);
+ {
+ QPainter painter(&img);
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ QRect rect = img.rect();
+ painter.fillRect(rect, Qt::transparent);
+ rect.adjust(2, 2, -2, -2);
+ painter.fillRect(rect, Qt::red);
+ rect.adjust(2, 2, -2, -2);
+ painter.fillRect(rect, Qt::green);
+ }
+ QIcon inputIcon(QPixmap::fromImage(img));
+
+ // Export a menu
+ QMenu inputMenu;
+ QAction *a1 = inputMenu.addAction("a1");
+ a1->setIcon(inputIcon);
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ // Import the menu
+ DBusMenuImporter *importer = new DBusMenuImporter(TEST_SERVICE, TEST_OBJECT_PATH);
+ QTest::qWait(500);
+
+ // Check icon of action
+ QMenu *outputMenu = importer->menu();
+ QCOMPARE(outputMenu->actions().count(), 1);
+
+ QIcon outputIcon = outputMenu->actions().first()->icon();
+ QVERIFY(!outputIcon.isNull());
+
+ QImage result = outputIcon.pixmap(16).toImage();
+ QByteArray origBytes, resultBytes;
+ img.save(origBytes);
+ result.save(resultBytes);
+ QCOMPARE(origBytes,resultBytes);
+}
+
+void DBusMenuImporterTest::testInvisibleItem()
+{
+ QMenu inputMenu;
+ QAction *action = inputMenu.addAction("Test");
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ DBusMenuImporter importer(TEST_SERVICE, TEST_OBJECT_PATH);
+ QTest::qWait(500);
+
+ QMenu *outputMenu = importer.menu();
+ QCOMPARE(outputMenu->actions().count(), 1);
+ QAction *outputAction = outputMenu->actions().first();
+
+ QVERIFY(outputAction->isVisible());
+
+ // Hide the action
+ action->setVisible(false);
+ QTest::qWait(500);
+ QVERIFY(!outputAction->isVisible());
+
+ // Show the action
+ action->setVisible(true);
+ QTest::qWait(500);
+ QVERIFY(outputAction->isVisible());
+}
+
+void DBusMenuImporterTest::testDisabledItem()
+{
+ QMenu inputMenu;
+ QAction *action = inputMenu.addAction("Test");
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, &inputMenu);
+
+ DBusMenuImporter importer(TEST_SERVICE, TEST_OBJECT_PATH);
+ QTest::qWait(500);
+
+ QMenu *outputMenu = importer.menu();
+ QCOMPARE(outputMenu->actions().count(), 1);
+ QAction *outputAction = outputMenu->actions().first();
+ QVERIFY(outputAction->isEnabled());
+
+ // Disable the action
+ DMDEBUG << "Disabling";
+ action->setEnabled(false);
+ QTest::qWait(500);
+ QVERIFY(!outputAction->isEnabled());
+
+ // Enable the action
+ action->setEnabled(true);
+ QTest::qWait(500);
+ QVERIFY(outputAction->isEnabled());
+}
+
+#include "dbusmenuimportertest.moc"
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENUIMPORTERTEST_H
+#define DBUSMENUIMPORTERTEST_H
+
+#define QT_GUI_LIB
+#include <QtGui>
+
+// Qt
+#include <QObject>
+
+// Local
+
+class DBusMenuImporterTest : public QObject
+{
+Q_OBJECT
+private Q_SLOTS:
+ void cleanup();
+ void testStandardItem();
+ void testAddingNewItem();
+ void testShortcut();
+ void testDeletingImporterWhileWaitingForAboutToShow();
+ void testDynamicMenu();
+ void testActionActivationRequested();
+ void testActionsAreDeletedWhenImporterIs();
+ void testIconData();
+ void testInvisibleItem();
+ void testDisabledItem();
+
+ void initTestCase();
+};
+
+#endif /* DBUSMENUIMPORTERTEST_H */
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+// Self
+#include "dbusmenushortcuttest.h"
+
+// Qt
+#include <QtTest>
+
+// DBusMenuQt
+#include <dbusmenushortcut_p.h>
+#include <debug_p.h>
+
+QTEST_MAIN(DBusMenuShortcutTest)
+
+namespace QTest
+{
+template<>
+char *toString(const DBusMenuShortcut &dmShortcut)
+{
+ QByteArray ba = "DBusMenuShortcut(";
+ Q_FOREACH(const QStringList& tokens, dmShortcut) {
+ ba += "(";
+ ba += tokens.join("+").toUtf8();
+ ba += ")";
+ }
+ ba += ")";
+ return qstrdup(ba.data());
+}
+}
+
+DBusMenuShortcut createKeyList(const QString& txt)
+{
+ DBusMenuShortcut lst;
+ QStringList tokens = txt.split(',');
+ Q_FOREACH(const QString& token, tokens) {
+ lst << token.split('+');
+ }
+ return lst;
+}
+
+#define ADD_ROW(ksArgs, klArgs) QTest::newRow(#ksArgs) << QKeySequence ksArgs << createKeyList(klArgs)
+
+void DBusMenuShortcutTest::testConverter_data()
+{
+ QTest::addColumn<QKeySequence>("keySequence");
+ QTest::addColumn<DBusMenuShortcut>("keyList");
+
+ ADD_ROW((Qt::ALT | Qt::Key_F4), "Alt+F4");
+ ADD_ROW((Qt::CTRL | Qt::Key_S), "Control+S");
+ ADD_ROW((Qt::CTRL | Qt::Key_X, Qt::ALT | Qt::SHIFT | Qt::Key_Q), "Control+X,Alt+Shift+Q");
+ ADD_ROW((Qt::META | Qt::Key_E), "Super+E");
+ ADD_ROW((Qt::CTRL | Qt::Key_Plus), "Control+plus");
+ ADD_ROW((Qt::CTRL | Qt::Key_Minus), "Control+minus");
+}
+
+void DBusMenuShortcutTest::testConverter()
+{
+ QFETCH(QKeySequence, keySequence);
+ QFETCH(DBusMenuShortcut, keyList);
+
+ DBusMenuShortcut list = DBusMenuShortcut::fromKeySequence(keySequence);
+ QCOMPARE(list, keyList);
+ QKeySequence sequence = keyList.toKeySequence();
+ QCOMPARE(sequence.toString(), keySequence.toString());
+}
+
+#include "dbusmenushortcuttest.moc"
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef DBUSMENUSHORTCUTTEST_H
+#define DBUSMENUSHORTCUTTEST_H
+
+#define QT_GUI_LIB
+#include <QtGui>
+
+// Qt
+#include <QObject>
+
+// Local
+
+class DBusMenuShortcutTest : public QObject
+{
+Q_OBJECT
+private Q_SLOTS:
+ void testConverter_data();
+ void testConverter();
+};
+
+#endif /* DBUSMENUSHORTCUTTEST_H */
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include <slowmenu.moc>
+
+#include <dbusmenuexporter.h>
+
+#include <QtDBus>
+#include <QtGui>
+#include <QApplication>
+
+static const char *TEST_SERVICE = "org.kde.dbusmenu-qt-test";
+static const char *TEST_OBJECT_PATH = "/TestMenuBar";
+
+SlowMenu::SlowMenu()
+: QMenu()
+{
+ connect(this, SIGNAL(aboutToShow()), SLOT(slotAboutToShow()));
+}
+
+void SlowMenu::slotAboutToShow()
+{
+ qDebug() << __FUNCTION__ << "Entering";
+ QTime time;
+ time.start();
+ while (time.elapsed() < 2000) {
+ qApp->processEvents();
+ }
+ qDebug() << __FUNCTION__ << "Leaving";
+}
+
+int main(int argc, char** argv)
+{
+ QApplication app(argc, argv);
+ QDBusConnection::sessionBus().registerService(TEST_SERVICE);
+ SlowMenu* inputMenu = new SlowMenu;
+ inputMenu->addAction("Test");
+ DBusMenuExporter exporter(TEST_OBJECT_PATH, inputMenu);
+ qDebug() << "Looping";
+ return app.exec();
+}
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef SLOWMENU_H
+#define SLOWMENU_H
+
+#include <QMenu>
+
+class SlowMenu : public QMenu
+{
+Q_OBJECT
+public:
+ SlowMenu();
+
+public Q_SLOTS:
+ void slotAboutToShow();
+};
+
+
+#endif /* SLOWMENU_H */
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include "testutils.h"
+
+#include <QCoreApplication>
+
+void waitForDeferredDeletes()
+{
+ while (QCoreApplication::hasPendingEvents()) {
+ QCoreApplication::sendPostedEvents();
+ QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
+ QCoreApplication::processEvents();
+ }
+}
+
+#include "testutils.moc"
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#ifndef TESTUTILS_H
+#define TESTUTILS_H
+
+// Local
+#include <debug_p.h>
+#include <dbusmenutypes_p.h>
+
+// Qt
+#include <QObject>
+#include <QMenu>
+#include <QVariant>
+
+class ManualSignalSpy : public QObject, public QList<QVariantList>
+{
+ Q_OBJECT
+public Q_SLOTS:
+ void receiveCall(int value)
+ {
+ append(QVariantList() << value);
+ }
+
+ void receiveCall(uint v1, int v2)
+ {
+ append(QVariantList() << v1 << v2);
+ }
+
+ void receiveCall(int v1, uint v2)
+ {
+ append(QVariantList() << v1 << v2);
+ }
+
+ void receiveCall(DBusMenuItemList itemList, DBusMenuItemKeysList removedPropsList)
+ {
+ QVariantList propsIds;
+ Q_FOREACH(DBusMenuItem item, itemList) {
+ propsIds << item.id;
+ }
+ QVariantList removedPropsIds;
+ Q_FOREACH(DBusMenuItemKeys props, removedPropsList) {
+ removedPropsIds << props.id;
+ }
+
+ QVariantList args;
+ args.push_back(propsIds);
+ args.push_back(removedPropsIds);
+ append(args);
+ }
+
+ void receiveCall(const QString& service, const QVariantMap& modifiedProperties, const QStringList& newProperties)
+ {
+ QVariantList args;
+ args.push_back(service);
+ args.push_back(modifiedProperties);
+ args.push_back(newProperties);
+ append(args);
+ }
+};
+
+class MenuFiller : public QObject
+{
+ Q_OBJECT
+public:
+ MenuFiller(QMenu *menu)
+ : m_menu(menu)
+ {
+ connect(m_menu, SIGNAL(aboutToShow()), SLOT(fillMenu()));
+ }
+
+ void addAction(QAction *action)
+ {
+ m_actions << action;
+ }
+
+public Q_SLOTS:
+ void fillMenu()
+ {
+ while (!m_actions.isEmpty()) {
+ m_menu->addAction(m_actions.takeFirst());
+ }
+ }
+
+private:
+ QMenu *m_menu;
+ QList<QAction *> m_actions;
+};
+
+void waitForDeferredDeletes();
+
+#endif /* TESTUTILS_H */
--- /dev/null
+find_package(QJSON)
+if (QJSON_FOUND)
+ message(STATUS "QJSON found, testapp will be built")
+ add_subdirectory(testapp)
+else (QJSON_FOUND)
+ message(STATUS "QJSON not found, testapp will not be built")
+endif (QJSON_FOUND)
--- /dev/null
+set(qtapp_SRCS
+ main.cpp
+ )
+
+add_executable(dbusmenubench-qtapp ${qtapp_SRCS})
+
+if (NOT USE_QT5)
+ # Qt4
+ include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../src
+ ${CMAKE_CURRENT_BINARY_DIR}/../../src
+ ${QT_INCLUDE_DIR}
+ ${QT_QTCORE_INCLUDE_DIR}
+ ${QT_QTGUI_INCLUDE_DIR}
+ ${QT_QTDBUS_INCLUDE_DIR}
+ ${QJSON_INCLUDE_DIR}
+ )
+
+ target_link_libraries(dbusmenubench-qtapp
+ dbusmenu-qt
+ ${QT_QTGUI_LIBRARY}
+ ${QT_QTCORE_LIBRARY}
+ ${QT_QTDBUS_LIBRARY}
+ ${QJSON_LIBRARIES}
+ )
+else()
+ # Qt5
+ include_directories(
+ ${CMAKE_CURRENT_SOURCE_DIR}/../../src
+ ${CMAKE_CURRENT_BINARY_DIR}/../../src
+ ${Qt5Widgets_INCLUDE_DIRS}
+ ${Qt5Core_INCLUDE_DIRS}
+ ${Qt5Gui_INCLUDE_DIRS}
+ ${Qt5DBus_INCLUDE_DIRS}
+ ${QJSON_INCLUDE_DIR}
+ )
+
+ target_link_libraries(dbusmenubench-qtapp
+ dbusmenu-qt5
+ ${Qt5Gui_LIBRARIES}
+ ${Qt5Core_LIBRARIES}
+ ${Qt5DBus_LIBRARIES}
+ ${Qt5Widgets_LIBRARIES}
+ ${QJSON_LIBRARIES}
+ )
+endif()
--- /dev/null
+/* This file is part of the dbusmenu-qt library
+ Copyright 2010 Canonical
+ Author: Aurelien Gateau <aurelien.gateau@canonical.com>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License (LGPL) as published by the Free Software Foundation;
+ either version 2 of the License, 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ Boston, MA 02110-1301, USA.
+*/
+#include <QApplication>
+#include <QDBusConnection>
+#include <QDebug>
+#include <QFile>
+#include <QMenu>
+
+#include <qjson/parser.h>
+
+#include <dbusmenuexporter.h>
+
+static const char *DBUS_SERVICE = "org.dbusmenu.test";
+static const char *DBUS_PATH = "/MenuBar";
+static const char *USAGE = "dbusmenubench-qtapp <path/to/menu.json>";
+
+void createMenuItem(QMenu *menu, const QVariant &item)
+{
+ QVariantMap map = item.toMap();
+
+ if (map.value("visible").toString() == "false") {
+ return;
+ }
+
+ QString type = map.value("type").toString();
+ if (type == "separator") {
+ menu->addSeparator();
+ return;
+ }
+
+ QString label = map.value("label").toString();
+ QAction *action = menu->addAction(label);
+ action->setEnabled(map.value("sensitive").toString() == "true");
+ if (map.contains("submenu")) {
+ QVariantList items = map.value("submenu").toList();
+ Q_FOREACH(const QVariant &item, items) {
+ QMenu *subMenu = new QMenu;
+ action->setMenu(subMenu);
+ createMenuItem(subMenu, item);
+ }
+ }
+}
+
+void initMenu(QMenu *menu, const QString &fileName)
+{
+ QJson::Parser parser;
+
+ QFile file(fileName);
+ if (!file.open(QIODevice::ReadOnly)) {
+ qCritical() << "Could not open file" << fileName;
+ return;
+ }
+
+ bool ok;
+ QVariant tree = parser.parse(&file, &ok);
+ if (!ok) {
+ qCritical() << "Could not parse json data from" << fileName;
+ return;
+ }
+
+ QVariantList list = tree.toList();
+ Q_FOREACH(const QVariant &item, list) {
+ createMenuItem(menu, item);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ QMenu menu;
+
+ if (argc != 2) {
+ qCritical() << USAGE;
+ return 1;
+ }
+ QString jsonFileName = argv[1];
+ initMenu(&menu, jsonFileName);
+
+ QDBusConnection connection = QDBusConnection::sessionBus();
+ if (!connection.registerService(DBUS_SERVICE)) {
+ qCritical() << "Could not register" << DBUS_SERVICE;
+ return 1;
+ }
+
+ DBusMenuExporter exporter(DBUS_PATH, &menu);
+ return app.exec();
+}