Import Upstream version 0.9.3+16.04.20160218

This commit is contained in:
openKylinBot 2022-05-14 03:36:26 +08:00
commit 04ab4db09c
44 changed files with 5947 additions and 0 deletions

145
CMakeLists.txt Normal file
View File

@ -0,0 +1,145 @@
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
)

481
COPYING Normal file
View File

@ -0,0 +1,481 @@
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.
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.
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.
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.
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.
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.
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.
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.
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!

277
Doxyfile.in Normal file
View File

@ -0,0 +1,277 @@
# 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

135
NEWS Normal file
View File

@ -0,0 +1,135 @@
# 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.

31
README Normal file
View File

@ -0,0 +1,31 @@
# 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

18
RELEASE_CHECK_LIST Normal file
View File

@ -0,0 +1,18 @@
- 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

View File

@ -0,0 +1,44 @@
# 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)

View File

@ -0,0 +1,5 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/dbusmenu-@QT_SUFFIX@-targets.cmake")
set_and_check(dbusmenu-@QT_SUFFIX@_INCLUDE_DIRS "@PACKAGE_INCLUDE_INSTALL_DIR@")

10
dbusmenu-qt.pc.in Normal file
View File

@ -0,0 +1,10 @@
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}

127
src/CMakeLists.txt Normal file
View File

@ -0,0 +1,127 @@
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}
)

View File

@ -0,0 +1,365 @@
<?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&lt;int&gt;"/>
<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>

2
src/dbusmenu_config.h.in Normal file
View File

@ -0,0 +1,2 @@
/* Whether QIcon::name() exists */
#cmakedefine HAVE_QICON_NAME

37
src/dbusmenu_export.h Normal file
View File

@ -0,0 +1,37 @@
/* 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 */

94
src/dbusmenu_p.cpp Normal file
View File

@ -0,0 +1,94 @@
/* 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"

59
src/dbusmenu_p.h Normal file
View File

@ -0,0 +1,59 @@
/* 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 */

39
src/dbusmenu_version.h.in Normal file
View File

@ -0,0 +1,39 @@
/* 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 */

506
src/dbusmenuexporter.cpp Normal file
View File

@ -0,0 +1,506 @@
/* 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"

95
src/dbusmenuexporter.h Normal file
View File

@ -0,0 +1,95 @@
/* 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 */

View File

@ -0,0 +1,186 @@
/* 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"

View File

@ -0,0 +1,76 @@
/* 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 */

View File

@ -0,0 +1,91 @@
/* 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 */

585
src/dbusmenuimporter.cpp Normal file
View File

@ -0,0 +1,585 @@
/* 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"

143
src/dbusmenuimporter.h Normal file
View File

@ -0,0 +1,143 @@
/* 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 */

View File

@ -0,0 +1,85 @@
/* 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);
}

43
src/dbusmenushortcut_p.h Normal file
View File

@ -0,0 +1,43 @@
/* 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 */

112
src/dbusmenutypes_p.cpp Normal file
View File

@ -0,0 +1,112 @@
/* 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;
}

96
src/dbusmenutypes_p.h Normal file
View File

@ -0,0 +1,96 @@
/* 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 */

48
src/debug_p.h Normal file
View File

@ -0,0 +1,48 @@
/* 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 */

64
src/utils.cpp Normal file
View File

@ -0,0 +1,64 @@
/* 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;
}

31
src/utils_p.h Normal file
View File

@ -0,0 +1,31 @@
/* 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 */

124
tests/CMakeLists.txt Normal file
View File

@ -0,0 +1,124 @@
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()

View File

@ -0,0 +1,815 @@
/* 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"

View File

@ -0,0 +1,63 @@
/* 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 */

View File

@ -0,0 +1,343 @@
/* 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"

View File

@ -0,0 +1,51 @@
/* 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 */

View File

@ -0,0 +1,85 @@
/* 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"

View File

@ -0,0 +1,40 @@
/* 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 */

58
tests/slowmenu.cpp Normal file
View File

@ -0,0 +1,58 @@
/* 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();
}

37
tests/slowmenu.h Normal file
View File

@ -0,0 +1,37 @@
/* 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 */

34
tests/testutils.cpp Normal file
View File

@ -0,0 +1,34 @@
/* 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"

109
tests/testutils.h Normal file
View File

@ -0,0 +1,109 @@
/* 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 */

7
tools/CMakeLists.txt Normal file
View File

@ -0,0 +1,7 @@
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)

View File

@ -0,0 +1,46 @@
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()

105
tools/testapp/main.cpp Normal file
View File

@ -0,0 +1,105 @@
/* 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();
}