Import Upstream version 0.9.3+16.04.20160218
This commit is contained in:
commit
04ab4db09c
|
@ -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
|
||||
)
|
|
@ -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!
|
|
@ -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
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
|
@ -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)
|
|
@ -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@")
|
|
@ -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}
|
|
@ -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}
|
||||
)
|
|
@ -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<int>"/>
|
||||
<annotation name="com.trolltech.QtDBus.QtTypeName.Out0" value="DBusMenuItemList"/>
|
||||
<dox:d>
|
||||
Returns the list of items which are children of @a parentId.
|
||||
</dox:d>
|
||||
<arg type="ai" name="ids" direction="in" >
|
||||
<dox:d>
|
||||
A list of ids that we should be finding the properties
|
||||
on. If the list is empty, all menu items should be sent.
|
||||
</dox:d>
|
||||
</arg>
|
||||
<arg type="as" name="propertyNames" direction="in" >
|
||||
<dox:d>
|
||||
The list of item properties we are
|
||||
interested in. If there are no entries in the list all of
|
||||
the properties will be sent.
|
||||
</dox:d>
|
||||
</arg>
|
||||
<arg type="a(ia{sv})" name="properties" direction="out" >
|
||||
<dox:d>
|
||||
An array of property values.
|
||||
An item in this area is represented as a struct following
|
||||
this format:
|
||||
@li id unsigned the item id
|
||||
@li properties map(string => variant) the requested item properties
|
||||
</dox:d>
|
||||
</arg>
|
||||
</method>
|
||||
|
||||
<method name="GetProperty">
|
||||
<dox:d>
|
||||
Get a signal property on a single item. This is not useful if you're
|
||||
going to implement this interface, it should only be used if you're
|
||||
debugging via a commandline tool.
|
||||
</dox:d>
|
||||
<arg type="i" name="id" direction="in">
|
||||
<dox:d>the id of the item which received the event</dox:d>
|
||||
</arg>
|
||||
<arg type="s" name="name" direction="in">
|
||||
<dox:d>the name of the property to get</dox:d>
|
||||
</arg>
|
||||
<arg type="v" name="value" direction="out">
|
||||
<dox:d>the value of the property</dox:d>
|
||||
</arg>
|
||||
</method>
|
||||
|
||||
<method name="Event">
|
||||
<dox:d><![CDATA[
|
||||
This is called by the applet to notify the application an event happened on a
|
||||
menu item.
|
||||
|
||||
@a type can be one of the following:
|
||||
|
||||
@li "clicked"
|
||||
@li "hovered"
|
||||
|
||||
Vendor specific events can be added by prefixing them with "x-<vendor>-"
|
||||
]]></dox:d>
|
||||
<arg type="i" name="id" direction="in" >
|
||||
<dox:d>the id of the item which received the event</dox:d>
|
||||
</arg>
|
||||
<arg type="s" name="eventId" direction="in" >
|
||||
<dox:d>the type of event</dox:d>
|
||||
</arg>
|
||||
<arg type="v" name="data" direction="in" >
|
||||
<dox:d>event-specific data</dox:d>
|
||||
</arg>
|
||||
<arg type="u" name="timestamp" direction="in" >
|
||||
<dox:d>The time that the event occured if available or the time the message was sent if not</dox:d>
|
||||
</arg>
|
||||
</method>
|
||||
|
||||
<method name="AboutToShow">
|
||||
<dox:d>
|
||||
This is called by the applet to notify the application that it is about
|
||||
to show the menu under the specified item.
|
||||
</dox:d>
|
||||
<arg type="i" name="id" direction="in">
|
||||
<dox:d>
|
||||
Which menu item represents the parent of the item about to be shown.
|
||||
</dox:d>
|
||||
</arg>
|
||||
<arg type="b" name="needUpdate" direction="out">
|
||||
<dox:d>
|
||||
Whether this AboutToShow event should result in the menu being updated.
|
||||
</dox:d>
|
||||
</arg>
|
||||
</method>
|
||||
|
||||
<!-- Signals -->
|
||||
<signal name="ItemsPropertiesUpdated">
|
||||
<annotation name="com.trolltech.QtDBus.QtTypeName.In0" value="DBusMenuItemList"/>
|
||||
<annotation name="com.trolltech.QtDBus.QtTypeName.In1" value="DBusMenuItemKeysList"/>
|
||||
<dox:d>
|
||||
Triggered when there are lots of property updates across many items
|
||||
so they all get grouped into a single dbus message. The format is
|
||||
the ID of the item with a hashtable of names and values for those
|
||||
properties.
|
||||
</dox:d>
|
||||
<arg type="a(ia{sv})" name="updatedProps" direction="out" />
|
||||
<arg type="a(ias)" name="removedProps" direction="out" />
|
||||
</signal>
|
||||
|
||||
<signal name="LayoutUpdated">
|
||||
<dox:d>
|
||||
Triggered by the application to notify display of a layout update, up to
|
||||
revision
|
||||
</dox:d>
|
||||
<arg type="u" name="revision" direction="out" >
|
||||
<dox:d>The revision of the layout that we're currently on</dox:d>
|
||||
</arg>
|
||||
<arg type="i" name="parent" direction="out" >
|
||||
<dox:d>
|
||||
If the layout update is only of a subtree, this is the
|
||||
parent item for the entries that have changed. It is zero if
|
||||
the whole layout should be considered invalid.
|
||||
</dox:d>
|
||||
</arg>
|
||||
</signal>
|
||||
<signal name="ItemActivationRequested">
|
||||
<dox:d>
|
||||
The server is requesting that all clients displaying this
|
||||
menu open it to the user. This would be for things like
|
||||
hotkeys that when the user presses them the menu should
|
||||
open and display itself to the user.
|
||||
</dox:d>
|
||||
<arg type="i" name="id" direction="out" >
|
||||
<dox:d>ID of the menu that should be activated</dox:d>
|
||||
</arg>
|
||||
<arg type="u" name="timestamp" direction="out" >
|
||||
<dox:d>The time that the event occured</dox:d>
|
||||
</arg>
|
||||
</signal>
|
||||
|
||||
<!-- End of interesting stuff -->
|
||||
|
||||
</interface>
|
||||
</node>
|
|
@ -0,0 +1,2 @@
|
|||
/* Whether QIcon::name() exists */
|
||||
#cmakedefine HAVE_QICON_NAME
|
|
@ -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 */
|
|
@ -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"
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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"
|
|
@ -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 */
|
|
@ -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"
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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"
|
|
@ -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 */
|
|
@ -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);
|
||||
}
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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 */
|
|
@ -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;
|
||||
}
|
|
@ -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 */
|
|
@ -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()
|
|
@ -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"
|
|
@ -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 */
|
|
@ -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"
|
|
@ -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 */
|
|
@ -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"
|
|
@ -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 */
|
|
@ -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();
|
||||
}
|
|
@ -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 */
|
|
@ -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"
|
|
@ -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 */
|
|
@ -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)
|
|
@ -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()
|
|
@ -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();
|
||||
}
|
Loading…
Reference in New Issue