commit 512032c43f85aec4bb224b1bb9f1d8fe09557bb7 Author: Andrew Lee (李健秋) Date: Wed Aug 12 03:27:51 2015 +0800 Adding upstream version 0.9.0. Signed-off-by: Andrew Lee (李健秋) diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..378eac2 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..190d298 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,10 @@ +Upstream Authors: + LXQt team: http://lxqt.org + Razor team: http://razor-qt.org + +Copyright: + Copyright (c) 2010-2012 Razor team + Copyright (c) 2012-2014 LXQt team + +License: GPL-2 and LGPL-2.1+ +The full text of the licenses can be found in the 'COPYING' file. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..34e30ed --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,81 @@ +cmake_minimum_required(VERSION 2.8.11) + +project(lxqt-globalkeys) + +# additional cmake files +set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + +option(UPDATE_TRANSLATIONS "Update source translation translations/*.ts files" OFF) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +# Set default library installation path +set(LIB_INSTALL_DIR "lib${LIB_SUFFIX}" CACHE PATH "Installation directory for libraries") + +set(CMAKE_INCLUDE_CURRENT_DIR ON) +set(CMAKE_POSITION_INDEPENDENT_CODE ON) +set(CMAKE_AUTOMOC ON) +find_package(Qt5Widgets REQUIRED) +find_package(Qt5DBus REQUIRED) +find_package(Qt5LinguistTools REQUIRED QUIET) +find_package(lxqt REQUIRED QUIET) +include(${LXQT_USE_FILE}) + +include(LXQtTranslate) + +# Standard directories for installation +include(GNUInstallDirs) + +set(LXQT_GLOBALKEYS_LIBRARY_NAME lxqt-globalkeys) +set(LXQT_GLOBALKEYS_UI_LIBRARY_NAME lxqt-globalkeys-ui) +set(LXQT_INSTALL_CMAKE_DIR ${CMAKE_INSTALL_FULL_LIBDIR}/cmake) + +#************************************************ +# Build config.cmake files +#************************************************ +#set(LXQT_SHARE_DIR ${CMAKE_INSTALL_PREFIX}/share/lxqt) +#set(LXQT_TRANSLATIONS_DIR ${LXQT_SHARE_DIR}/translations) +include(cmake/FindInstallConfigPath.cmake) + +set(LXQT_GLOBALKEYS_CMAKE_NAME ${LXQT_GLOBALKEYS_LIBRARY_NAME}) +set(LXQT_GLOBALKEYS_UI_CMAKE_NAME ${LXQT_GLOBALKEYS_UI_LIBRARY_NAME}) + +set(LXQT_GLOBALKEYS_INCLUDE_DIR "${CMAKE_INSTALL_PREFIX}/include/${LXQT_GLOBALKEYS_LIBRARY_NAME}") +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/lxqt_globalkeys-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/${LXQT_GLOBALKEYS_CMAKE_NAME}-config.cmake" + @ONLY +) + +set(LXQT_GLOBALKEYS_UI_INCLUDE_DIR ${CMAKE_INSTALL_PREFIX}/include/${LXQT_GLOBALKEYS_UI_LIBRARY_NAME}) +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/lxqt_globalkeys_ui-config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/${LXQT_GLOBALKEYS_UI_CMAKE_NAME}-config.cmake" + @ONLY +) +#************************************************ +# End of build config.cmake +#************************************************ +set(LXQT_GLOBALKEYS_SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}") +add_subdirectory(daemon) +add_subdirectory(config) +add_subdirectory(client) +add_subdirectory(ui) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LXQT_GLOBALKEYS_CMAKE_NAME}-config.cmake DESTINATION ${LXQT_INSTALL_CMAKE_DIR}/${LXQT_GLOBALKEYS_CMAKE_NAME}) +install(FILES cmake/lxqt_globalkeys_use.cmake DESTINATION ${LXQT_INSTALL_CMAKE_DIR}/${LXQT_GLOBALKEYS_CMAKE_NAME}) + +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${LXQT_GLOBALKEYS_UI_CMAKE_NAME}-config.cmake DESTINATION ${LXQT_INSTALL_CMAKE_DIR}/${LXQT_GLOBALKEYS_UI_CMAKE_NAME}) +install(FILES cmake/lxqt_globalkeys_ui_use.cmake DESTINATION ${LXQT_INSTALL_CMAKE_DIR}/${LXQT_GLOBALKEYS_UI_CMAKE_NAME}) + +# building tarball with CPack ------------------------------------------------- +include(InstallRequiredSystemLibraries) +set(CPACK_PACKAGE_VERSION_MAJOR ${LXQT_MAJOR_VERSION}) +set(CPACK_PACKAGE_VERSION_MINOR ${LXQT_MINOR_VERSION}) +set(CPACK_PACKAGE_VERSION_PATCH ${LXQT_PATCH_VERSION}) +set(CPACK_GENERATOR TBZ2) +set(CPACK_SOURCE_GENERATOR TBZ2) +set(CPACK_SOURCE_IGNORE_FILES /build/;.gitignore;.*~;.git;.kdev4;temp) +include(CPack) diff --git a/COPYING b/COPYING new file mode 100644 index 0000000..a8dd823 --- /dev/null +++ b/COPYING @@ -0,0 +1,461 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 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 Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + 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 Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +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 and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +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 other code 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. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes a de-facto standard. To achieve this, non-free programs must +be allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + 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, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser 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 combine 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) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) 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. + + d) 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. + + e) 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 materials to be 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 with +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 Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +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 diff --git a/client/CMakeLists.txt b/client/CMakeLists.txt new file mode 100644 index 0000000..0ba5c56 --- /dev/null +++ b/client/CMakeLists.txt @@ -0,0 +1,177 @@ +set(PROJECT_NAME ${LXQT_GLOBALKEYS_LIBRARY_NAME}) +project(${PROJECT_NAME}) + +set(QT_DBUS_PREFIX "org.qtproject") + +set(${PROJECT_NAME}_HEADER_NAMESPACE "LXQtGlobalKeys") + +include_directories( + ${PROJECT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} +) + +set(${PROJECT_NAME}_PATH_TO_DAEMON "${LXQT_GLOBALKEYS_SOURCE_DIR}/daemon") + +add_definitions(-DSHARED_EXPORT=Q_DECL_EXPORT) + +set(${PROJECT_NAME}_SOURCES + client.cpp + action.cpp +) + +# +# WARNING: Changing stuff here implies changing this: +# install(FILES ${${PROJECT_NAME}_MAIN_HEADER} DESTINATION include/${PROJECT_NAME} COMPONENT development RENAME "lxqtglobalkeys.h") +# +set(${PROJECT_NAME}_MAIN_HEADER + lxqt-globalkeys.h +) + +set(${PROJECT_NAME}_PUBLIC_CPP_HEADERS + client.h + action.h +) + +set(${PROJECT_NAME}_PUBLIC_CLASSES + Client + Action + LXQtGlobalKeys +) + +set(${PROJECT_NAME}_PRIVATE_CPP_HEADERS + client_p.h + action_p.h +) + +set(${PROJECT_NAME}_FORMS +) + +set(${PROJECT_NAME}_RESOURCES +) + +set(${PROJECT_NAME}_TRANSLATIONS +) + +set(${PROJECT_NAME}_DBUS_ADAPTORS + ${${PROJECT_NAME}_PATH_TO_DAEMON}/org.lxqt.global_key_shortcuts.client.xml +) + +set_source_files_properties(${${PROJECT_NAME}_PATH_TO_DAEMON}/org.lxqt.global_key_shortcuts.client.xml PROPERTIES + INCLUDE action_p.h + PARENT_CLASSNAME GlobalKeyShortcut::ActionImpl + BASENAME org.lxqt.global_key_shortcuts.client + CLASSNAME OrgLxqtActionClientAdaptor +) + +set(${PROJECT_NAME}_DBUS_INTERFACES + ${${PROJECT_NAME}_PATH_TO_DAEMON}/org.lxqt.global_key_shortcuts.native.xml +) + +set_source_files_properties(${${PROJECT_NAME}_PATH_TO_DAEMON}/org.lxqt.global_key_shortcuts.native.xml PROPERTIES + BASENAME org.lxqt.global_key_shortcuts.native +) + +set(${PROJECT_NAME}_PUBLIC_HEADERS + ${${PROJECT_NAME}_PUBLIC_CPP_HEADERS} +) + +set(${PROJECT_NAME}_PRIVATE_HEADERS + ${${PROJECT_NAME}_PRIVATE_CPP_HEADERS} +) + +set(${PROJECT_NAME}_CPP_HEADERS + ${${PROJECT_NAME}_PUBLIC_CPP_HEADERS} + ${${PROJECT_NAME}_PRIVATE_CPP_HEADERS} +) + +set(${PROJECT_NAME}_HEADERS + ${${PROJECT_NAME}_PUBLIC_HEADERS} + ${${PROJECT_NAME}_PRIVATE_HEADERS} +) + +set(${PROJECT_NAME}_TRANSLATABLE + ${${PROJECT_NAME}_SOURCES} + ${${PROJECT_NAME}_HEADERS} + ${${PROJECT_NAME}_FORMS} +) + +foreach(DBUS_ADAPTOR ${${PROJECT_NAME}_DBUS_ADAPTORS}) + get_filename_component(DBUS_ADAPTOR_FILENAME ${DBUS_ADAPTOR} NAME) + configure_file( + ${DBUS_ADAPTOR} + "${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME}" + @ONLY + ) + get_source_file_property(DBUS_ADAPTOR_INCLUDE ${DBUS_ADAPTOR} INCLUDE) + get_source_file_property(DBUS_ADAPTOR_PARENT_CLASSNAME ${DBUS_ADAPTOR} PARENT_CLASSNAME) + get_source_file_property(DBUS_ADAPTOR_BASENAME ${DBUS_ADAPTOR} BASENAME) + get_source_file_property(DBUS_ADAPTOR_CLASSNAME ${DBUS_ADAPTOR} CLASSNAME) + if(DBUS_ADAPTOR_BASENAME) + if(DBUS_ADAPTOR_CLASSNAME) + qt5_add_dbus_adaptor(${PROJECT_NAME}_DBUS_ADAPTOR_FILES "${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME}" ${DBUS_ADAPTOR_INCLUDE} ${DBUS_ADAPTOR_PARENT_CLASSNAME} ${DBUS_ADAPTOR_BASENAME} ${DBUS_ADAPTOR_CLASSNAME}) + else() + qt5_add_dbus_adaptor(${PROJECT_NAME}_DBUS_ADAPTOR_FILES "${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME}" ${DBUS_ADAPTOR_INCLUDE} ${DBUS_ADAPTOR_PARENT_CLASSNAME} ${DBUS_ADAPTOR_BASENAME}) + endif() + else() + qt5_add_dbus_adaptor(${PROJECT_NAME}_DBUS_ADAPTOR_FILES "${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME}" ${DBUS_ADAPTOR_INCLUDE} ${DBUS_ADAPTOR_PARENT_CLASSNAME}) + endif() +endforeach() + +foreach(DBUS_INTERFACE ${${PROJECT_NAME}_DBUS_INTERFACES}) + get_filename_component(DBUS_INTERFACE_FILENAME ${DBUS_INTERFACE} NAME) + configure_file( + ${DBUS_INTERFACE} + "${CMAKE_CURRENT_BINARY_DIR}/${DBUS_INTERFACE_FILENAME}" + @ONLY + ) + get_source_file_property(DBUS_INTERFACE_BASENAME ${DBUS_INTERFACE} BASENAME) + get_source_file_property(DBUS_INTERFACE_INCLUDE ${DBUS_INTERFACE} INCLUDE) + get_source_file_property(DBUS_INTERFACE_CLASSNAME ${DBUS_INTERFACE} CLASSNAME) + get_source_file_property(DBUS_INTERFACE_NO_NAMESPACE ${DBUS_INTERFACE} NO_NAMESPACE) + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${DBUS_INTERFACE_FILENAME} PROPERTIES + INCLUDE ${DBUS_INTERFACE_INCLUDE} + CLASSNAME ${DBUS_INTERFACE_CLASSNAME} + NO_NAMESPACE ${DBUS_INTERFACE_NO_NAMESPACE} + ) + qt5_add_dbus_interface(${PROJECT_NAME}_DBUS_INTERFACE_FILES ${CMAKE_CURRENT_BINARY_DIR}/${DBUS_INTERFACE_FILENAME} ${DBUS_INTERFACE_BASENAME}) +endforeach() + +qt5_add_resources(${PROJECT_NAME}_RESOURCE_FILES ${${PROJECT_NAME}_RESOURCES}) +qt5_wrap_ui(${PROJECT_NAME}_FORM_FILES ${${PROJECT_NAME}_FORMS}) + +set(${PROJECT_NAME}_GENERATED_FILES + ${${PROJECT_NAME}_FORM_FILES} + ${${PROJECT_NAME}_RESOURCE_FILES} + ${${PROJECT_NAME}_QM_FILES} + ${${PROJECT_NAME}_DBUS_INTERFACE_FILES} + ${${PROJECT_NAME}_DBUS_ADAPTOR_FILES} +) + +set(${PROJECT_NAME}_ALL_FILES + ${${PROJECT_NAME}_SOURCES} + ${${PROJECT_NAME}_HEADERS} + ${${PROJECT_NAME}_GENERATED_FILES} +) + +add_library(${PROJECT_NAME} SHARED ${${PROJECT_NAME}_ALL_FILES}) +target_link_libraries(${PROJECT_NAME} Qt5::Widgets Qt5::DBus) +set_target_properties(${PROJECT_NAME} + PROPERTIES + VERSION ${LXQT_VERSION} + SOVERSION ${LXQT_MAJOR_VERSION} +) + +include(create_portable_headers) + +create_portable_headers(${PROJECT_NAME}_PORTABLE_HEADERS + NAMESPACE "${${PROJECT_NAME}_NAMESPACE}/" + FILENAMES ${${PROJECT_NAME}_PUBLIC_CLASSES} +) +install(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR} COMPONENT runtime) +install(FILES ${${PROJECT_NAME}_MAIN_HEADER} DESTINATION include/${PROJECT_NAME} COMPONENT development) +install(FILES ${${PROJECT_NAME}_MAIN_HEADER} DESTINATION include/${PROJECT_NAME} COMPONENT development RENAME "lxqtglobalkeys.h") +install(FILES ${${PROJECT_NAME}_PORTABLE_HEADERS} DESTINATION include/${PROJECT_NAME}/${${PROJECT_NAME}_HEADER_NAMESPACE} COMPONENT development) +install(FILES ${${PROJECT_NAME}_PUBLIC_HEADERS} DESTINATION include/${PROJECT_NAME} COMPONENT development) + +include(create_pkgconfig_file) +create_pkgconfig_file(${PROJECT_NAME} "LXQt global key shortcuts client library") diff --git a/client/action.cpp b/client/action.cpp new file mode 100644 index 0000000..54017e0 --- /dev/null +++ b/client/action.cpp @@ -0,0 +1,130 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "action.h" +#include "action_p.h" +#include "client_p.h" + +#include "org.lxqt.global_key_shortcuts.client.h" + + +namespace GlobalKeyShortcut +{ + +ActionImpl::ActionImpl(ClientImpl *client, Action *interface, const QString &path, const QString &description, QObject *parent) + : QObject(parent) + , mClient(client) + , mInterface(interface) + , mPath(path) + , mDescription(description) +{ + new OrgLxqtActionClientAdaptor(this); + + connect(this, SIGNAL(emitActivated()), mInterface, SIGNAL(activated())); + connect(this, SIGNAL(emitShortcutChanged(QString, QString)), mInterface, SIGNAL(shortcutChanged(QString, QString))); +} + +ActionImpl::~ActionImpl() +{ + mClient->removeAction(this); +} + +QString ActionImpl::changeShortcut(const QString &shortcut) +{ + mShortcut = mClient->changeClientActionShortcut(mPath, shortcut); + return mShortcut; +} + +bool ActionImpl::changeDescription(const QString &description) +{ + bool result = mClient->modifyClientAction(mPath, description); + if (result) + { + mDescription = description; + } + return result; +} + +void ActionImpl::setShortcut(const QString &shortcut) +{ + mShortcut = shortcut; +} + +QString ActionImpl::path() const +{ + return mPath; +} + +QString ActionImpl::shortcut() const +{ + return mShortcut; +} + +QString ActionImpl::description() const +{ + return mDescription; +} + +void ActionImpl::setValid(bool valid) +{ + mValid = valid; +} + +bool ActionImpl::isValid() const +{ + return mValid; +} + +void ActionImpl::activated() +{ + emit emitActivated(); +} + +void ActionImpl::shortcutChanged(const QString &oldShortcut, const QString &newShortcut) +{ + emit emitShortcutChanged(oldShortcut, newShortcut); +} + + +Action::Action(QObject *parent) + : QObject(parent) + , impl(0) +{ +} + +Action::~Action() +{ +} + +QString Action::changeShortcut(const QString &shortcut) { return impl->changeShortcut(shortcut); } +bool Action::changeDescription(const QString &description) { return impl->changeDescription(description); } +QString Action::path() const { return impl->path(); } +QString Action::shortcut() const { return impl->shortcut(); } +QString Action::description() const { return impl->description(); } +bool Action::isValid() const { return impl->isValid(); } + +} diff --git a/client/action.h b/client/action.h new file mode 100644 index 0000000..bb40573 --- /dev/null +++ b/client/action.h @@ -0,0 +1,78 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_KEY_SHORTCUT_CLIENT__ACTION__INCLUDED +#define GLOBAL_KEY_SHORTCUT_CLIENT__ACTION__INCLUDED + +#include + +#include +#include + + +namespace GlobalKeyShortcut +{ + +class ActionImpl; +class ClientImpl; + +#ifndef SHARED_EXPORT +#define SHARED_EXPORT Q_DECL_IMPORT +#endif + +class SHARED_EXPORT Action : public QObject +{ + Q_OBJECT + + friend class ActionImpl; + friend class ClientImpl; + +public: + ~Action(); + + QString changeShortcut(const QString &shortcut); + bool changeDescription(const QString &description); + + QString path() const; + QString shortcut() const; + QString description() const; + + bool isValid() const; + +signals: + void activated(); + void shortcutChanged(const QString &oldShortcut, const QString &newShortcut); + +private: + Action(QObject *parent = 0); + + ActionImpl *impl; +}; + +} + +#endif // GLOBAL_KEY_SHORTCUT_CLIENT__ACTION__INCLUDED diff --git a/client/action_p.h b/client/action_p.h new file mode 100644 index 0000000..0a707ec --- /dev/null +++ b/client/action_p.h @@ -0,0 +1,83 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_KEY_SHORTCUT_CLIENT__ACTION__IMPL__INCLUDED +#define GLOBAL_KEY_SHORTCUT_CLIENT__ACTION__IMPL__INCLUDED + +#include +#include +#include +#include + + +namespace GlobalKeyShortcut +{ + +class Action; +class ClientImpl; + +class ActionImpl : public QObject +{ + Q_OBJECT + +public: + ActionImpl(ClientImpl *client, Action *interface, const QString &path, const QString &description, QObject *parent = 0); + ~ActionImpl(); + + QString changeShortcut(const QString &shortcut); + bool changeDescription(const QString &description); + + void setShortcut(const QString &shortcut); + + QString path() const; + QString shortcut() const; + QString description() const; + + void setValid(bool valid); + bool isValid() const; + +public slots: + void activated(); + void shortcutChanged(const QString &oldShortcut, const QString &newShortcut); + +signals: + void emitActivated(); + void emitShortcutChanged(const QString &oldShortcut, const QString &newShortcut); + +private: + ClientImpl *mClient; + Action *mInterface; + QString mAlias; + QString mPath; + QString mShortcut; + QString mDescription; + bool mValid; +}; + +} + +#endif // GLOBAL_KEY_SHORTCUT_CLIENT__ACTION__IMPL__INCLUDED diff --git a/client/client.cpp b/client/client.cpp new file mode 100644 index 0000000..3f4a7a0 --- /dev/null +++ b/client/client.cpp @@ -0,0 +1,300 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "client.h" +#include "client_p.h" +#include "action_p.h" +#include "org.lxqt.global_key_shortcuts.native.h" + +#include + + +namespace GlobalKeyShortcut +{ + +ClientImpl::ClientImpl(Client *interface, QObject *parent) + : QObject(parent) + , mInterface(interface) + , mServiceWatcher(new QDBusServiceWatcher("org.lxqt.global_key_shortcuts", QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this)) + , mDaemonPresent(false) +{ + connect(mServiceWatcher, SIGNAL(serviceUnregistered(QString)), this, SLOT(daemonDisappeared(QString))); + connect(mServiceWatcher, SIGNAL(serviceRegistered(QString)), this, SLOT(daemonAppeared(QString))); + mProxy = new org::lxqt::global_key_shortcuts::native("org.lxqt.global_key_shortcuts", "/native", QDBusConnection::sessionBus(), this); + mDaemonPresent = mProxy->isValid(); + + connect(this, SIGNAL(emitShortcutGrabbed(QString)), mInterface, SIGNAL(shortcutGrabbed(QString))); + connect(this, SIGNAL(emitGrabShortcutFailed()), mInterface, SIGNAL(grabShortcutFailed())); + connect(this, SIGNAL(emitGrabShortcutCancelled()), mInterface, SIGNAL(grabShortcutCancelled())); + connect(this, SIGNAL(emitGrabShortcutTimedout()), mInterface, SIGNAL(grabShortcutTimedout())); + connect(this, SIGNAL(emitDaemonDisappeared()), mInterface, SIGNAL(daemonDisappeared())); + connect(this, SIGNAL(emitDaemonAppeared()), mInterface, SIGNAL(daemonAppeared())); + connect(this, SIGNAL(emitDaemonPresenceChanged(bool)), mInterface, SIGNAL(daemonPresenceChanged(bool))); +} + +ClientImpl::~ClientImpl() +{ + QMap::iterator M = mActions.end(); + for (QMap::iterator I = mActions.begin(); I != M; ++I) + { + QDBusConnection::sessionBus().unregisterObject(QString("/global_key_shortcuts") + I.key()); + + delete I.value(); + } + mActions.clear(); +} + +void ClientImpl::daemonDisappeared(const QString &) +{ + mDaemonPresent = false; + emit emitDaemonDisappeared(); + emit emitDaemonPresenceChanged(mDaemonPresent); +} + +void ClientImpl::daemonAppeared(const QString &) +{ + QMap::iterator last = mActions.end(); + for (QMap::iterator I = mActions.begin(); I != last; ++I) + { + ActionImpl *globalActionImpl = I.value()->impl; + + QDBusPendingReply reply = mProxy->addClientAction(globalActionImpl->shortcut(), QDBusObjectPath(globalActionImpl->path()), globalActionImpl->description()); + reply.waitForFinished(); + globalActionImpl->setValid(!reply.isError() && reply.argumentAt<1>()); + + if (globalActionImpl->isValid()) + { + globalActionImpl->setShortcut(reply.argumentAt<0>()); + } + } + mDaemonPresent = true; + emit emitDaemonAppeared(); + emit emitDaemonPresenceChanged(mDaemonPresent); +} + +bool ClientImpl::isDaemonPresent() const +{ + return mDaemonPresent; +} + +Action *ClientImpl::addClientAction(const QString &shortcut, const QString &path, const QString &description, QObject *parent) +{ + if (!QRegExp("(/[A-Za-z0-9_]+){2,}").exactMatch(path)) + { + return 0; + } + + if (mActions.contains(path)) + { + return 0; + } + + Action *globalAction = new Action(parent); + + ActionImpl *globalActionImpl = new ActionImpl(this, globalAction, path, description, globalAction); + globalAction->impl = globalActionImpl; + + if (!QDBusConnection::sessionBus().registerObject(QString("/global_key_shortcuts") + path, globalActionImpl)) + { + return 0; + } + + if (mDaemonPresent) + { + QDBusPendingReply reply = mProxy->addClientAction(shortcut, QDBusObjectPath(path), description); + reply.waitForFinished(); + globalActionImpl->setValid(!reply.isError() && reply.argumentAt<1>()); + if (globalActionImpl->isValid()) + { + globalActionImpl->setShortcut(reply.argumentAt<0>()); + } + } + else + { + globalActionImpl->setValid(false); + } + + mActions[path] = globalAction; + + + return globalAction; +} + +void removeAction(Action *action); + +QString ClientImpl::changeClientActionShortcut(const QString &path, const QString &shortcut) +{ + if (!mActions.contains(path)) + { + return QString(); + } + + QDBusPendingReply reply = mProxy->changeClientActionShortcut(QDBusObjectPath(path), shortcut); + reply.waitForFinished(); + if (reply.isError()) + { + return QString(); + } + + return reply.argumentAt<0>(); +} + +bool ClientImpl::modifyClientAction(const QString &path, const QString &description) +{ + if (!mActions.contains(path)) + { + return false; + } + + QDBusPendingReply reply = mProxy->modifyClientAction(QDBusObjectPath(path), description); + reply.waitForFinished(); + if (reply.isError()) + { + return false; + } + + return reply.argumentAt<0>(); +} + +bool ClientImpl::removeClientAction(const QString &path) +{ + if (!mActions.contains(path)) + { + return false; + } + + QDBusPendingReply reply = mProxy->removeClientAction(QDBusObjectPath(path)); + reply.waitForFinished(); + if (reply.isError()) + { + return false; + } + + QDBusConnection::sessionBus().unregisterObject(QString("/global_key_shortcuts") + path); + + mActions[path]->disconnect(); + mActions.remove(path); + + return reply.argumentAt<0>(); +} + +void ClientImpl::removeAction(ActionImpl *actionImpl) +{ + QString path = actionImpl->path(); + if (!mActions.contains(path)) + { + return; + } + + QDBusPendingReply reply = mProxy->deactivateClientAction(QDBusObjectPath(path)); + reply.waitForFinished(); + + QDBusConnection::sessionBus().unregisterObject(QString("/global_key_shortcuts") + path); + + mActions[path]->disconnect(); + mActions.remove(path); +} + +void ClientImpl::grabShortcut(uint timeout) +{ + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(mProxy->grabShortcut(timeout), this); + + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher *)), this, SLOT(grabShortcutFinished(QDBusPendingCallWatcher *))); +} + +void ClientImpl::cancelShortcutGrab() +{ + mProxy->cancelShortcutGrab(); +} + +void ClientImpl::grabShortcutFinished(QDBusPendingCallWatcher *call) +{ + QDBusPendingReply reply = *call; + if (reply.isError()) + { + emit emitGrabShortcutFailed(); + } + else + { + if (reply.argumentAt<1>()) + { + emit emitGrabShortcutFailed(); + } + else + { + if (reply.argumentAt<2>()) + { + emit emitGrabShortcutCancelled(); + } + else + { + if (reply.argumentAt<3>()) + { + emit emitGrabShortcutTimedout(); + } + else + { + emit emitShortcutGrabbed(reply.argumentAt<0>()); + } + } + } + } + + call->deleteLater(); +} + + +static Client *globalActionNativeClient = 0; + +Client *Client::instance() +{ + if (!globalActionNativeClient) + { + globalActionNativeClient = new Client(); + } + + return globalActionNativeClient; +} + +Client::Client() + : QObject(0) + , impl(new ClientImpl(this, this)) +{ +} + +Client::~Client() +{ + globalActionNativeClient = 0; +} + +Action *Client::addAction(const QString &shortcut, const QString &path, const QString &description, QObject *parent) { return impl->addClientAction(shortcut, path, description, parent); } +bool Client::removeAction(const QString &path) { return impl->removeClientAction(path); } +void Client::grabShortcut(uint timeout) { impl->grabShortcut(timeout); } +void Client::cancelShortcutGrab() { impl->cancelShortcutGrab(); } +bool Client::isDaemonPresent() const { return impl->isDaemonPresent(); } + +} diff --git a/client/client.h b/client/client.h new file mode 100644 index 0000000..9b955a2 --- /dev/null +++ b/client/client.h @@ -0,0 +1,82 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_KEY_SHORTCUT_CLIENT__CLIENT__INCLUDED +#define GLOBAL_KEY_SHORTCUT_CLIENT__CLIENT__INCLUDED + +#include + +#include +#include + + +namespace GlobalKeyShortcut +{ + +class Action; +class ClientImpl; + +#ifndef SHARED_EXPORT +#define SHARED_EXPORT Q_DECL_IMPORT +#endif + +class SHARED_EXPORT Client : public QObject +{ + Q_OBJECT + +public: + static Client *instance(); + ~Client(); + + Action *addAction(const QString &shortcut, const QString &path, const QString &description, QObject *parent = 0); + bool removeAction(const QString &path); + + bool isDaemonPresent() const; + +public slots: + void grabShortcut(uint timeout); + void cancelShortcutGrab(); + +signals: + void shortcutGrabbed(const QString &); + void grabShortcutFailed(); + void grabShortcutCancelled(); + void grabShortcutTimedout(); + + void daemonDisappeared(); + void daemonAppeared(); + void daemonPresenceChanged(bool); + +private: + Client(); + + ClientImpl *impl; +}; + +} + +#endif // GLOBAL_KEY_SHORTCUT_CLIENT__CLIENT__INCLUDED diff --git a/client/client_adaptor.cpp b/client/client_adaptor.cpp new file mode 100644 index 0000000..30ee3ee --- /dev/null +++ b/client/client_adaptor.cpp @@ -0,0 +1,47 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "client_adaptor.hpp" + +#include "org.lxqt.global_action.client.h" + + +ClientAdaptor::ClientAdaptor(const QString &path, QObject *parent) + : QObject(parent) + , mPath(path) +{ +} + +void ClientAdaptor::activated() +{ + emit on_activated(mPath); +} + +void ClientAdaptor::shortcutChanged(const QString &oldShortcut, const QString &newShortcut) +{ + emit on_shortcutChanged(mPath, oldShortcut, newShortcut); +} diff --git a/client/client_adaptor.h b/client/client_adaptor.h new file mode 100644 index 0000000..f2246b1 --- /dev/null +++ b/client/client_adaptor.h @@ -0,0 +1,51 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_NATIVE_CLIENT__CLIENT_ADAPTOR__INCLUDED +#define GLOBAL_ACTION_NATIVE_CLIENT__CLIENT_ADAPTOR__INCLUDED + +#include + +class ClientAdaptor : public QObject +{ + Q_OBJECT +public: + explicit ClientAdaptor(const QString &path, QObject *parent = 0); + +signals: + void on_activated(const QString &path); + void on_shortcutChanged(const QString &path, const QString &oldShortcut, const QString &newShortcut); + +public slots: + void activated(); + void shortcutChanged(const QString &oldShortcut, const QString &newShortcut); + +private: + QString mPath; +}; + +#endif // GLOBAL_ACTION_NATIVE_CLIENT__CLIENT_ADAPTOR__INCLUDED diff --git a/client/client_p.h b/client/client_p.h new file mode 100644 index 0000000..86ae596 --- /dev/null +++ b/client/client_p.h @@ -0,0 +1,105 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_KEY_SHORTCUT_CLIENT__CLIENT__IMPL__INCLUDED +#define GLOBAL_KEY_SHORTCUT_CLIENT__CLIENT__IMPL__INCLUDED + +#include +#include +#include +#include + +#include "action.h" + + +class OrgLxqtGlobal_key_shortcutsNativeInterface; +namespace org +{ +namespace lxqt +{ +namespace global_key_shortcuts +{ +typedef ::OrgLxqtGlobal_key_shortcutsNativeInterface native; +} +} +} + +class QDBusServiceWatcher; + +namespace GlobalKeyShortcut +{ +class Client; + +class ClientAdaptor; + +class ClientImpl : public QObject +{ + Q_OBJECT + +public: + ClientImpl(Client *interface, QObject *parent = 0); + ~ClientImpl(); + + Action *addClientAction(const QString &shortcut, const QString &path, const QString &description, QObject *parent); + + QString changeClientActionShortcut(const QString &path, const QString &shortcut); + bool modifyClientAction(const QString &path, const QString &description); + bool removeClientAction(const QString &path); + + void removeAction(ActionImpl *action); + + void grabShortcut(uint timeout); + void cancelShortcutGrab(); + + bool isDaemonPresent() const; + +public slots: + void grabShortcutFinished(QDBusPendingCallWatcher *call); + void daemonDisappeared(const QString &); + void daemonAppeared(const QString &); + +signals: + void emitShortcutGrabbed(const QString &); + void emitGrabShortcutFailed(); + void emitGrabShortcutCancelled(); + void emitGrabShortcutTimedout(); + + void emitDaemonDisappeared(); + void emitDaemonAppeared(); + void emitDaemonPresenceChanged(bool); + +private: + Client *mInterface; + org::lxqt::global_key_shortcuts::native *mProxy; + QMap mActions; + QDBusServiceWatcher *mServiceWatcher; + bool mDaemonPresent; +}; + +} + +#endif // GLOBAL_KEY_SHORTCUT_CLIENT__CLIENT__IMPL__INCLUDED diff --git a/client/lxqt-globalkeys.h b/client/lxqt-globalkeys.h new file mode 100644 index 0000000..5b34431 --- /dev/null +++ b/client/lxqt-globalkeys.h @@ -0,0 +1,34 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_KEY_SHORTCUT_CLIENT__INCLUDED +#define GLOBAL_KEY_SHORTCUT_CLIENT__INCLUDED + +#include +#include + +#endif // GLOBAL_KEY_SHORTCUT_CLIENT__INCLUDED diff --git a/cmake/FindInstallConfigPath.cmake b/cmake/FindInstallConfigPath.cmake new file mode 100644 index 0000000..62dadb4 --- /dev/null +++ b/cmake/FindInstallConfigPath.cmake @@ -0,0 +1,20 @@ +# XDG standards expects system-wide configuration files in the /etc/xdg/lxqt location. +# Unfortunately QSettings we are using internally can be overriden in the Qt compilation +# time to use different path for system-wide configs. (for example configure ... -sysconfdir /etc/settings ...) +# This path can be found calling Qt4's qmake: +# qmake -query QT_INSTALL_CONFIGURATION +# +if(NOT DEFINED LXQT_ETC_XDG_DIR) + if(NOT QT_QMAKE_EXECUTABLE) + message(FATAL_ERROR "LXQT_ETC_XDG_DIR: qmake not found or wrongly detected (inlude before qt configured?)") + endif() + + execute_process(COMMAND ${QT_QMAKE_EXECUTABLE} -query QT_INSTALL_CONFIGURATION + OUTPUT_VARIABLE LXQT_ETC_XDG_DIR + OUTPUT_STRIP_TRAILING_WHITESPACE) + + message(STATUS "LXQT_ETC_XDG_DIR autodetected as '${LXQT_ETC_XDG_DIR}'") + message(STATUS "You can set it manually with -DLXQT_ETC_XDG_DIR=") + message(STATUS "") +endif () + diff --git a/cmake/create_pkgconfig_file.cmake b/cmake/create_pkgconfig_file.cmake new file mode 100644 index 0000000..fbbd6f5 --- /dev/null +++ b/cmake/create_pkgconfig_file.cmake @@ -0,0 +1,33 @@ +# +# Write a pkg-config pc file for given "name" with "decription" +# Arguments: +# name: a library name (withoud "lib" prefix and "so" suffixes +# desc: a desription string +# +macro (create_pkgconfig_file name desc) + set(_pkgfname "${CMAKE_CURRENT_BINARY_DIR}/${name}.pc") + #message(STATUS "${name}: writing pkgconfig file ${_pkgfname}") + + file(WRITE "${_pkgfname}" + "# file generated by LXQt cmake build\n" + "prefix=${CMAKE_INSTALL_PREFIX}\n" + "libdir=\${prefix}/lib${LIB_SUFFIX}\n" + "includedir=\${prefix}/include\n" + "\n" + "Name: ${name}\n" + "Description: ${desc}\n" + "Version: ${LXQT_VERSION}\n" + "Libs: -L\${libdir} -l${name}\n" + "Cflags: -I\${includedir}\n" + "\n" + ) + + # FreeBSD loves to install files to different locations + # http://www.freebsd.org/doc/handbook/dirstructure.html + if(${CMAKE_SYSTEM_NAME} STREQUAL "FreeBSD") + install(FILES ${_pkgfname} DESTINATION libdata/pkgconfig) + else() + install(FILES ${_pkgfname} DESTINATION lib${LIB_SUFFIX}/pkgconfig) + endif() + +endmacro() diff --git a/cmake/create_portable_headers.cmake b/cmake/create_portable_headers.cmake new file mode 100644 index 0000000..13a67a2 --- /dev/null +++ b/cmake/create_portable_headers.cmake @@ -0,0 +1,30 @@ +# Creates portable headers; e.g.: +# Creates XdgAction from xdgaction.h +# XdgAction contents: +# #include "xdgaction.h" +# +# Use: +# set(PUBLIC_CLASSES MyClass YourClass) +# create_portable_headers(PORTABLE_HEADERS ${PUBLIC_CLASSES}) +# PORTABLE_HEADER is an return value that contains the full name of the +# generated headers. + +function(create_portable_headers outfiles) + set(options) + set(oneValueArgs NAMESPACE) + set(multiValueArgs FILENAMES) + + cmake_parse_arguments(_CREATE_PORTABLE_HEADERS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(prefix ${_CREATE_PORTABLE_HEADERS_NAMESPACE}) + set(class_list ${_CREATE_PORTABLE_HEADERS_FILENAMES}) + foreach(f ${class_list}) + string(TOLOWER "${f}.h" _filename) + + file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/${f} + "#include \"../${_filename}\"") + + list(APPEND ${outfiles} ${CMAKE_CURRENT_BINARY_DIR}/${f}) + endforeach() + + set(${outfiles} ${${outfiles}} PARENT_SCOPE) +endfunction() diff --git a/cmake/lxqt_globalkeys-config.cmake.in b/cmake/lxqt_globalkeys-config.cmake.in new file mode 100644 index 0000000..959c442 --- /dev/null +++ b/cmake/lxqt_globalkeys-config.cmake.in @@ -0,0 +1,43 @@ +# - Find the lxqt-globalkeys include and library dirs and define a some macros +# +# The module defines the following variables +# LXQT_GLOBALKEYS_FOUND - Set to TRUE if all of the above has been found +# +# LXQT_GLOBALKEYS_INCLUDE_DIR - The lxqt-globalkeys include directory +# +# LXQT_GLOBALKEYS_INCLUDE_DIRS - The lxqt-globalkeys include directory +# +# LXQT_GLOBALKEYS_LIBRARY_DIRS - The lxqt-globalkeys include directory +# +# LXQT_GLOBALKEYS_LIBRARIES - The libraries needed to use lxqt-globalkeys +# +# LXQT_GLOBALKEYS_USE_FILE - The variable LXQT_GLOBALKEYS_USE_FILE is set which is the path +# to a CMake file that can be included to compile lxqt-globalkeys +# applications and libraries. It sets up the compilation +# environment for include directories and populates a +# LXQT_GLOBALKEYS_LIBRARIES variable. +# +# LXQT_GLOBALKEYS_QT_LIBRARIES - The Qt libraries needed by lxqt-globalkeys +# +# Typical usage: +# find_package(lxqt-globalkeys) +# include(${LXQT_GLOBALKEYS_USE_FILE}) +# add_executable(use-lxqt-globalkeys main.cpp) +# target_link_libraries(use-qtxdg ${LXQT_GLOBALKEYS_QT_LIBRARIES} ${LXQT_GLOBALKEYS_LIBRARIES}) + +set(LXQT_GLOBALKEYS_INCLUDE_DIR @LXQT_GLOBALKEYS_INCLUDE_DIR@) +set(LXQT_GLOBALKEYS_LIBRARY @LXQT_GLOBALKEYS_LIBRARY_NAME@) + +set(LXQT_GLOBALKEYS_LIBRARIES "${LXQT_GLOBALKEYS_LIBRARY}") +set(LXQT_GLOBALKEYS_INCLUDE_DIRS "${LXQT_GLOBALKEYS_INCLUDE_DIR}") +set(LXQT_GLOBALKEYS_LIBRARY_DIRS @CMAKE_INSTALL_PREFIX@/@LIB_INSTALL_DIR@) + +set(LXQT_GLOBALKEYS_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/lxqt_globalkeys_use.cmake") +set(LXQT_GLOBALKEYS_FOUND 1) + +set(LXQT_GLOBALKEYS_MAJOR_VERSION @LXQT_MAJOR_VERSION@) +set(LXQT_GLOBALKEYS_MINOR_VERSION @LXQT_MINOR_VERSION@) +set(LXQT_GLOBALKEYS_PATCH_VERSION @LXQT_PATCH_VERSION@) +set(LXQT_GLOBALKEYS_VERSION @LXQT_VERSION@) + +mark_as_advanced(LXQT_GLOBALKEYS_LIBRARY LXQT_GLOBALKEYS_INCLUDE_DIR) diff --git a/cmake/lxqt_globalkeys_ui-config.cmake.in b/cmake/lxqt_globalkeys_ui-config.cmake.in new file mode 100644 index 0000000..e88f572 --- /dev/null +++ b/cmake/lxqt_globalkeys_ui-config.cmake.in @@ -0,0 +1,43 @@ +# - Find the lxqt-globalkeys-ui include and library dirs and define a some macros +# +# The module defines the following variables +# LXQT_GLOBALKEYS_UI_FOUND - Set to TRUE if all of the above has been found +# +# LXQT_GLOBALKEYS_UI_INCLUDE_DIR - The lxqt-globalkeys-ui include directory +# +# LXQT_GLOBALKEYS_UI_INCLUDE_DIRS - The lxqt-globalkeys-ui include directory +# +# LXQT_GLOBALKEYS_LIBRARY_DIRS - The lxqt-globalkeys include directory +# +# LXQT_GLOBALKEYS_UI_LIBRARIES - The libraries needed to use lxqt-globalkeys-ui +# +# LXQT_GLOBALKEYS_UI_USE_FILE - The variable LXQT_GLOBALKEYS_UI_USE_FILE is set which is the path +# to a CMake file that can be included to compile lxqt-globalkeys-ui +# applications and libraries. It sets up the compilation +# environment for include directories and populates a +# LXQT_GLOBALKEYS_UI_LIBRARIES variable. +# +# LXQT_GLOBALKEYS_UI_QT_LIBRARIES - The Qt libraries needed by lxqt-globalkeys-ui +# +# Typical usage: +# find_package(lxqt-globalkeys-ui) +# include(${LXQT_GLOBALKEYS_UI_USE_FILE}) +# add_executable(use-lxqt-globalkeys-ui main.cpp) +# target_link_libraries(use-qtxdg ${LXQT_GLOBALKEYS_UI_QT_LIBRARIES} ${LXQT_GLOBALKEYS_UI_LIBRARIES}) + +set(LXQT_GLOBALKEYS_UI_INCLUDE_DIR @LXQT_GLOBALKEYS_UI_INCLUDE_DIR@) +set(LXQT_GLOBALKEYS_UI_LIBRARY @LXQT_GLOBALKEYS_UI_LIBRARY_NAME@) + +set(LXQT_GLOBALKEYS_UI_LIBRARIES "${LXQT_GLOBALKEYS_UI_LIBRARY}") +set(LXQT_GLOBALKEYS_UI_INCLUDE_DIRS "${LXQT_GLOBALKEYS_UI_INCLUDE_DIR}") +set(LXQT_GLOBALKEYS_UI_LIBRARY_DIRS @CMAKE_INSTALL_PREFIX@/@LIB_INSTALL_DIR@) + +set(LXQT_GLOBALKEYS_UI_USE_FILE "${CMAKE_CURRENT_LIST_DIR}/lxqt_globalkeys_ui_use.cmake") +set(LXQT_GLOBALKEYS_UI_FOUND 1) + +set(LXQT_GLOBALKEYS_UI_MAJOR_VERSION @LXQT_MAJOR_VERSION@) +set(LXQT_GLOBALKEYS_UI_MINOR_VERSION @LXQT_MINOR_VERSION@) +set(LXQT_GLOBALKEYS_UI_PATCH_VERSION @LXQT_PATCH_VERSION@) +set(LXQT_GLOBALKEYS_UI_VERSION @LXQT_VERSION@) + +mark_as_advanced(LXQT_GLOBALKEYS_UI_LIBRARY LXQT_GLOBALKEYS_UI_INCLUDE_DIR) diff --git a/cmake/lxqt_globalkeys_ui_use.cmake b/cmake/lxqt_globalkeys_ui_use.cmake new file mode 100644 index 0000000..f122cb3 --- /dev/null +++ b/cmake/lxqt_globalkeys_ui_use.cmake @@ -0,0 +1,2 @@ +include_directories(${LXQT_GLOBALKEYS_UI_INCLUDE_DIRS}) +link_directories(${LXQT_GLOBALKEYS_UI_LIBRARY_DIRS}) diff --git a/cmake/lxqt_globalkeys_use.cmake b/cmake/lxqt_globalkeys_use.cmake new file mode 100644 index 0000000..44f5e98 --- /dev/null +++ b/cmake/lxqt_globalkeys_use.cmake @@ -0,0 +1,2 @@ +include_directories(${LXQT_GLOBALKEYS_INCLUDE_DIRS}) +link_directories(${LXQT_GLOBALKEYS_LIBRARY_DIRS}) diff --git a/config/CMakeLists.txt b/config/CMakeLists.txt new file mode 100644 index 0000000..7259545 --- /dev/null +++ b/config/CMakeLists.txt @@ -0,0 +1,151 @@ +set(PROJECT_NAME lxqt-config-globalkeyshortcuts) +project(${PROJECT_NAME}) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +find_package(KF5WindowSystem REQUIRED QUIET) + +set(QTX_INCLUDE_DIRS "") +set(QTX_LIBRARIES Qt5::Widgets Qt5::DBus) + +set(QT_DBUS_PREFIX "org.qtproject") + +include_directories( + "${PROJECT_SOURCE_DIR}" + "${CMAKE_CURRENT_BINARY_DIR}" + ${QTX_INCLUDE_DIRS} + ${LXQT_INCLUDE_DIRS} +) + +set(${PROJECT_NAME}_PATH_TO_DAEMON "${LXQT_GLOBALKEYS_SOURCE_DIR}/daemon") + +set(${PROJECT_NAME}_SOURCES + main.cpp + main_window.cpp + actions.cpp + default_model.cpp + shortcut_selector.cpp + ${${PROJECT_NAME}_PATH_TO_DAEMON}/meta_types.cpp + edit_action_dialog.cpp + shortcut_delegate.cpp +) + +set(${PROJECT_NAME}_FORMS + main_window.ui + edit_action_dialog.ui +) + +set(${PROJECT_NAME}_RESOURCES +) + +set(${PROJECT_NAME}_TRANSLATIONS +) + +set(${PROJECT_NAME}_CPP_HEADERS + ${${PROJECT_NAME}_PATH_TO_DAEMON}/meta_types.h +) + +set(${PROJECT_NAME}_DBUS_ADAPTORS +) + +set(${PROJECT_NAME}_DBUS_INTERFACES + ${${PROJECT_NAME}_PATH_TO_DAEMON}/org.lxqt.global_key_shortcuts.daemon.xml +) +set_source_files_properties(${${PROJECT_NAME}_PATH_TO_DAEMON}/org.lxqt.global_key_shortcuts.daemon.xml PROPERTIES + BASENAME org.lxqt.global_key_shortcuts.daemon + INCLUDE ${${PROJECT_NAME}_PATH_TO_DAEMON}/meta_types.h +) + +set(${PROJECT_NAME}_HEADERS + ${${PROJECT_NAME}_CPP_HEADERS} +) + +set(${PROJECT_NAME}_TRANSLATABLE + ${${PROJECT_NAME}_SOURCES} + ${${PROJECT_NAME}_HEADERS} + ${${PROJECT_NAME}_FORMS} +) + +foreach(DBUS_ADAPTOR ${${PROJECT_NAME}_DBUS_ADAPTORS}) + get_filename_component(DBUS_ADAPTOR_FILENAME ${DBUS_ADAPTOR} NAME) + configure_file( + ${DBUS_ADAPTOR} + "${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME}" + @ONLY + ) + get_source_file_property(DBUS_ADAPTOR_INCLUDE ${DBUS_ADAPTOR} INCLUDE) + get_source_file_property(DBUS_ADAPTOR_PARENT_CLASSNAME ${DBUS_ADAPTOR} PARENT_CLASSNAME) + get_source_file_property(DBUS_ADAPTOR_BASENAME ${DBUS_ADAPTOR} BASENAME) + get_source_file_property(DBUS_ADAPTOR_CLASSNAME ${DBUS_ADAPTOR} CLASSNAME) + if(DBUS_ADAPTOR_BASENAME) + if(DBUS_ADAPTOR_CLASSNAME) + qt5_add_dbus_adaptor(${PROJECT_NAME}_DBUS_ADAPTOR_FILES "${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME}" ${DBUS_ADAPTOR_INCLUDE} ${DBUS_ADAPTOR_PARENT_CLASSNAME} ${DBUS_ADAPTOR_BASENAME} ${DBUS_ADAPTOR_CLASSNAME}) + else() + qt5_add_dbus_adaptor(${PROJECT_NAME}_DBUS_ADAPTOR_FILES "${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME}" ${DBUS_ADAPTOR_INCLUDE} ${DBUS_ADAPTOR_PARENT_CLASSNAME} ${DBUS_ADAPTOR_BASENAME}) + endif() + else() + qt5_add_dbus_adaptor(${PROJECT_NAME}_DBUS_ADAPTOR_FILES "${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME}" ${DBUS_ADAPTOR_INCLUDE} ${DBUS_ADAPTOR_PARENT_CLASSNAME}) + endif() +endforeach() + +foreach(DBUS_INTERFACE ${${PROJECT_NAME}_DBUS_INTERFACES}) + get_filename_component(DBUS_INTERFACE_FILENAME ${DBUS_INTERFACE} NAME) + configure_file( + ${DBUS_INTERFACE} + ${CMAKE_CURRENT_BINARY_DIR}/${DBUS_INTERFACE_FILENAME} + @ONLY + ) + get_source_file_property(DBUS_INTERFACE_BASENAME ${DBUS_INTERFACE} BASENAME) + get_source_file_property(DBUS_INTERFACE_INCLUDE ${DBUS_INTERFACE} INCLUDE) + get_source_file_property(DBUS_INTERFACE_CLASSNAME ${DBUS_INTERFACE} CLASSNAME) + get_source_file_property(DBUS_INTERFACE_NO_NAMESPACE ${DBUS_INTERFACE} NO_NAMESPACE) + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${DBUS_INTERFACE_FILENAME} PROPERTIES + INCLUDE ${DBUS_INTERFACE_INCLUDE} + CLASSNAME ${DBUS_INTERFACE_CLASSNAME} + NO_NAMESPACE ${DBUS_INTERFACE_NO_NAMESPACE} + ) + qt5_add_dbus_interface(${PROJECT_NAME}_DBUS_INTERFACE_FILES ${CMAKE_CURRENT_BINARY_DIR}/${DBUS_INTERFACE_FILENAME} ${DBUS_INTERFACE_BASENAME}) +endforeach() + +qt5_add_resources(${PROJECT_NAME}_RESOURCE_FILES ${${PROJECT_NAME}_RESOURCES}) +qt5_wrap_ui(${PROJECT_NAME}_FORM_FILES ${${PROJECT_NAME}_FORMS}) + +# Translations ********************************** +lxqt_translate_ts(${PROJECT_NAME}_QM_FILES + UPDATE_TRANSLATIONS ${UPDATE_TRANSLATIONS} + SOURCES + ${${PROJECT_NAME}_TRANSLATABLE} + INSTALL_DIR + ${LXQT_TRANSLATIONS_DIR}/${PROJECT_NAME} +) + +lxqt_app_translation_loader(${PROJECT_NAME}_QM_LOADER ${PROJECT_NAME}) +lxqt_translate_desktop(${PROJECT_NAME}_DESKTOP_FILES + SOURCES + ${PROJECT_NAME}.desktop.in +) +#************************************************ + +set(${PROJECT_NAME}_GENERATED_FILES + ${${PROJECT_NAME}_FORM_FILES} + ${${PROJECT_NAME}_RESOURCE_FILES} + ${${PROJECT_NAME}_QM_FILES} + ${${PROJECT_NAME}_DBUS_INTERFACE_FILES} + ${${PROJECT_NAME}_DBUS_ADAPTOR_FILES} + ${${PROJECT_NAME}_DESKTOP_FILES} + ${${PROJECT_NAME}_QM_LOADER} +) + +set(${PROJECT_NAME}_ALL_FILES + ${${PROJECT_NAME}_SOURCES} + ${${PROJECT_NAME}_HEADERS} + ${${PROJECT_NAME}_GENERATED_FILES} +) + +add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_ALL_FILES}) +target_link_libraries(${PROJECT_NAME} KF5::WindowSystem ${QTX_LIBRARIES} ${LXQT_LIBRARIES}) + +install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin) +install(FILES ${${PROJECT_NAME}_DESKTOP_FILES} DESTINATION ${CMAKE_INSTALL_PREFIX}/share/applications) diff --git a/config/actions.cpp b/config/actions.cpp new file mode 100644 index 0000000..907c048 --- /dev/null +++ b/config/actions.cpp @@ -0,0 +1,680 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "actions.h" + +#include "org.lxqt.global_key_shortcuts.daemon.h" + +Actions::Actions(QObject *parent) + : QObject(parent) + , mServiceWatcher(new QDBusServiceWatcher("org.lxqt.global_key_shortcuts", QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this)) + , mMultipleActionsBehaviour(MULTIPLE_ACTIONS_BEHAVIOUR_FIRST) +{ + connect(mServiceWatcher, SIGNAL(serviceUnregistered(QString)), this, SLOT(on_daemonDisappeared(QString))); + connect(mServiceWatcher, SIGNAL(serviceRegistered(QString)), this, SLOT(on_daemonAppeared(QString))); + mDaemonProxy = new org::lxqt::global_key_shortcuts::daemon("org.lxqt.global_key_shortcuts", "/daemon", QDBusConnection::sessionBus(), this); + + connect(mDaemonProxy, SIGNAL(actionAdded(qulonglong)), this, SLOT(on_actionAdded(qulonglong))); + connect(mDaemonProxy, SIGNAL(actionEnabled(qulonglong, bool)), this, SLOT(on_actionEnabled(qulonglong, bool))); + connect(mDaemonProxy, SIGNAL(clientActionSenderChanged(qulonglong, QString)), this, SLOT(on_clientActionSenderChanged(qulonglong, QString))); + connect(mDaemonProxy, SIGNAL(actionModified(qulonglong)), this, SLOT(on_actionModified(qulonglong))); + connect(mDaemonProxy, SIGNAL(actionRemoved(qulonglong)), this, SLOT(on_actionRemoved(qulonglong))); + connect(mDaemonProxy, SIGNAL(actionShortcutChanged(qulonglong)), this, SLOT(on_actionShortcutChanged(qulonglong))); + connect(mDaemonProxy, SIGNAL(actionsSwapped(qulonglong, qulonglong)), this, SLOT(on_actionsSwapped(qulonglong, qulonglong))); + connect(mDaemonProxy, SIGNAL(multipleActionsBehaviourChanged(uint)), this, SLOT(on_multipleActionsBehaviourChanged(uint))); + + QTimer::singleShot(0, this, SLOT(delayedInit())); +} + +Actions::~Actions() +{ +} + +void Actions::delayedInit() +{ + if (mDaemonProxy->isValid()) + { + on_daemonAppeared(QString()); + } +} + +void Actions::on_daemonDisappeared(const QString &) +{ + clear(); + emit daemonDisappeared(); +} + +void Actions::on_daemonAppeared(const QString &) +{ + init(); + emit daemonAppeared(); +} + +void Actions::init() +{ + clear(); + + mGeneralActionInfo = getAllActions(); + GeneralActionInfos::const_iterator M = mGeneralActionInfo.constEnd(); + for (GeneralActionInfos::const_iterator I = mGeneralActionInfo.constBegin(); I != M; ++I) + { + if (I.value().type == "client") + { + QString shortcut; + QString description; + bool enabled; + QDBusObjectPath path; + if (getClientActionInfoById(I.key(), shortcut, description, enabled, path)) + { + ClientActionInfo info; + info.shortcut = shortcut; + info.description = description; + info.enabled = enabled; + info.path = path; + mClientActionInfo[I.key()] = info; + + updateClientActionSender(I.key()); + } + } + else if (I.value().type == "method") + { + QString shortcut; + QString description; + bool enabled; + QString service; + QDBusObjectPath path; + QString interface; + QString method; + if (getMethodActionInfoById(I.key(), shortcut, description, enabled, service, path, interface, method)) + { + MethodActionInfo info; + info.shortcut = shortcut; + info.description = description; + info.enabled = enabled; + info.service = service; + info.path = path; + info.interface = interface; + info.method = method; + mMethodActionInfo[I.key()] = info; + } + } + else if (I.value().type == "command") + { + QString shortcut; + QString description; + bool enabled; + QString command; + QStringList arguments; + if (getCommandActionInfoById(I.key(), shortcut, description, enabled, command, arguments)) + { + CommandActionInfo info; + info.shortcut = shortcut; + info.description = description; + info.enabled = enabled; + info.command = command; + info.arguments = arguments; + mCommandActionInfo[I.key()] = info; + } + } + } + + mMultipleActionsBehaviour = static_cast(getMultipleActionsBehaviour()); +} + +void Actions::clear() +{ + mGeneralActionInfo.clear(); + mClientActionInfo.clear(); + mMethodActionInfo.clear(); + mCommandActionInfo.clear(); + mMultipleActionsBehaviour = MULTIPLE_ACTIONS_BEHAVIOUR_FIRST; +} + +QList Actions::allActionIds() const +{ + return mGeneralActionInfo.keys(); +} + +QPair Actions::actionById(qulonglong id) const +{ + GeneralActionInfos::const_iterator I = mGeneralActionInfo.constFind(id); + if (I == mGeneralActionInfo.constEnd()) + { + return qMakePair(false, GeneralActionInfo()); + } + return qMakePair(true, I.value()); +} + +QList Actions::allClientActionIds() const +{ + return mClientActionInfo.keys(); +} + +QPair Actions::clientActionInfoById(qulonglong id) const +{ + ClientActionInfos::const_iterator I = mClientActionInfo.constFind(id); + if (I == mClientActionInfo.constEnd()) + { + return qMakePair(false, ClientActionInfo()); + } + return qMakePair(true, I.value()); +} + +QList Actions::allMethodActionIds() const +{ + return mMethodActionInfo.keys(); +} + +QPair Actions::methodActionInfoById(qulonglong id) const +{ + MethodActionInfos::const_iterator I = mMethodActionInfo.constFind(id); + if (I == mMethodActionInfo.constEnd()) + { + return qMakePair(false, MethodActionInfo()); + } + return qMakePair(true, I.value()); +} + +QList Actions::allCommandActionIds() const +{ + return mCommandActionInfo.keys(); +} + +QPair Actions::commandActionInfoById(qulonglong id) const +{ + CommandActionInfos::const_iterator I = mCommandActionInfo.constFind(id); + if (I == mCommandActionInfo.constEnd()) + { + return qMakePair(false, CommandActionInfo()); + } + return qMakePair(true, I.value()); +} + +MultipleActionsBehaviour Actions::multipleActionsBehaviour() const +{ + return mMultipleActionsBehaviour; +} + +void Actions::do_actionAdded(qulonglong id) +{ + QString shortcut; + QString description; + bool enabled; + QString type; + QString info; + if (getActionById(id, shortcut, description, enabled, type, info)) + { + GeneralActionInfo generalActionInfo; + generalActionInfo.shortcut = shortcut; + generalActionInfo.description = description; + generalActionInfo.enabled = enabled; + generalActionInfo.type = type; + generalActionInfo.info = info; + mGeneralActionInfo[id] = generalActionInfo; + } + + if (type == "client") + { + QDBusObjectPath path; + if (getClientActionInfoById(id, shortcut, description, enabled, path)) + { + ClientActionInfo clientActionInfo; + clientActionInfo.shortcut = shortcut; + clientActionInfo.description = description; + clientActionInfo.enabled = enabled; + clientActionInfo.path = path; + mClientActionInfo[id] = clientActionInfo; + } + } + else if (type == "method") + { + QString service; + QDBusObjectPath path; + QString interface; + QString method; + if (getMethodActionInfoById(id, shortcut, description, enabled, service, path, interface, method)) + { + MethodActionInfo methodActionInfo; + methodActionInfo.shortcut = shortcut; + methodActionInfo.description = description; + methodActionInfo.enabled = enabled; + methodActionInfo.service = service; + methodActionInfo.path = path; + methodActionInfo.interface = interface; + methodActionInfo.method = method; + mMethodActionInfo[id] = methodActionInfo; + } + } + else if (type == "command") + { + QString command; + QStringList arguments; + if (getCommandActionInfoById(id, shortcut, description, enabled, command, arguments)) + { + CommandActionInfo commandActionInfo; + commandActionInfo.shortcut = shortcut; + commandActionInfo.description = description; + commandActionInfo.enabled = enabled; + commandActionInfo.command = command; + commandActionInfo.arguments = arguments; + mCommandActionInfo[id] = commandActionInfo; + } + } +} + +void Actions::on_actionAdded(qulonglong id) +{ + do_actionAdded(id); + emit actionAdded(id); +} + +void Actions::on_actionEnabled(qulonglong id, bool enabled) +{ + GeneralActionInfos::iterator GI = mGeneralActionInfo.find(id); + if (GI != mGeneralActionInfo.end()) + { + GI.value().enabled = enabled; + + if (GI.value().type == "client") + { + ClientActionInfos::iterator DI = mClientActionInfo.find(id); + if (DI != mClientActionInfo.end()) + { + DI.value().enabled = enabled; + } + } + else if (GI.value().type == "method") + { + MethodActionInfos::iterator MI = mMethodActionInfo.find(id); + if (MI != mMethodActionInfo.end()) + { + MI.value().enabled = enabled; + } + } + else if (GI.value().type == "command") + { + CommandActionInfos::iterator CI = mCommandActionInfo.find(id); + if (CI != mCommandActionInfo.end()) + { + CI.value().enabled = enabled; + } + } + } + emit actionEnabled(id, enabled); +} + +void Actions::on_clientActionSenderChanged(qulonglong id, const QString &sender) +{ + mClientActionSenders[id] = sender; + emit actionModified(id); +} + +void Actions::on_actionModified(qulonglong id) +{ + do_actionAdded(id); + emit actionModified(id); +} + +void Actions::on_actionShortcutChanged(qulonglong id) +{ + do_actionAdded(id); + emit actionModified(id); +} + +void Actions::on_actionsSwapped(qulonglong id1, qulonglong id2) +{ + GeneralActionInfos::iterator GI1 = mGeneralActionInfo.find(id1); + GeneralActionInfos::iterator GI2 = mGeneralActionInfo.find(id2); + if ((GI1 != mGeneralActionInfo.end()) && (GI2 != mGeneralActionInfo.end())) + { + bool swapped = false; + + if (GI1.value().type == GI2.value().type) + { + if (GI1.value().type == "client") + { + ClientActionInfos::iterator DI1 = mClientActionInfo.find(id1); + ClientActionInfos::iterator DI2 = mClientActionInfo.find(id2); + if ((DI1 != mClientActionInfo.end()) && (DI2 != mClientActionInfo.end())) + { + ClientActionInfo clientActionInfo = DI1.value(); + DI1.value() = DI2.value(); + DI2.value() = clientActionInfo; + swapped = true; + } + } + else if (GI1.value().type == "method") + { + MethodActionInfos::iterator MI1 = mMethodActionInfo.find(id1); + MethodActionInfos::iterator MI2 = mMethodActionInfo.find(id2); + if ((MI1 != mMethodActionInfo.end()) && (MI2 != mMethodActionInfo.end())) + { + MethodActionInfo methodActionInfo = MI1.value(); + MI1.value() = MI2.value(); + MI2.value() = methodActionInfo; + swapped = true; + } + } + else if (GI1.value().type == "command") + { + CommandActionInfos::iterator CI1 = mCommandActionInfo.find(id1); + CommandActionInfos::iterator CI2 = mCommandActionInfo.find(id2); + if ((CI1 != mCommandActionInfo.end()) && (CI2 != mCommandActionInfo.end())) + { + CommandActionInfo commandActionInfo = CI1.value(); + CI1.value() = CI2.value(); + CI2.value() = commandActionInfo; + swapped = true; + } + } + } + + if (swapped) + { + GeneralActionInfo generalActionInfo = GI1.value(); + GI1.value() = GI2.value(); + GI2.value() = generalActionInfo; + } + else + { + do_actionRemoved(id1); + do_actionRemoved(id2); + do_actionAdded(id1); + do_actionAdded(id2); + } + } + emit actionsSwapped(id1, id2); +} + +void Actions::do_actionRemoved(qulonglong id) +{ + mGeneralActionInfo.remove(id); + mClientActionInfo.remove(id); + mMethodActionInfo.remove(id); + mCommandActionInfo.remove(id); +} + +void Actions::on_actionRemoved(qulonglong id) +{ + do_actionRemoved(id); + emit actionRemoved(id); +} + +void Actions::on_multipleActionsBehaviourChanged(uint behaviour) +{ + mMultipleActionsBehaviour = static_cast(behaviour); + emit multipleActionsBehaviourChanged(mMultipleActionsBehaviour); +} + +bool Actions::getClientActionInfoById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QDBusObjectPath &path) +{ + return mDaemonProxy->getClientActionInfoById(id, shortcut, description, enabled, path); +} + +bool Actions::getMethodActionInfoById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QString &service, QDBusObjectPath &path, QString &interface, QString &method) +{ + return mDaemonProxy->getMethodActionInfoById(id, shortcut, description, enabled, service, path, interface, method); +} + +bool Actions::getCommandActionInfoById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QString &command, QStringList &arguments) +{ + return mDaemonProxy->getCommandActionInfoById(id, shortcut, description, enabled, command, arguments); +} + +QList Actions::getAllActionIds() +{ + QDBusPendingReply > reply = mDaemonProxy->getAllActionIds(); + reply.waitForFinished(); + if (reply.isError()) + { + return QList(); + } + + return reply.argumentAt<0>(); +} + +bool Actions::getActionById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QString &type, QString &info) +{ + return mDaemonProxy->getActionById(id, shortcut, description, enabled, type, info); +} + +QMap Actions::getAllActions() +{ + QDBusPendingReply > reply = mDaemonProxy->getAllActions(); + reply.waitForFinished(); + if (reply.isError()) + { + return QMap(); + } + + return reply.argumentAt<0>(); +} + +uint Actions::getMultipleActionsBehaviour() +{ + QDBusPendingReply reply = mDaemonProxy->getMultipleActionsBehaviour(); + reply.waitForFinished(); + if (reply.isError()) + { + return 0; + } + + return reply.argumentAt<0>(); +} + +QPair Actions::addMethodAction(const QString &shortcut, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description) +{ + QDBusPendingReply reply = mDaemonProxy->addMethodAction(shortcut, service, path, interface, method, description); + reply.waitForFinished(); + if (reply.isError()) + { + return qMakePair(QString(), 0ull); + } + + return qMakePair(reply.argumentAt<0>(), reply.argumentAt<1>()); +} + +QPair Actions::addCommandAction(const QString &shortcut, const QString &command, const QStringList &arguments, const QString &description) +{ + QDBusPendingReply reply = mDaemonProxy->addCommandAction(shortcut, command, arguments, description); + reply.waitForFinished(); + if (reply.isError()) + { + return qMakePair(QString(), 0ull); + } + + return qMakePair(reply.argumentAt<0>(), reply.argumentAt<1>()); +} + +bool Actions::modifyActionDescription(const qulonglong &id, const QString &description) +{ + QDBusPendingReply reply = mDaemonProxy->modifyActionDescription(id, description); + reply.waitForFinished(); + if (reply.isError()) + { + return false; + } + + return reply.argumentAt<0>(); +} + +bool Actions::modifyMethodAction(const qulonglong &id, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description) +{ + QDBusPendingReply reply = mDaemonProxy->modifyMethodAction(id, service, path, interface, method, description); + reply.waitForFinished(); + if (reply.isError()) + { + return false; + } + + return reply.argumentAt<0>(); +} + +bool Actions::modifyCommandAction(const qulonglong &id, const QString &command, const QStringList &arguments, const QString &description) +{ + QDBusPendingReply reply = mDaemonProxy->modifyCommandAction(id, command, arguments, description); + reply.waitForFinished(); + if (reply.isError()) + { + return false; + } + + return reply.argumentAt<0>(); +} + +bool Actions::enableAction(qulonglong id, bool enabled) +{ + QDBusPendingReply reply = mDaemonProxy->enableAction(id, enabled); + reply.waitForFinished(); + if (reply.isError()) + { + return false; + } + + return reply.argumentAt<0>(); +} + +bool Actions::isActionEnabled(qulonglong id) +{ + QDBusPendingReply reply = mDaemonProxy->isActionEnabled(id); + reply.waitForFinished(); + if (reply.isError()) + { + return false; + } + + return reply.argumentAt<0>(); +} + +QString Actions::getClientActionSender(qulonglong id) +{ + return mClientActionSenders[id]; +} + +QString Actions::updateClientActionSender(qulonglong id) +{ + QDBusPendingReply reply = mDaemonProxy->getClientActionSender(id); + reply.waitForFinished(); + if (reply.isError()) + { + return QString(); + } + + QString sender = reply.argumentAt<0>(); + mClientActionSenders[id] = sender; + return sender; +} + +QString Actions::changeShortcut(const qulonglong &id, const QString &shortcut) +{ + QDBusPendingReply reply = mDaemonProxy->changeShortcut(id, shortcut); + reply.waitForFinished(); + if (reply.isError()) + { + return QString(); + } + + return reply.argumentAt<0>(); +} + +bool Actions::swapActions(const qulonglong &id1, const qulonglong &id2) +{ + QDBusPendingReply reply = mDaemonProxy->swapActions(id1, id2); + reply.waitForFinished(); + if (reply.isError()) + { + return false; + } + + return reply.argumentAt<0>(); +} + +bool Actions::removeAction(const qulonglong &id) +{ + QDBusPendingReply reply = mDaemonProxy->removeAction(id); + reply.waitForFinished(); + if (reply.isError()) + { + return false; + } + + return reply.argumentAt<0>(); +} + +void Actions::setMultipleActionsBehaviour(const MultipleActionsBehaviour &behaviour) +{ + QDBusPendingReply reply = mDaemonProxy->setMultipleActionsBehaviour(behaviour); + reply.waitForFinished(); +} + +void Actions::grabShortcut(uint timeout) +{ + QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(mDaemonProxy->grabShortcut(timeout), this); + + connect(watcher, SIGNAL(finished(QDBusPendingCallWatcher *)), this, SLOT(grabShortcutFinished(QDBusPendingCallWatcher *))); +} + +void Actions::cancelShortcutGrab() +{ + mDaemonProxy->cancelShortcutGrab(); +} + +void Actions::grabShortcutFinished(QDBusPendingCallWatcher *call) +{ + QDBusPendingReply reply = *call; + if (reply.isError()) + { + emit grabShortcutFailed(); + } + else + { + if (reply.argumentAt<1>()) + { + emit grabShortcutFailed(); + } + else + { + if (reply.argumentAt<2>()) + { + emit grabShortcutCancelled(); + } + else + { + if (reply.argumentAt<3>()) + { + emit grabShortcutTimedout(); + } + else + { + emit shortcutGrabbed(reply.argumentAt<0>()); + } + } + } + } + + call->deleteLater(); +} diff --git a/config/actions.h b/config/actions.h new file mode 100644 index 0000000..32b0cbe --- /dev/null +++ b/config/actions.h @@ -0,0 +1,182 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_CONFIG__ACTIONS__INCLUDED +#define GLOBAL_ACTION_CONFIG__ACTIONS__INCLUDED + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../daemon/meta_types.h" + + +class OrgLxqtGlobal_key_shortcutsDaemonInterface; +namespace org +{ +namespace lxqt +{ +namespace global_key_shortcuts +{ +typedef ::OrgLxqtGlobal_key_shortcutsDaemonInterface daemon; +} +} +} + +class QDBusServiceWatcher; +class QDBusPendingCallWatcher; + +class Actions : public QObject +{ + Q_OBJECT +public: + Actions(QObject *parent = 0); + ~Actions(); + + + QList allActionIds() const; + QPair actionById(qulonglong id) const; + + QList allClientActionIds() const; + QPair clientActionInfoById(qulonglong id) const; + + QList allMethodActionIds() const; + QPair methodActionInfoById(qulonglong id) const; + + QList allCommandActionIds() const; + QPair commandActionInfoById(qulonglong id) const; + + + QPair addMethodAction(const QString &shortcut, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description); + QPair addCommandAction(const QString &shortcut, const QString &command, const QStringList &arguments, const QString &description); + + bool modifyActionDescription(const qulonglong &id, const QString &description); + bool modifyMethodAction(const qulonglong &id, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description); + bool modifyCommandAction(const qulonglong &id, const QString &command, const QStringList &arguments, const QString &description); + + bool enableAction(qulonglong id, bool enabled); + bool isActionEnabled(qulonglong id); + + QString getClientActionSender(qulonglong id); + QString updateClientActionSender(qulonglong id); + + QString changeShortcut(const qulonglong &id, const QString &shortcut); + + bool swapActions(const qulonglong &id1, const qulonglong &id2); + + bool removeAction(const qulonglong &id); + + + MultipleActionsBehaviour multipleActionsBehaviour() const; + + void setMultipleActionsBehaviour(const MultipleActionsBehaviour &behaviour); + + + void grabShortcut(uint timeout); + void cancelShortcutGrab(); + +signals: + void daemonDisappeared(); + void daemonAppeared(); + + void actionAdded(qulonglong id); + void actionEnabled(qulonglong id, bool enabled); + void actionModified(qulonglong id); + void actionsSwapped(qulonglong id1, qulonglong id2); + void actionRemoved(qulonglong id); + + void multipleActionsBehaviourChanged(MultipleActionsBehaviour behaviour); + + void shortcutGrabbed(const QString &); + void grabShortcutFailed(); + void grabShortcutCancelled(); + void grabShortcutTimedout(); + +private: + void init(); + void clear(); + + QList getAllActionIds(); + + bool getActionById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QString &type, QString &info); + QMap getAllActions(); + + bool getClientActionInfoById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QDBusObjectPath &path); + bool getMethodActionInfoById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QString &service, QDBusObjectPath &path, QString &interface, QString &method); + bool getCommandActionInfoById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QString &command, QStringList &arguments); + + uint getMultipleActionsBehaviour(); + +private slots: + void delayedInit(); + + void on_daemonDisappeared(const QString &); + void on_daemonAppeared(const QString &); + + void on_actionAdded(qulonglong id); + void on_actionEnabled(qulonglong id, bool enabled); + void on_clientActionSenderChanged(qulonglong id, const QString &sender); + void on_actionModified(qulonglong id); + void on_actionShortcutChanged(qulonglong id); + void on_actionsSwapped(qulonglong id1, qulonglong id2); + void on_actionRemoved(qulonglong id); + void on_multipleActionsBehaviourChanged(uint behaviour); + + void grabShortcutFinished(QDBusPendingCallWatcher *call); + +private: + void do_actionAdded(qulonglong id); + void do_actionRemoved(qulonglong id); + +private: + org::lxqt::global_key_shortcuts::daemon *mDaemonProxy; + QDBusServiceWatcher *mServiceWatcher; + + typedef QMap GeneralActionInfos; + GeneralActionInfos mGeneralActionInfo; + + typedef QMap ClientActionInfos; + ClientActionInfos mClientActionInfo; + + typedef QMap ClientActionSenders; + ClientActionSenders mClientActionSenders; + + typedef QMap MethodActionInfos; + MethodActionInfos mMethodActionInfo; + + typedef QMap CommandActionInfos; + CommandActionInfos mCommandActionInfo; + + MultipleActionsBehaviour mMultipleActionsBehaviour; +}; + +#endif // GLOBAL_ACTION_CONFIG__ACTIONS__INCLUDED diff --git a/config/default_model.cpp b/config/default_model.cpp new file mode 100644 index 0000000..430adae --- /dev/null +++ b/config/default_model.cpp @@ -0,0 +1,354 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + + +#include "default_model.h" +#include "actions.h" + + +DefaultModel::DefaultModel(Actions *actions, const QColor &grayedOutColour, const QFont &highlightedFont, const QFont &italicFont, const QFont &highlightedItalicFont, QObject *parent) + : QAbstractTableModel(parent) + , mActions(actions) + , mGrayedOutColour(grayedOutColour) + , mHighlightedFont(highlightedFont) + , mItalicFont(italicFont) + , mHighlightedItalicFont(highlightedItalicFont) +{ + connect(actions, SIGNAL(daemonDisappeared()), SLOT(daemonDisappeared())); + connect(actions, SIGNAL(daemonAppeared()), SLOT(daemonAppeared())); + connect(actions, SIGNAL(actionAdded(qulonglong)), SLOT(actionAdded(qulonglong))); + connect(actions, SIGNAL(actionModified(qulonglong)), SLOT(actionModified(qulonglong))); + connect(actions, SIGNAL(actionEnabled(qulonglong, bool)), SLOT(actionEnabled(qulonglong, bool))); + connect(actions, SIGNAL(actionsSwapped(qulonglong, qulonglong)), SLOT(actionsSwapped(qulonglong, qulonglong))); + connect(actions, SIGNAL(actionRemoved(qulonglong)), SLOT(actionRemoved(qulonglong))); + + mVerboseType["command"] = tr("Command"); + mVerboseType["method"] = tr("DBus call"); + mVerboseType["client"] = tr("Client"); +} + +DefaultModel::~DefaultModel() +{ +} + +int DefaultModel::rowCount(const QModelIndex &/*parent*/) const +{ + return mContent.size(); +} + +int DefaultModel::columnCount(const QModelIndex &/*parent*/) const +{ + return 5; +} + +QVariant DefaultModel::data(const QModelIndex &index, int role) const +{ + switch (role) + { + case Qt::DisplayRole: + if ((index.row() >= 0) && (index.row() < rowCount()) && (index.column() >= 0) && (index.column() < columnCount())) + switch (index.column()) + { + case 0: + return mContent.keys()[index.row()]; + + case 1: + return mContent[mContent.keys()[index.row()]].shortcut; + + case 2: + return mContent[mContent.keys()[index.row()]].description; + + case 3: + return mVerboseType[mContent[mContent.keys()[index.row()]].type]; + + case 4: + return mContent[mContent.keys()[index.row()]].info; + } + break; + + case Qt::EditRole: + if ((index.row() >= 0) && (index.row() < rowCount()) && (index.column() >= 0) && (index.column() < columnCount())) + switch (index.column()) + { + case 1: + return mContent[mContent.keys()[index.row()]].shortcut; + } + break; + + case Qt::FontRole: + { + if ((index.row() >= 0) && (index.row() < rowCount())) + { + qulonglong id = mContent.keys()[index.row()]; + bool multiple = (index.column() == 1) && (mShortcuts[mContent[id].shortcut].size() > 1); + bool inactive = (mContent[id].type == "client") && (mActions->getClientActionSender(id).isEmpty()); + if (multiple || inactive) + return multiple ? (inactive ? mHighlightedItalicFont : mHighlightedFont) : mItalicFont; + } + } + break; + + case Qt::ForegroundRole: + if (!mContent[mContent.keys()[index.row()]].enabled) + { + return mGrayedOutColour; + } + break; + + case Qt::CheckStateRole: + if ((index.row() >= 0) && (index.row() < rowCount()) && (index.column() == 0)) + { + return mContent[mContent.keys()[index.row()]].enabled ? Qt::Checked : Qt::Unchecked; + } + break; + + default: + ; + } + + return QVariant(); +} + +QVariant DefaultModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + switch (role) + { + case Qt::DisplayRole: + switch (orientation) + { + case Qt::Horizontal: + switch (section) + { + case 0: + return tr("Id"); + + case 1: + return tr("Shortcut"); + + case 2: + return tr("Description"); + + case 3: + return tr("Type"); + + case 4: + return tr("Info"); + } + break; + + default: + ; + } + break; + + default: + ; + } + + return QAbstractTableModel::headerData(section, orientation, role); +} + +Qt::ItemFlags DefaultModel::flags(const QModelIndex &index) const +{ + Qt::ItemFlags result = Qt::ItemIsSelectable | Qt::ItemIsEnabled; + if (index.column() == 0) + { + result |= Qt::ItemIsUserCheckable; + } + if (index.column() == 1) + { + result |= Qt::ItemIsEditable; + } + return result; +} + +bool DefaultModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + switch (role) + { + case Qt::EditRole: + if ((index.row() >= 0) && (index.row() < rowCount()) && index.column() == 1) + { + mActions->changeShortcut(mContent.keys()[index.row()], value.toString()); + return true; + } + break; + } + return false; +} + +qulonglong DefaultModel::id(const QModelIndex &index) const +{ + if ((index.row() >= 0) && (index.row() < rowCount())) + { + return mContent.keys()[index.row()]; + } + return 0ull; +} + +void DefaultModel::daemonDisappeared() +{ + beginResetModel(); + + mContent.clear(); + mShortcuts.clear(); + + endResetModel(); +} + +void DefaultModel::daemonAppeared() +{ + QList allIds = mActions->allActionIds(); + + beginInsertRows(QModelIndex(), 0, allIds.size() - 1); + + foreach(qulonglong id, allIds) + { + mContent[id] = mActions->actionById(id).second; + mShortcuts[mContent[id].shortcut].insert(id); + } + + endInsertRows(); +} + +void DefaultModel::actionAdded(qulonglong id) +{ + if (!mContent.contains(id)) + { + QPair result = mActions->actionById(id); + if (result.first) + { + QList keys = mContent.keys(); + int row = qBinaryFind(keys, mContent.lowerBound(id).key()) - keys.constBegin(); + + beginInsertRows(QModelIndex(), row, row); + + mContent[id] = result.second; + mShortcuts[mContent[id].shortcut].insert(id); + + endInsertRows(); + + keys = mContent.keys(); + foreach(qulonglong siblingId, mShortcuts[mContent[id].shortcut]) + { + if (id != siblingId) + { + int siblingRow = qBinaryFind(keys, siblingId) - keys.constBegin(); + emit dataChanged(index(siblingRow, 1), index(siblingRow, 1)); + } + } + } + } +} + +void DefaultModel::actionEnabled(qulonglong id, bool enabled) +{ + if (mContent.contains(id)) + { + QList keys = mContent.keys(); + int row = qBinaryFind(keys, id) - keys.constBegin(); + + mContent[id].enabled = enabled; + + emit dataChanged(index(row, 0), index(row, 3)); + } +} + +void DefaultModel::actionModified(qulonglong id) +{ + if (mContent.contains(id)) + { + QPair result = mActions->actionById(id); + if (result.first) + { + QList keys = mContent.keys(); + int row = qBinaryFind(keys, id) - keys.constBegin(); + + if (mContent[id].shortcut != result.second.shortcut) + { + mShortcuts[result.second.shortcut].insert(id); + mShortcuts[mContent[id].shortcut].remove(id); + foreach(qulonglong siblingId, mShortcuts[mContent[id].shortcut]) + { + int siblingRow = qBinaryFind(keys, siblingId) - keys.constBegin(); + emit dataChanged(index(siblingRow, 1), index(siblingRow, 1)); + } + foreach(qulonglong siblingId, mShortcuts[result.second.shortcut]) + { + int siblingRow = qBinaryFind(keys, siblingId) - keys.constBegin(); + emit dataChanged(index(siblingRow, 1), index(siblingRow, 1)); + } + } + + mContent[id] = result.second; + + emit dataChanged(index(row, 0), index(row, 3)); + } + } +} + +void DefaultModel::actionsSwapped(qulonglong id1, qulonglong id2) +{ + if (mContent.contains(id1) && mContent.contains(id2)) + { + QList keys = mContent.keys(); + int row1 = qBinaryFind(keys, id1) - keys.constBegin(); + int row2 = qBinaryFind(keys, id2) - keys.constBegin(); + + // swap + GeneralActionInfo tmp = mContent[id1]; + mContent[id1] = mContent[id2]; + mContent[id2] = tmp; + + emit dataChanged(index(row1, 0), index(row1, 3)); + emit dataChanged(index(row2, 0), index(row2, 3)); + } +} + +void DefaultModel::actionRemoved(qulonglong id) +{ + if (mContent.contains(id)) + { + QList keys = mContent.keys(); + int row = qBinaryFind(keys, id) - keys.constBegin(); + + beginRemoveRows(QModelIndex(), row, row); + + mShortcuts[mContent[id].shortcut].remove(id); + QString shortcut = mContent[id].shortcut; + + mContent.remove(id); + + endRemoveRows(); + + foreach(qulonglong siblingId, mShortcuts[shortcut]) + { + int siblingRow = qBinaryFind(keys, siblingId) - keys.constBegin(); + emit dataChanged(index(siblingRow, 1), index(siblingRow, 1)); + } + } +} diff --git a/config/default_model.h b/config/default_model.h new file mode 100644 index 0000000..2e31efd --- /dev/null +++ b/config/default_model.h @@ -0,0 +1,93 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_CONFIG__DEFAULT_MODEL__INCLUDED +#define GLOBAL_ACTION_CONFIG__DEFAULT_MODEL__INCLUDED + + +#include +#include +#include +#include + +#include "../daemon/meta_types.h" + + +class Actions; + + +template +class QOrderedSet : public QMap +{ +public: + typename QMap::iterator insert(const Key &akey) + { + return QMap::insert(akey, akey); + } +}; + +class DefaultModel : public QAbstractTableModel +{ + Q_OBJECT +public: + explicit DefaultModel(Actions *actions, const QColor &grayedOutColour, const QFont &highlightedFont, const QFont &italicFont, const QFont &highlightedItalicFont, QObject *parent = 0); + ~DefaultModel(); + + int rowCount(const QModelIndex &parent = QModelIndex()) const; + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + Qt::ItemFlags flags(const QModelIndex &index) const; + + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole); + + qulonglong id(const QModelIndex &index) const; + +public slots: + void daemonDisappeared(); + void daemonAppeared(); + + void actionAdded(qulonglong id); + void actionEnabled(qulonglong id, bool enabled); + void actionModified(qulonglong id); + void actionsSwapped(qulonglong id1, qulonglong id2); + void actionRemoved(qulonglong id); + +private: + Actions *mActions; + QMap mContent; + QMap > mShortcuts; + + QColor mGrayedOutColour; + QFont mHighlightedFont; + QFont mItalicFont; + QFont mHighlightedItalicFont; + + QMap mVerboseType; +}; + +#endif // GLOBAL_ACTION_CONFIG__DEFAULT_MODEL__INCLUDED diff --git a/config/edit_action_dialog.cpp b/config/edit_action_dialog.cpp new file mode 100644 index 0000000..168b4b4 --- /dev/null +++ b/config/edit_action_dialog.cpp @@ -0,0 +1,252 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "edit_action_dialog.h" +#include "actions.h" + +EditActionDialog::EditActionDialog(Actions *actions, QWidget *parent) + : QDialog(parent) + , mActions(actions) +{ + setupUi(this); + shortcut_SS->setActions(mActions); + + connect(this, SIGNAL(accepted()), SLOT(when_accepted())); +} + +void EditActionDialog::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) + { + case QEvent::LanguageChange: + retranslateUi(this); + break; + default: + break; + } +} + +static QString joinCommandLine(const QString &command, QStringList arguments) +{ + arguments.prepend(command); + int m = arguments.length(); + for (int i = 0; i < m; ++i) + { + QString &item = arguments[i]; + if (item.contains(QRegExp("[ \r\n\t\"']"))) + { + item.prepend("'").append("'"); + } + else if (item.isEmpty()) + { + item = QString("''"); + } + } + return arguments.join(" "); +} + +static QStringList splitCommandLine(QString commandLine) +{ + commandLine.prepend(" ").append(" "); + QStringList result; + QRegExp spacePattern("\\s+"); + QRegExp itemPattern("([^ \r\n\t\"']+)|((\"([^\"]|\\\")*\")|('([^']|\\')*'))(?=\\s)"); + + for (int pos = 0; ;) + { + if (commandLine.indexOf(spacePattern, pos) != pos) + { + return QStringList(); + } + pos += spacePattern.matchedLength(); + + if (pos == commandLine.length()) + { + break; + } + + if (commandLine.indexOf(itemPattern, pos) != pos) + { + return QStringList(); + } + pos += itemPattern.matchedLength(); + + QString item = itemPattern.cap(2); + if (item.length()) + { + result << item.mid(1, item.length() - 2); + } + else + { + result << itemPattern.cap(1); + } + } + return result; +} + +void EditActionDialog::when_accepted() +{ + if (mId) + { + if (command_RB->isChecked()) + { + QStringList commandLine = splitCommandLine(command_PTE->toPlainText()); + if (!commandLine.length()) + { + return; + } + mActions->modifyCommandAction(mId, commandLine[0], commandLine.mid(1), description_LE->text()); + } + else if (dbus_method_RB->isChecked()) + { + mActions->modifyMethodAction(mId, service_LE->text(), QDBusObjectPath(path_LE->text()), interface_LE->text(), method_LE->text(), description_LE->text()); + } + mActions->changeShortcut(mId, mShortcut); + mActions->enableAction(mId, enabled_CB->isChecked()); + } + else + { + QPair result = qMakePair(QString(), 0ull); + if (command_RB->isChecked()) + { + QStringList commandLine = splitCommandLine(command_PTE->toPlainText()); + if (!commandLine.length()) + { + return; + } + result = mActions->addCommandAction(mShortcut, commandLine[0], commandLine.mid(1), description_LE->text()); + } + else if (dbus_method_RB->isChecked()) + { + result = mActions->addMethodAction(mShortcut, service_LE->text(), QDBusObjectPath(path_LE->text()), interface_LE->text(), method_LE->text(), description_LE->text()); + } + if (result.second && !enabled_CB->isChecked()) + { + mActions->enableAction(result.second, false); + } + } +} + +bool EditActionDialog::load(qulonglong id) +{ + mId = id; + + if (mId) + { + QPair info = mActions->actionById(id); + if (!info.first) + { + return false; + } + + bool canEdit = ((info.second.type == "command") || (info.second.type == "method")); + + mShortcut = info.second.shortcut; + shortcut_SS->setText(mShortcut); + description_LE->setText(info.second.description); + enabled_CB->setChecked(info.second.enabled); + command_RB->setChecked(info.second.type == "command"); + dbus_method_RB->setChecked(info.second.type == "method"); + action_SW->setCurrentWidget((info.second.type == "method") ? dbus_method_P : command_P); + if (info.second.type == "command") + { + QPair commandInfo = mActions->commandActionInfoById(id); + if (!commandInfo.first) + { + return false; + } + command_PTE->setPlainText(joinCommandLine(commandInfo.second.command, commandInfo.second.arguments)); + } + else if (info.second.type == "method") + { + QPair methodInfo = mActions->methodActionInfoById(id); + if (!methodInfo.first) + { + return false; + } + service_LE->setText(methodInfo.second.service); + path_LE->setText(methodInfo.second.path.path()); + interface_LE->setText(methodInfo.second.interface); + method_LE->setText(methodInfo.second.method); + } + else + { + command_PTE->clear(); + } + + description_LE->setEnabled(canEdit); + command_RB->setEnabled(false); + dbus_method_RB->setEnabled(false); + action_SW->setEnabled(canEdit); + } + else + { + shortcut_SS->setText(QString()); + description_LE->clear(); + enabled_CB->setChecked(true); + command_RB->setChecked(false); + dbus_method_RB->setChecked(false); + action_SW->setCurrentWidget(command_P); + command_PTE->clear(); + service_LE->clear(); + path_LE->clear(); + interface_LE->clear(); + method_LE->clear(); + + description_LE->setEnabled(true); + command_RB->setEnabled(true); + dbus_method_RB->setEnabled(true); + action_SW->setEnabled(false); + } + + return true; +} + +void EditActionDialog::on_shortcut_SS_shortcutGrabbed(const QString &shortcut) +{ + mShortcut = shortcut; + shortcut_SS->setText(shortcut); +} + +void EditActionDialog::on_command_RB_clicked(bool checked) +{ + if (checked) + { + action_SW->setCurrentWidget(command_P); + action_SW->setEnabled(true); + } +} + +void EditActionDialog::on_dbus_method_RB_clicked(bool checked) +{ + if (checked) + { + action_SW->setCurrentWidget(dbus_method_P); + action_SW->setEnabled(true); + } +} diff --git a/config/edit_action_dialog.h b/config/edit_action_dialog.h new file mode 100644 index 0000000..29c45d6 --- /dev/null +++ b/config/edit_action_dialog.h @@ -0,0 +1,62 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_CONFIG__EDIT_ACTION_DIALOG__INCLUDED +#define GLOBAL_ACTION_CONFIG__EDIT_ACTION_DIALOG__INCLUDED + + +#include "ui_edit_action_dialog.h" + + +class Actions; + +class EditActionDialog : public QDialog, private Ui::EditActionDialog +{ + Q_OBJECT + +public: + explicit EditActionDialog(Actions *actions, QWidget *parent = 0); + + bool load(qulonglong id); + +protected slots: + void when_accepted(); + + void on_shortcut_SS_shortcutGrabbed(const QString &); + void on_command_RB_clicked(bool); + void on_dbus_method_RB_clicked(bool); + +protected: + void changeEvent(QEvent *e); + +private: + Actions *mActions; + qulonglong mId; + QString mShortcut; +}; + +#endif // GLOBAL_ACTION_CONFIG__EDIT_ACTION_DIALOG__INCLUDED diff --git a/config/edit_action_dialog.ui b/config/edit_action_dialog.ui new file mode 100644 index 0000000..77c1225 --- /dev/null +++ b/config/edit_action_dialog.ui @@ -0,0 +1,328 @@ + + + EditActionDialog + + + + 0 + 0 + 400 + 286 + + + + Edit Action + + + + + + QFormLayout::AllNonFixedFieldsGrow + + + 6 + + + 6 + + + + + &Shortcut: + + + shortcut_SS + + + + + + + 0 + + + + + + 81 + 0 + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + &Description: + + + description_LE + + + + + + + + + + &Enabled + + + true + + + + + + + 6 + + + + + &Command + + + + + + + &DBus message + + + + + + + + + Type: + + + + + + + + + Qt::Horizontal + + + + + + + 0 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 6 + + + 0 + + + + + Co&mmand: + + + command_PTE + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 6 + + + 6 + + + + + S&ervice: + + + service_LE + + + + + + + + + + &Path: + + + path_LE + + + + + + + + + + &Interface: + + + interface_LE + + + + + + + + + + &Method: + + + method_LE + + + + + + + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + + ShortcutSelector + QToolButton +
shortcut_selector.h
+
+
+ + shortcut_SS + description_LE + enabled_CB + command_RB + dbus_method_RB + command_PTE + service_LE + path_LE + interface_LE + method_LE + buttonBox + + + + + buttonBox + accepted() + EditActionDialog + accept() + + + 248 + 254 + + + 157 + 274 + + + + + buttonBox + rejected() + EditActionDialog + reject() + + + 316 + 260 + + + 286 + 274 + + + + +
diff --git a/config/lxqt-config-globalkeyshortcuts.desktop.in b/config/lxqt-config-globalkeyshortcuts.desktop.in new file mode 100644 index 0000000..12d7669 --- /dev/null +++ b/config/lxqt-config-globalkeyshortcuts.desktop.in @@ -0,0 +1,11 @@ +[Desktop Entry] +Type=Application +Name=Shortcut Keys +GenericName=Keyboard Shortcut Settings +Comment=Configure shortcut keys in LXQt +Exec=lxqt-config-globalkeyshortcuts +Icon=preferences-desktop-keyboard +Categories=Settings;DesktopSettings;Qt;LXQt; +OnlyShowIn=LXQt;LXDE; + +#TRANSLATIONS_DIR=translations diff --git a/config/main.cpp b/config/main.cpp new file mode 100644 index 0000000..c2db8bd --- /dev/null +++ b/config/main.cpp @@ -0,0 +1,41 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include + +#include "main_window.h" + +int main(int argc, char *argv[]) +{ + LxQt::SingleApplication a(argc, argv); + + MainWindow w; + a.setActivationWindow(&w); + w.show(); + + return a.exec(); +} diff --git a/config/main_window.cpp b/config/main_window.cpp new file mode 100644 index 0000000..1319e1a --- /dev/null +++ b/config/main_window.cpp @@ -0,0 +1,202 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "main_window.h" +#include "actions.h" +#include "default_model.h" +#include "edit_action_dialog.h" +#include "shortcut_delegate.h" + +#include +#include + + +MainWindow::MainWindow(QWidget *parent) + : QDialog(parent) + , mEditActionDialog(0) +{ + setupUi(this); + + QColor grayedOutColour(actions_TV->palette().color(actions_TV->foregroundRole())); + QColor backgroundColour(actions_TV->palette().color(actions_TV->backgroundRole())); + grayedOutColour.toHsl(); + backgroundColour.toHsl(); + grayedOutColour.setHslF(grayedOutColour.hslHueF(), grayedOutColour.hslSaturationF(), (grayedOutColour.lightnessF() + backgroundColour.lightnessF() * 3) / 4.0); + grayedOutColour.toRgb(); + + QFont highlightedFont(actions_TV->font()); + QFont italicFont(actions_TV->font()); + QFont highlightedItalicFont(actions_TV->font()); + highlightedFont.setBold(!highlightedFont.bold()); + italicFont.setItalic(!italicFont.italic()); + highlightedItalicFont.setItalic(!highlightedItalicFont.italic()); + highlightedItalicFont.setBold(!highlightedItalicFont.bold()); + + mActions = new Actions(this); + mDefaultModel = new DefaultModel(mActions, grayedOutColour, highlightedFont, italicFont, highlightedItalicFont, this); + mSortFilterProxyModel = new QSortFilterProxyModel(this); + + mSortFilterProxyModel->setSourceModel(mDefaultModel); + actions_TV->setModel(mSortFilterProxyModel); + + mSelectionModel = new QItemSelectionModel(actions_TV->model()); + actions_TV->setSelectionModel(mSelectionModel); + + actions_TV->setItemDelegateForColumn(1, new ShortcutDelegate(mActions, this)); + + connect(mSelectionModel, SIGNAL(selectionChanged(QItemSelection, QItemSelection)), SLOT(selectionChanged(QItemSelection, QItemSelection))); + + connect(mActions, SIGNAL(daemonDisappeared()), SLOT(daemonDisappeared())); + connect(mActions, SIGNAL(daemonAppeared()), SLOT(daemonAppeared())); + connect(mActions, SIGNAL(multipleActionsBehaviourChanged(MultipleActionsBehaviour)), SLOT(multipleActionsBehaviourChanged(MultipleActionsBehaviour))); +} + +void MainWindow::changeEvent(QEvent *e) +{ + QDialog::changeEvent(e); + switch (e->type()) + { + case QEvent::LanguageChange: + retranslateUi(this); + break; + default: + break; + } +} + +void MainWindow::daemonDisappeared() +{ + add_PB->setEnabled(false); + actions_TV->setEnabled(false); + multipleActionsBehaviour_CB->setEnabled(false); +} + +void MainWindow::daemonAppeared() +{ + add_PB->setEnabled(true); + actions_TV->setEnabled(true); + multipleActionsBehaviour_CB->setEnabled(true); + + multipleActionsBehaviour_CB->setCurrentIndex(mActions->multipleActionsBehaviour()); + + actions_TV->horizontalHeader()->setSortIndicator(0, Qt::AscendingOrder); + + int m = actions_TV->model()->columnCount() - 1; + for (int i = 0; i < m; ++i) + { + actions_TV->resizeColumnToContents(i); + actions_TV->setColumnWidth(i, actions_TV->columnWidth(i) + 20); + } +} + +void MainWindow::multipleActionsBehaviourChanged(MultipleActionsBehaviour behaviour) +{ + multipleActionsBehaviour_CB->setCurrentIndex(behaviour); +} + +void MainWindow::on_multipleActionsBehaviour_CB_currentIndexChanged(int index) +{ + mActions->setMultipleActionsBehaviour(static_cast(index)); +} + +void MainWindow::selectionChanged(const QItemSelection &/*selected*/, const QItemSelection &/*deselected*/) +{ + QModelIndexList rows = mSelectionModel->selectedRows(); + + modify_PB->setEnabled(rows.length() == 1); + remove_PB->setEnabled(rows.length() != 0); + + bool enableSwap = (rows.length() == 2); + if (enableSwap) + { + QPair info0 = mActions->actionById(mDefaultModel->id(mSortFilterProxyModel->mapToSource(rows[0]))); + QPair info1 = mActions->actionById(mDefaultModel->id(mSortFilterProxyModel->mapToSource(rows[1]))); + enableSwap = (info0.first && info1.first && (info0.second.shortcut == info1.second.shortcut)); + } + swap_PB->setEnabled(enableSwap); +} + +void MainWindow::on_add_PB_clicked() +{ + editAction(QModelIndex()); +} + +void MainWindow::on_modify_PB_clicked() +{ + editAction(mSelectionModel->currentIndex()); +} + +void MainWindow::on_swap_PB_clicked() +{ + QModelIndexList rows = mSelectionModel->selectedRows(); + mActions->swapActions(mDefaultModel->id(mSortFilterProxyModel->mapToSource(rows[0])), mDefaultModel->id(mSortFilterProxyModel->mapToSource(rows[1]))); +} + +void MainWindow::on_remove_PB_clicked() +{ + foreach(QModelIndex rowIndex, mSelectionModel->selectedRows()) + mActions->removeAction(mDefaultModel->id(mSortFilterProxyModel->mapToSource(rowIndex))); +} + +void MainWindow::on_actions_TV_doubleClicked(const QModelIndex &index) +{ + switch (index.column()) + { + case 0: + { + qulonglong id = mDefaultModel->id(mSortFilterProxyModel->mapToSource(index)); + mActions->enableAction(id, !mActions->isActionEnabled(id)); + } + break; + + case 1: + break; + + default: + editAction(index); + } +} + +void MainWindow::editAction(const QModelIndex &index) +{ + qulonglong id = 0; + + if (index.isValid()) + { + id = mDefaultModel->id(mSortFilterProxyModel->mapToSource(index)); + } + + if (!mEditActionDialog) + { + mEditActionDialog = new EditActionDialog(mActions, this); + } + + if (mEditActionDialog->load(id)) + { + mEditActionDialog->exec(); + } +} diff --git a/config/main_window.h b/config/main_window.h new file mode 100644 index 0000000..968759b --- /dev/null +++ b/config/main_window.h @@ -0,0 +1,80 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_CONFIG__MAIN_WINDOW__INCLUDED +#define GLOBAL_ACTION_CONFIG__MAIN_WINDOW__INCLUDED + + +#include "ui_main_window.h" +#include "../daemon/meta_types.h" + + +class Actions; +class DefaultModel; +class QItemSelectionModel; +class QSortFilterProxyModel; +class EditActionDialog; + +class MainWindow : public QDialog, private Ui::MainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget *parent = 0); + +public slots: + void daemonDisappeared(); + void daemonAppeared(); + + void multipleActionsBehaviourChanged(MultipleActionsBehaviour behaviour); + +protected: + void changeEvent(QEvent *e); + +protected slots: + void selectionChanged(const QItemSelection &, const QItemSelection &); + + void on_add_PB_clicked(); + void on_modify_PB_clicked(); + void on_swap_PB_clicked(); + void on_remove_PB_clicked(); + + void on_multipleActionsBehaviour_CB_currentIndexChanged(int); + + void on_actions_TV_doubleClicked(const QModelIndex &); + +private: + Actions *mActions; + DefaultModel *mDefaultModel; + QSortFilterProxyModel *mSortFilterProxyModel; + QItemSelectionModel *mSelectionModel; + EditActionDialog *mEditActionDialog; + + void editAction(const QModelIndex &); +}; + +#endif // GLOBAL_ACTION_CONFIG__MAIN_WINDOW__INCLUDED diff --git a/config/main_window.ui b/config/main_window.ui new file mode 100644 index 0000000..1708271 --- /dev/null +++ b/config/main_window.ui @@ -0,0 +1,212 @@ + + + MainWindow + + + + 0 + 0 + 720 + 314 + + + + Global Actions Manager + + + + + + + + QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed + + + true + + + QAbstractItemView::ExtendedSelection + + + QAbstractItemView::SelectRows + + + true + + + true + + + false + + + + + + + + + false + + + Add ... + + + + + + + false + + + Remove + + + + + + + false + + + Modify ... + + + + + + + Qt::Horizontal + + + + + + + false + + + Swap + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + Multiple actions behaviour: + + + + + + + + First + + + + + Last + + + + + None + + + + + All + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Close + + + + + + + + + + actions_TV + add_PB + remove_PB + modify_PB + swap_PB + close_PB + + + + + close_PB + clicked() + MainWindow + close() + + + 476 + 281 + + + 488 + 260 + + + + + diff --git a/config/shortcut_delegate.cpp b/config/shortcut_delegate.cpp new file mode 100644 index 0000000..5cbc5ec --- /dev/null +++ b/config/shortcut_delegate.cpp @@ -0,0 +1,72 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "shortcut_delegate.h" +#include "shortcut_selector.h" +#include "actions.h" + +#include + + +ShortcutDelegate::ShortcutDelegate(Actions *actions, QObject *parent) + : QItemDelegate(parent) + , mActions(actions) +{ +} + +QWidget *ShortcutDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &/* option */, const QModelIndex &/* index */) const +{ + ShortcutSelector *button = new ShortcutSelector(mActions, parent); + button->autoApplyShortcut(); + QTimer::singleShot(0, button, SLOT(click())); + return button; +} + +void ShortcutDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const +{ + ShortcutSelector *button = static_cast(editor); + if (button->isGrabbing()) + button->cancelNow(); + model->setData(index, button->text(), Qt::EditRole); +} + +void ShortcutDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const +{ + editor->setGeometry(option.rect); +} + +void ShortcutDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + static_cast(editor)->setText(index.model()->data(index, Qt::EditRole).toString()); +} + +QSize ShortcutDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const +{ + QSize sizeHint = QItemDelegate::sizeHint(option , index); + sizeHint.setHeight(sizeHint.height() + 15); + return sizeHint; +} diff --git a/config/shortcut_delegate.h b/config/shortcut_delegate.h new file mode 100644 index 0000000..d5768aa --- /dev/null +++ b/config/shortcut_delegate.h @@ -0,0 +1,57 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_CONFIG__SHORTCUT_DELEGATE__INCLUDED +#define GLOBAL_ACTION_CONFIG__SHORTCUT_DELEGATE__INCLUDED + + +#include + + +class Actions; + +class ShortcutDelegate : public QItemDelegate +{ + Q_OBJECT + +public: + explicit ShortcutDelegate(Actions *actions, QObject *parent = 0); + + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const; + + void setEditorData(QWidget *editor, const QModelIndex &index) const; + void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const; + + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; + + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const; + +private: + Actions *mActions; +}; + +#endif // GLOBAL_ACTION_CONFIG__SHORTCUT_DELEGATE__INCLUDED diff --git a/config/shortcut_selector.cpp b/config/shortcut_selector.cpp new file mode 100644 index 0000000..7c2bb8e --- /dev/null +++ b/config/shortcut_selector.cpp @@ -0,0 +1,164 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "shortcut_selector.h" +#include "actions.h" + +#include +#include + + +ShortcutSelector::ShortcutSelector(Actions *actions, QWidget *parent) + : QToolButton(parent) + , mActions(0) + , mShortcutTimer(new QTimer(this)) + , mAutoApplyShortcut(false) +{ + init(); + setActions(actions); +} + +ShortcutSelector::ShortcutSelector(QWidget *parent) + : QToolButton(parent) + , mActions(0) + , mShortcutTimer(new QTimer(this)) + , mAutoApplyShortcut(false) +{ + init(); +} + +void ShortcutSelector::init() +{ + setCheckable(true); + setFocusPolicy(Qt::StrongFocus); + + mShortcutTimer->setInterval(1000); + mShortcutTimer->setSingleShot(false); + + connect(this, SIGNAL(clicked()), this, SLOT(grabShortcut())); + + connect(mShortcutTimer, SIGNAL(timeout()), this, SLOT(shortcutTimer_timeout())); +} + +void ShortcutSelector::setActions(Actions *actions) +{ + if (mActions) + { + return; + } + mActions = actions; + connect(mActions, SIGNAL(grabShortcutCancelled()), this, SLOT(grabShortcut_fail())); + connect(mActions, SIGNAL(grabShortcutTimedout()), this, SLOT(grabShortcut_fail())); + connect(mActions, SIGNAL(grabShortcutFailed()), this, SLOT(grabShortcut_fail())); + connect(mActions, SIGNAL(shortcutGrabbed(QString)), this, SLOT(newShortcutGrabbed(QString))); +} + +void ShortcutSelector::grabShortcut(int timeout) +{ + if (!mActions) + { + return; + } + + if (!isChecked()) + { + mActions->cancelShortcutGrab(); + return; + } + + mTimeoutCounter = timeout; + mOldShortcut = text(); + setText(QString::number(mTimeoutCounter)); + mShortcutTimer->start(); + mActions->grabShortcut(mTimeoutCounter * mShortcutTimer->interval()); +} + +void ShortcutSelector::shortcutTimer_timeout() +{ + if (mTimeoutCounter > 0) + { + --mTimeoutCounter; + setText(QString::number(mTimeoutCounter)); + } + else + setText(QString()); +} + +void ShortcutSelector::grabShortcut_fail() +{ + if (isChecked()) + { + setChecked(false); + mShortcutTimer->stop(); + setText(mOldShortcut); + emit shortcutGrabbed(mOldShortcut); + } +} + +void ShortcutSelector::newShortcutGrabbed(const QString &newShortcut) +{ + setChecked(false); + mShortcutTimer->stop(); + if (mAutoApplyShortcut) + setText(newShortcut); + else + setText(QString()); + emit shortcutGrabbed(newShortcut); +} + +void ShortcutSelector::clear() +{ + setText(QString()); +} + +QAction * ShortcutSelector::addMenuAction(const QString &title) +{ + QMenu *subMenu = menu(); + if (!subMenu) + { + setPopupMode(QToolButton::MenuButtonPopup); + subMenu = new QMenu(this); + setMenu(subMenu); + } + QAction *action = new QAction(title, subMenu); + subMenu->addAction(action); + return action; +} + +bool ShortcutSelector::isGrabbing() const +{ + return isChecked(); +} + +void ShortcutSelector::cancelNow() +{ + if (isChecked()) + { + grabShortcut_fail(); + mActions->cancelShortcutGrab(); + } +} diff --git a/config/shortcut_selector.h b/config/shortcut_selector.h new file mode 100644 index 0000000..b10d214 --- /dev/null +++ b/config/shortcut_selector.h @@ -0,0 +1,80 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_CONFIG__SHORTCUT_SELECTOR__INCLUDED +#define GLOBAL_ACTION_CONFIG__SHORTCUT_SELECTOR__INCLUDED + +#include +#include +#include + + +class Actions; +class QTimer; + +class ShortcutSelector : public QToolButton +{ + Q_OBJECT +public: + explicit ShortcutSelector(Actions *actions, QWidget *parent = 0); + explicit ShortcutSelector(QWidget *parent = 0); + void setActions(Actions *actions); + + QAction *addMenuAction(const QString &title); + + bool shortcutAutoApplied(void) const { return mAutoApplyShortcut; } + + bool isGrabbing() const; + +signals: + void shortcutGrabbed(const QString &); + +public slots: + void grabShortcut(int timeout = 10); + + void clear(); + + void autoApplyShortcut(bool value = true) { mAutoApplyShortcut = value; } + + void cancelNow(); + +private slots: + void shortcutTimer_timeout(); + void grabShortcut_fail(); + void newShortcutGrabbed(const QString &); + +private: + Actions *mActions; + QString mOldShortcut; + int mTimeoutCounter; + QTimer *mShortcutTimer; + bool mAutoApplyShortcut; + + void init(); +}; + +#endif // GLOBAL_ACTION_CONFIG__SHORTCUT_SELECTOR__INCLUDED diff --git a/config/translations/lxqt-config-globalkeyshortcuts.ts b/config/translations/lxqt-config-globalkeyshortcuts.ts new file mode 100644 index 0000000..5546633 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts.ts @@ -0,0 +1,93 @@ + + + + + CommandFinder + + ... + + + + Find a command + + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + + + + Description + + + + Shortcut + + + + Command + + + + Add New + + + + Remove + + + + Add Group + + + + Reset + + + + Close + + + + + ShortcutEditor + + None + + + + Add Shortcut + + + + Remove + + + + New Group + + + + Reset Changes + + + + Question + + + + Delete group: %1? (everything inside will be removed altogether) + + + + Delete %1? + + + + Binding for %1 already exists. Replace old one? + + + + diff --git a/config/translations/lxqt-config-globalkeyshortcuts_ar.ts b/config/translations/lxqt-config-globalkeyshortcuts_ar.ts new file mode 100644 index 0000000..a83dcd2 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_ar.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + + + + Find a command + + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + + + + Description + الوصف + + + Shortcut + + + + Command + + + + Add New + + + + Remove + إزالة + + + Add Group + + + + Reset + + + + Close + إغلاق + + + + ShortcutEditor + + None + ﻻشيء + + + Add Shortcut + + + + Remove + إزالة + + + New Group + + + + Reset Changes + + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_cs.desktop b/config/translations/lxqt-config-globalkeyshortcuts_cs.desktop new file mode 100644 index 0000000..7681216 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_cs.desktop @@ -0,0 +1,4 @@ +# Translations +Name[cs]=Nastavení klávesových zkratek +Comment[cs]=Nastavit celkové klávesové zkratky pro prostředí LxQt +GenericName[cs]=Nastavení celkových klávesových zkratek diff --git a/config/translations/lxqt-config-globalkeyshortcuts_cs.ts b/config/translations/lxqt-config-globalkeyshortcuts_cs.ts new file mode 100644 index 0000000..f7e4397 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_cs.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + Najít příkaz + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + Editor zkratek + + + Description + Popis + + + Shortcut + Zkratka + + + Command + Příkaz + + + Add New + Přidat novou + + + Remove + Odstranit + + + Add Group + Přidat skupinu + + + Reset + Vynulovat + + + Close + Zavřít + + + + ShortcutEditor + + None + Žádná + + + Add Shortcut + Přidat zkratku + + + Remove + Odstranit + + + New Group + Nová skupina + + + Reset Changes + Vynulovat změny + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_cs_CZ.desktop b/config/translations/lxqt-config-globalkeyshortcuts_cs_CZ.desktop new file mode 100644 index 0000000..f219214 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_cs_CZ.desktop @@ -0,0 +1,4 @@ +# Translations +Name[cs_CZ]=Nastavení klávesových zkratek +Comment[cs_CZ]=Nastavit celkové klávesové zkratky pro prostředí LxQt +GenericName[cs_CZ]=Nastavení celkových klávesových zkratek diff --git a/config/translations/lxqt-config-globalkeyshortcuts_cs_CZ.ts b/config/translations/lxqt-config-globalkeyshortcuts_cs_CZ.ts new file mode 100644 index 0000000..95b0518 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_cs_CZ.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + Najít příkaz + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + Editor zkratek + + + Description + Popis + + + Shortcut + Zkratka + + + Command + Příkaz + + + Add New + Přidat novou + + + Remove + Odstranit + + + Add Group + Přidat skupinu + + + Reset + Vynulovat + + + Close + Zavřít + + + + ShortcutEditor + + None + Žádná + + + Add Shortcut + Přidat zkratku + + + Remove + Odstranit + + + New Group + Nová skupina + + + Reset Changes + Vynulovat změny + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_da.desktop b/config/translations/lxqt-config-globalkeyshortcuts_da.desktop new file mode 100644 index 0000000..73b6098 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_da.desktop @@ -0,0 +1,4 @@ +# Translations +Name[da]=Konfiguration af LxQt Tastaturgenveje +Comment[da]=Konfigurer globale tastaturgenveje for LxQt-skrivebordet +GenericName[da]=Konfiguration af LxQt Globale Tastaturgenveje diff --git a/config/translations/lxqt-config-globalkeyshortcuts_da_DK.desktop b/config/translations/lxqt-config-globalkeyshortcuts_da_DK.desktop new file mode 100644 index 0000000..75a41c3 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_da_DK.desktop @@ -0,0 +1,4 @@ +# Translations +Name[da_DK]=Konfiguration af LxQt Tastaturgenveje +Comment[da_DK]=Konfigurer globale tastaturgenveje for LxQt-skrivebordet +GenericName[da_DK]=Konfiguration af LxQt Globale Tastaturgenveje diff --git a/config/translations/lxqt-config-globalkeyshortcuts_da_DK.ts b/config/translations/lxqt-config-globalkeyshortcuts_da_DK.ts new file mode 100644 index 0000000..1b5f50f --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_da_DK.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + Find en kommando + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + LxQt Tastaturgenveje + + + Description + Beskrivelse + + + Shortcut + Genvej + + + Command + Kommando + + + Add New + Tilføj ny + + + Remove + Fjern + + + Add Group + Tilføj gruppe + + + Reset + Nulstil + + + Close + Afslut + + + + ShortcutEditor + + None + Ingen + + + Add Shortcut + Tilføj genvej + + + Remove + Fjern + + + New Group + Ny gruppe + + + Reset Changes + Nulstil ændringer + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_de.ts b/config/translations/lxqt-config-globalkeyshortcuts_de.ts new file mode 100644 index 0000000..d1b91fb --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_de.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + + + + Find a command + + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + + + + Description + + + + Shortcut + + + + Command + + + + Add New + + + + Remove + Entfernen + + + Add Group + + + + Reset + + + + Close + Schließen + + + + ShortcutEditor + + None + + + + Add Shortcut + + + + Remove + Entfernen + + + New Group + + + + Reset Changes + + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_de_DE.desktop b/config/translations/lxqt-config-globalkeyshortcuts_de_DE.desktop new file mode 100644 index 0000000..9db6c39 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_de_DE.desktop @@ -0,0 +1,4 @@ +# Translations +Name[de_DE]=LxQt Hotkey Konfiguration +Comment[de_DE]=Globalen Hotkey für LxQt Desktop definieren +GenericName[de_DE]=LxQt Globale Hotkey Konfiguration diff --git a/config/translations/lxqt-config-globalkeyshortcuts_de_DE.ts b/config/translations/lxqt-config-globalkeyshortcuts_de_DE.ts new file mode 100644 index 0000000..4496961 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_de_DE.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + Finde einen Befehl + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + LxQt Hotkey Editor + + + Description + Beschreibung + + + Shortcut + Hotkey + + + Command + Befehl + + + Add New + Hinzufügen + + + Remove + Entfernen + + + Add Group + Gruppe hinzufügen + + + Reset + Verwerfen + + + Close + Schließen + + + + ShortcutEditor + + None + Kein + + + Add Shortcut + Hotkey hinzufügen + + + Remove + Entfernen + + + New Group + Neue Gruppe + + + Reset Changes + Änderungen verwerfen + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_el_GR.desktop b/config/translations/lxqt-config-globalkeyshortcuts_el_GR.desktop new file mode 100644 index 0000000..6943d5e --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_el_GR.desktop @@ -0,0 +1,4 @@ +# Translations +Name[el_GR]=Διαμόρφωση συντομεύσεων LxQt +Comment[el_GR]=Διαμόρφωση των καθολικών συντομεύσεων του LxQt +GenericName[el_GR]=Διαμόρφωση καθολικών συντομεύσεων LxQt diff --git a/config/translations/lxqt-config-globalkeyshortcuts_el_GR.ts b/config/translations/lxqt-config-globalkeyshortcuts_el_GR.ts new file mode 100644 index 0000000..b4425ab --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_el_GR.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + Εύρεση εντολής + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + Επεξεργασία συντομεύσεων LxQt + + + Description + Περιγραφή + + + Shortcut + Συντόμευση + + + Command + Εντολή + + + Add New + Προσθήκη νέας + + + Remove + Αφαίρεση + + + Add Group + Προσθήκη ομάδας + + + Reset + Επαναφορά + + + Close + Κλείσιμο + + + + ShortcutEditor + + None + Κανένα + + + Add Shortcut + Προσθήκη συντόμευσης + + + Remove + Αφαίρεση + + + New Group + Νέα ομάδα + + + Reset Changes + Επαναφορά αλλαγών + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_eo.ts b/config/translations/lxqt-config-globalkeyshortcuts_eo.ts new file mode 100644 index 0000000..b660dda --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_eo.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + + + + Find a command + + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + + + + Description + Priskribo + + + Shortcut + + + + Command + + + + Add New + + + + Remove + Forigi + + + Add Group + + + + Reset + + + + Close + Fermi + + + + ShortcutEditor + + None + Nenio + + + Add Shortcut + + + + Remove + Forigi + + + New Group + + + + Reset Changes + + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_es.desktop b/config/translations/lxqt-config-globalkeyshortcuts_es.desktop new file mode 100644 index 0000000..0d356d6 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_es.desktop @@ -0,0 +1,4 @@ +# Translations +Name[es]=Configuración de Atajos de LxQt +Comment[es]=Configure atajos globales del Escritorio de LxQt +GenericName[es]=Configuración de Atajos Globales de LxQt diff --git a/config/translations/lxqt-config-globalkeyshortcuts_es.ts b/config/translations/lxqt-config-globalkeyshortcuts_es.ts new file mode 100644 index 0000000..1087c9e --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_es.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + Encontrar un comando + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + Editor de Atajos de LxQt + + + Description + Descripción + + + Shortcut + Atajo + + + Command + Comando + + + Add New + Agregar nuevo + + + Remove + Borrar + + + Add Group + Agregar Grupo + + + Reset + Reiniciar + + + Close + Cerrar + + + + ShortcutEditor + + None + Ninguno + + + Add Shortcut + Agregar Atajo + + + Remove + Borrar + + + New Group + Nuevo Grupo + + + Reset Changes + Reiniciar Cambios + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_es_VE.desktop b/config/translations/lxqt-config-globalkeyshortcuts_es_VE.desktop new file mode 100644 index 0000000..4437987 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_es_VE.desktop @@ -0,0 +1,4 @@ +# Translations +Name[es_VE]=Configuracion de atajos de LxQt +Comment[es_VE]=Configura atajos de teclado para el escritorio LxQt +GenericName[es_VE]=Configuracion de Atajos globales de LxQt diff --git a/config/translations/lxqt-config-globalkeyshortcuts_es_VE.ts b/config/translations/lxqt-config-globalkeyshortcuts_es_VE.ts new file mode 100644 index 0000000..5df8082 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_es_VE.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + Encontrar un comando + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + Editor de atajos + + + Description + Descripcion + + + Shortcut + Atajo + + + Command + Comando + + + Add New + Agregar Nuevo + + + Remove + Remover + + + Add Group + Agregar Grupo + + + Reset + Reiniciar + + + Close + Cerrar + + + + ShortcutEditor + + None + Nada + + + Add Shortcut + Nuevo atajo + + + Remove + Remover + + + New Group + Nuevo Grupo + + + Reset Changes + Reiniciar cambios + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_eu.desktop b/config/translations/lxqt-config-globalkeyshortcuts_eu.desktop new file mode 100644 index 0000000..743cffd --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_eu.desktop @@ -0,0 +1,4 @@ +# Translations +Name[eu]=LxQt lasterbideen konfigurazioa +Comment[eu]=Konfiguratu LxQt mahaigainaren lasterbide globalak +GenericName[eu]=LxQt lasterbide globalen konfigurazioa diff --git a/config/translations/lxqt-config-globalkeyshortcuts_eu.ts b/config/translations/lxqt-config-globalkeyshortcuts_eu.ts new file mode 100644 index 0000000..7a64f38 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_eu.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + Bilatu komando bat + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + LxQt lasterbide-editorea + + + Description + Deskribapena + + + Shortcut + Lasterbidea + + + Command + Komandoa + + + Add New + Gehitu berria + + + Remove + Kendu + + + Add Group + Gehitu taldea + + + Reset + Berrezarri + + + Close + Itxi + + + + ShortcutEditor + + None + Bat ere ez + + + Add Shortcut + Gehitu lasterbidea + + + Remove + Kendu + + + New Group + Talde berria + + + Reset Changes + Berrezarri aldaketak + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_fi.ts b/config/translations/lxqt-config-globalkeyshortcuts_fi.ts new file mode 100644 index 0000000..0959c80 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_fi.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + + + + Find a command + + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + + + + Description + Kuvaus + + + Shortcut + + + + Command + + + + Add New + + + + Remove + Poista + + + Add Group + + + + Reset + + + + Close + Sulje + + + + ShortcutEditor + + None + + + + Add Shortcut + + + + Remove + Poista + + + New Group + + + + Reset Changes + + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_fr_FR.ts b/config/translations/lxqt-config-globalkeyshortcuts_fr_FR.ts new file mode 100644 index 0000000..6d988bd --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_fr_FR.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + + + + Find a command + + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + + + + Description + + + + Shortcut + + + + Command + + + + Add New + + + + Remove + Supprimer + + + Add Group + + + + Reset + + + + Close + Fermer + + + + ShortcutEditor + + None + Aucun(e) + + + Add Shortcut + + + + Remove + Supprimer + + + New Group + + + + Reset Changes + + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_it_IT.desktop b/config/translations/lxqt-config-globalkeyshortcuts_it_IT.desktop new file mode 100644 index 0000000..f614196 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_it_IT.desktop @@ -0,0 +1,4 @@ +# Translations +Name[it_IT]=Configurazione delle scorciatoie di LxQt +Comment[it_IT]=Configura le scorciatoie globali del desktop LxQt +GenericName[it_IT]=Configurazione delle scorciatoie globali di LxQt diff --git a/config/translations/lxqt-config-globalkeyshortcuts_it_IT.ts b/config/translations/lxqt-config-globalkeyshortcuts_it_IT.ts new file mode 100644 index 0000000..9154b81 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_it_IT.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + Trova un comando + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + Editor delle scorciatoie di LxQt + + + Description + Descrizione + + + Shortcut + Scorciatoie + + + Command + Comando + + + Add New + Aggiungi nuova + + + Remove + Rimuovi + + + Add Group + Aggiungi gruppo + + + Reset + Azzera + + + Close + Chiudi + + + + ShortcutEditor + + None + Nessuna + + + Add Shortcut + Aggiungi scorciatoia + + + Remove + Rimuovi + + + New Group + Nuovo gruppo + + + Reset Changes + Azzera le modifiche + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_ja.ts b/config/translations/lxqt-config-globalkeyshortcuts_ja.ts new file mode 100644 index 0000000..bc8968e --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_ja.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + + + + Find a command + + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + + + + Description + + + + Shortcut + + + + Command + + + + Add New + + + + Remove + + + + Add Group + + + + Reset + + + + Close + 閉じる + + + + ShortcutEditor + + None + なし + + + Add Shortcut + + + + Remove + + + + New Group + + + + Reset Changes + + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_lt.ts b/config/translations/lxqt-config-globalkeyshortcuts_lt.ts new file mode 100644 index 0000000..7047418 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_lt.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + + + + Find a command + + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + + + + Description + + + + Shortcut + + + + Command + + + + Add New + + + + Remove + Pašalinti + + + Add Group + + + + Reset + + + + Close + Uždaryti + + + + ShortcutEditor + + None + Nieko + + + Add Shortcut + + + + Remove + Pašalinti + + + New Group + + + + Reset Changes + + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_nl.ts b/config/translations/lxqt-config-globalkeyshortcuts_nl.ts new file mode 100644 index 0000000..e73fe57 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_nl.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + + + + Find a command + + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + + + + Description + + + + Shortcut + + + + Command + + + + Add New + + + + Remove + Verwijderen + + + Add Group + + + + Reset + + + + Close + Sluiten + + + + ShortcutEditor + + None + Geen + + + Add Shortcut + + + + Remove + Verwijderen + + + New Group + + + + Reset Changes + + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_pl_PL.desktop b/config/translations/lxqt-config-globalkeyshortcuts_pl_PL.desktop new file mode 100644 index 0000000..502470f --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_pl_PL.desktop @@ -0,0 +1,4 @@ +# Translations +Name[pl_PL]=Konfiguracja skrótów pulpitu LxQt +Comment[pl_PL]=Określ globalny skrót pulpitu LxQt +GenericName[pl_PL]=Konfiguracja globalnych skrótów pulpitu LxQt diff --git a/config/translations/lxqt-config-globalkeyshortcuts_pl_PL.ts b/config/translations/lxqt-config-globalkeyshortcuts_pl_PL.ts new file mode 100644 index 0000000..07e3884 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_pl_PL.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + Znajdź polecenie + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + Edytor Skrótów LxQt + + + Description + Opis + + + Shortcut + Skrót + + + Command + Polecenie + + + Add New + Dodaj + + + Remove + Usuń + + + Add Group + Dodaj grupę + + + Reset + Reset + + + Close + Zamknij + + + + ShortcutEditor + + None + Brak + + + Add Shortcut + Dodaj skrót + + + Remove + Usuń + + + New Group + Dodaj grupę + + + Reset Changes + Resetuj zmiany + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_pt.desktop b/config/translations/lxqt-config-globalkeyshortcuts_pt.desktop new file mode 100644 index 0000000..393cf64 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_pt.desktop @@ -0,0 +1,4 @@ +# Translations +Name[pt]=Teclas de atalho +GenericName[pt]=Definições das teclas de atalho +Comment[pt]=Configurar as teclas de atalho do LxQt diff --git a/config/translations/lxqt-config-globalkeyshortcuts_pt.ts b/config/translations/lxqt-config-globalkeyshortcuts_pt.ts new file mode 100644 index 0000000..c9e3ebc --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_pt.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + Encontrar um comando + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + Editor de atalhos do LxQt + + + Description + Descrição + + + Shortcut + Atalho + + + Command + Comando + + + Add New + Adicionar + + + Remove + Remover + + + Add Group + Adicionar grupo + + + Reset + Restaurar + + + Close + Fechar + + + + ShortcutEditor + + None + Nada + + + Add Shortcut + Adicionar atalho + + + Remove + Remover + + + New Group + Novo grupo + + + Reset Changes + Restaurar alterações + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_pt_BR.desktop b/config/translations/lxqt-config-globalkeyshortcuts_pt_BR.desktop new file mode 100644 index 0000000..f8474f6 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_pt_BR.desktop @@ -0,0 +1,4 @@ +# Translations +Name[pt_BR]=Configuração Do Atalho +Comment[pt_BR]=Configurar atalhos globais do desktop LxQt +GenericName[pt_BR]=Configurações Dos Atalhos Globais diff --git a/config/translations/lxqt-config-globalkeyshortcuts_pt_BR.ts b/config/translations/lxqt-config-globalkeyshortcuts_pt_BR.ts new file mode 100644 index 0000000..fee7ca2 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_pt_BR.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + Encontrar um comando + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + Editor De Atalhos + + + Description + Descrição + + + Shortcut + Atalho + + + Command + Comando + + + Add New + Adicionar Novo + + + Remove + Remover + + + Add Group + Adicionar Grupo + + + Reset + Redefinir + + + Close + Fechar + + + + ShortcutEditor + + None + Nenhum + + + Add Shortcut + Adicionat Atalho + + + Remove + Remover + + + New Group + Novo Grupo + + + Reset Changes + Redefinir Alterações + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_ro_RO.desktop b/config/translations/lxqt-config-globalkeyshortcuts_ro_RO.desktop new file mode 100644 index 0000000..c8db222 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_ro_RO.desktop @@ -0,0 +1,4 @@ +# Translations +Name[ro_RO]=Configurație acceleratori LxQt +Comment[ro_RO]=Configurează acceleratorii globali pentru LxQt +GenericName[ro_RO]=Configurație acceleratori globali LxQt diff --git a/config/translations/lxqt-config-globalkeyshortcuts_ro_RO.ts b/config/translations/lxqt-config-globalkeyshortcuts_ro_RO.ts new file mode 100644 index 0000000..53e9379 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_ro_RO.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + Caută o comandă + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + Editor acceleratori LxQt + + + Description + Descriere + + + Shortcut + Accelerator + + + Command + Comandă + + + Add New + Adaugă nou + + + Remove + Elimină + + + Add Group + Adaugă grup + + + Reset + Resetează + + + Close + Închide + + + + ShortcutEditor + + None + Nimic + + + Add Shortcut + Adaugă accelerator + + + Remove + Elimină + + + New Group + Grup nou + + + Reset Changes + Resetează modificările + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_ru.desktop b/config/translations/lxqt-config-globalkeyshortcuts_ru.desktop new file mode 100644 index 0000000..aeba45b --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_ru.desktop @@ -0,0 +1,4 @@ +# Translations +Name[ru]=Сочетания клавиш +Comment[ru]=Настроить сочетание клавиш в LxQt +GenericName[ru]=Настроить сочетания клавиш \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_ru.ts b/config/translations/lxqt-config-globalkeyshortcuts_ru.ts new file mode 100644 index 0000000..75f9917 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_ru.ts @@ -0,0 +1,168 @@ + + + + + DefaultModel + + + Command + Команда + + + + DBus call + Вызов DBus + + + + Client + Клиент + + + + Id + id + + + + Shortcut + Сочетание клавиш + + + + Description + Описание + + + + Type + Тип + + + + Info + Информация + + + + EditActionDialog + + + Edit Action + Изменить действие + + + + &Shortcut: + &Сочетание клавиш: + + + + &Description: + &Описание: + + + + &Enabled + &Разрешено + + + + &Command + &Команда + + + + &DBus message + Сообщение &DBus + + + + Type: + Тип: + + + + Co&mmand: + Ко&манда: + + + + S&ervice: + С&ервис: + + + + &Path: + &Путь: + + + + &Interface: + &Интерфейс: + + + + &Method: + &Метод: + + + + MainWindow + + + Global Actions Manager + Управление глобальными действиями + + + + Add ... + Добавить… + + + + Remove + Удалить + + + + Modify ... + Изменить… + + + + Swap + Заменить + + + + Multiple actions behaviour: + Поведение нескольких действий: + + + + First + Первое + + + + Last + Последнее + + + + None + Не задано + + + + All + Все + + + + Close + Закрыть + + + diff --git a/config/translations/lxqt-config-globalkeyshortcuts_ru_RU.desktop b/config/translations/lxqt-config-globalkeyshortcuts_ru_RU.desktop new file mode 100644 index 0000000..adc25b7 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_ru_RU.desktop @@ -0,0 +1,4 @@ +# Translations +Name[ru_RU]=Сочетания клавиш +Comment[ru_RU]=Настроить сочетание клавиш в LxQt +GenericName[ru_RU]=Настроить сочетания клавиш \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_ru_RU.ts b/config/translations/lxqt-config-globalkeyshortcuts_ru_RU.ts new file mode 100644 index 0000000..9326e0e --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_ru_RU.ts @@ -0,0 +1,168 @@ + + + + + DefaultModel + + + Command + Команда + + + + DBus call + Вызов DBus + + + + Client + Клиент + + + + Id + id + + + + Shortcut + Сочетание клавиш + + + + Description + Описание + + + + Type + Тип + + + + Info + Информация + + + + EditActionDialog + + + Edit Action + Изменить действие + + + + &Shortcut: + &Сочетание клавиш: + + + + &Description: + &Описание: + + + + &Enabled + &Разрешено + + + + &Command + &Команда + + + + &DBus message + Сообщение &DBus + + + + Type: + Тип: + + + + Co&mmand: + Ко&манда: + + + + S&ervice: + С&ервис: + + + + &Path: + &Путь: + + + + &Interface: + &Интерфейс: + + + + &Method: + &Метод: + + + + MainWindow + + + Global Actions Manager + Управление глобальными действиями + + + + Add ... + Добавить… + + + + Remove + Удалить + + + + Modify ... + Изменить… + + + + Swap + Заменить + + + + Multiple actions behaviour: + Поведение нескольких действий: + + + + First + Первое + + + + Last + Последнее + + + + None + Не задано + + + + All + Все + + + + Close + Закрыть + + + diff --git a/config/translations/lxqt-config-globalkeyshortcuts_sl.ts b/config/translations/lxqt-config-globalkeyshortcuts_sl.ts new file mode 100644 index 0000000..3f51acf --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_sl.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + + + + Find a command + + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + + + + Description + + + + Shortcut + + + + Command + + + + Add New + + + + Remove + Odstrani + + + Add Group + + + + Reset + + + + Close + Zapri + + + + ShortcutEditor + + None + Brez + + + Add Shortcut + + + + Remove + Odstrani + + + New Group + + + + Reset Changes + + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_th_TH.desktop b/config/translations/lxqt-config-globalkeyshortcuts_th_TH.desktop new file mode 100644 index 0000000..1f5bb10 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_th_TH.desktop @@ -0,0 +1,4 @@ +# Translations +Name[th_TH]=การตั้งค่าปุ่มลัด LxQt +Comment[th_TH]=ตั้งค่าปุ่มลัดหลักของ LxQt เดกส์ท็อป +GenericName[th_TH]=การตั้งค่าปุ่มลัดหลัก LxQt diff --git a/config/translations/lxqt-config-globalkeyshortcuts_th_TH.ts b/config/translations/lxqt-config-globalkeyshortcuts_th_TH.ts new file mode 100644 index 0000000..f6dc646 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_th_TH.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + หาคำสั่ง + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + ตัวแก้ไขปุ่มลัด LxQt + + + Description + รายละเอียด + + + Shortcut + ปุ่มลัด + + + Command + คำสั่ง + + + Add New + เพิ่มใหม่ + + + Remove + ลบทิ้ง + + + Add Group + เพิ่มกลุ่ม + + + Reset + กลับค่าเดิม + + + Close + ปิด + + + + ShortcutEditor + + None + ไม่ต้อง + + + Add Shortcut + เพิ่มปุ่มลัด + + + Remove + ลบทิ้ง + + + New Group + กลุ่มใหม่ + + + Reset Changes + กลับค่าเดิม + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_tr.ts b/config/translations/lxqt-config-globalkeyshortcuts_tr.ts new file mode 100644 index 0000000..e25703d --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_tr.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + + + + Find a command + + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + + + + Description + + + + Shortcut + + + + Command + + + + Add New + + + + Remove + + + + Add Group + + + + Reset + + + + Close + Kapat + + + + ShortcutEditor + + None + Yok + + + Add Shortcut + + + + Remove + + + + New Group + + + + Reset Changes + + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_uk.desktop b/config/translations/lxqt-config-globalkeyshortcuts_uk.desktop new file mode 100644 index 0000000..57a58f5 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_uk.desktop @@ -0,0 +1,4 @@ +# Translations +Name[uk]=Налаштування скорочень LxQt +Comment[uk]=Налаштувати глобальні скорочення стільниці LxQt +GenericName[uk]=Налаштування глобальних скорочень LxQt diff --git a/config/translations/lxqt-config-globalkeyshortcuts_uk.ts b/config/translations/lxqt-config-globalkeyshortcuts_uk.ts new file mode 100644 index 0000000..37e77c5 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_uk.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + Знайти команду + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + Редактор скорочень LxQt + + + Description + Опис + + + Shortcut + Cкорочення + + + Command + Команда + + + Add New + Додати нове + + + Remove + Вилучити + + + Add Group + Додати групу + + + Reset + Cкинути + + + Close + Закрити + + + + ShortcutEditor + + None + Нічого + + + Add Shortcut + Додати скорочення + + + Remove + Вилучити + + + New Group + Нова група + + + Reset Changes + Скинути зміни + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_zh_CN.desktop b/config/translations/lxqt-config-globalkeyshortcuts_zh_CN.desktop new file mode 100644 index 0000000..83f94fd --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_zh_CN.desktop @@ -0,0 +1,4 @@ +# Translations +Name[zh_CN]=LxQt 快捷键配置 +Comment[zh_CN]=配置 LxQt 桌面全局快捷键 +GenericName[zh_CN]=LxQt 全局快捷键配置 diff --git a/config/translations/lxqt-config-globalkeyshortcuts_zh_CN.ts b/config/translations/lxqt-config-globalkeyshortcuts_zh_CN.ts new file mode 100644 index 0000000..76f7d6a --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_zh_CN.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + 查找命令 + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + LxQt 快捷键编辑器 + + + Description + 描述 + + + Shortcut + 快捷键 + + + Command + 命令 + + + Add New + 添加新 + + + Remove + 删除 + + + Add Group + 添加组 + + + Reset + 重置 + + + Close + 关闭 + + + + ShortcutEditor + + None + + + + Add Shortcut + 添加快捷键 + + + Remove + 删除 + + + New Group + 新建组 + + + Reset Changes + 重置更改 + + + \ No newline at end of file diff --git a/config/translations/lxqt-config-globalkeyshortcuts_zh_TW.desktop b/config/translations/lxqt-config-globalkeyshortcuts_zh_TW.desktop new file mode 100644 index 0000000..5a13245 --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_zh_TW.desktop @@ -0,0 +1,4 @@ +# Translations +Name[zh_TW]=LxQt快捷鍵設定 +Comment[zh_TW]=設定LxQt桌面的全域快捷鍵 +GenericName[zh_TW]=LxQt全域快捷鍵設定 diff --git a/config/translations/lxqt-config-globalkeyshortcuts_zh_TW.ts b/config/translations/lxqt-config-globalkeyshortcuts_zh_TW.ts new file mode 100644 index 0000000..ff7272c --- /dev/null +++ b/config/translations/lxqt-config-globalkeyshortcuts_zh_TW.ts @@ -0,0 +1,75 @@ + + + CommandFinder + + ... + ... + + + Find a command + 尋找指令 + + + + ShortcutConfigWindow + + LxQt Shortcut Editor + LxQt快捷鍵編輯器 + + + Description + 描述 + + + Shortcut + 快捷鍵 + + + Command + 指令 + + + Add New + 新增 + + + Remove + 移除 + + + Add Group + 新增群組 + + + Reset + 重設 + + + Close + 關閉 + + + + ShortcutEditor + + None + + + + Add Shortcut + 增加快捷鍵 + + + Remove + 移除 + + + New Group + 新群組 + + + Reset Changes + 重設更改 + + + \ No newline at end of file diff --git a/daemon/CMakeLists.txt b/daemon/CMakeLists.txt new file mode 100644 index 0000000..b7d3ea8 --- /dev/null +++ b/daemon/CMakeLists.txt @@ -0,0 +1,152 @@ +set(PROJECT_NAME lxqt-globalkeysd) +project(${PROJECT_NAME}) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +find_package(X11) +include_directories(${X11_INCLUDE_DIR}) + +set(QT_DBUS_PREFIX "org.qtproject") + +include_directories( + ${PROJECT_SOURCE_DIR} + ${CMAKE_CURRENT_BINARY_DIR} +) + +set(${PROJECT_NAME}_SOURCES + main.cpp + core.cpp + daemon_adaptor.cpp + native_adaptor.cpp + client_proxy.cpp + log_target.cpp + pipe_utils.cpp + string_utils.cpp + base_action.cpp + method_action.cpp + client_action.cpp + command_action.cpp + meta_types.cpp +) + +set(${PROJECT_NAME}_CPP_HEADERS + log_target.h + pipe_utils.h + string_utils.h + base_action.h + method_action.h + client_action.h + command_action.h + meta_types.h +) + +set(${PROJECT_NAME}_FORMS +) + +set(${PROJECT_NAME}_RESOURCES +) + +set(${PROJECT_NAME}_TRANSLATIONS +) + +set(${PROJECT_NAME}_DBUS_ADAPTORS + org.lxqt.global_key_shortcuts.daemon.xml + org.lxqt.global_key_shortcuts.native.xml +) + +set_source_files_properties(org.lxqt.global_key_shortcuts.daemon.xml PROPERTIES + INCLUDE daemon_adaptor.h + PARENT_CLASSNAME DaemonAdaptor + BASENAME org.lxqt.global_key_shortcuts.daemon + CLASSNAME OrgLxqtGlobalActionDaemonAdaptor +) + +set_source_files_properties(org.lxqt.global_key_shortcuts.native.xml PROPERTIES + INCLUDE native_adaptor.h + PARENT_CLASSNAME NativeAdaptor + BASENAME org.lxqt.global_key_shortcuts.native + CLASSNAME OrgLxqtGlobalActionNativeAdaptor +) + +set(${PROJECT_NAME}_DBUS_INTERFACES + org.lxqt.global_key_shortcuts.client.xml +) + +set_source_files_properties(org.lxqt.global_key_shortcuts.client.xml PROPERTIES + BASENAME org.lxqt.global_key_shortcuts.client +) + +set(${PROJECT_NAME}_HEADERS + ${${PROJECT_NAME}_CPP_HEADERS} +) + +set(${PROJECT_NAME}_TRANSLATABLE + ${${PROJECT_NAME}_SOURCES} + ${${PROJECT_NAME}_HEADERS} + ${${PROJECT_NAME}_FORMS} +) + +foreach(DBUS_ADAPTOR ${${PROJECT_NAME}_DBUS_ADAPTORS}) + get_filename_component(DBUS_ADAPTOR_FILENAME ${DBUS_ADAPTOR} NAME) + configure_file( + ${DBUS_ADAPTOR} + ${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME} + @ONLY + ) + get_source_file_property(DBUS_ADAPTOR_INCLUDE ${DBUS_ADAPTOR} INCLUDE) + get_source_file_property(DBUS_ADAPTOR_PARENT_CLASSNAME ${DBUS_ADAPTOR} PARENT_CLASSNAME) + get_source_file_property(DBUS_ADAPTOR_BASENAME ${DBUS_ADAPTOR} BASENAME) + get_source_file_property(DBUS_ADAPTOR_CLASSNAME ${DBUS_ADAPTOR} CLASSNAME) + if(DBUS_ADAPTOR_BASENAME) + if(DBUS_ADAPTOR_CLASSNAME) + qt5_add_dbus_adaptor(${PROJECT_NAME}_DBUS_ADAPTOR_FILES ${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME} ${DBUS_ADAPTOR_INCLUDE} ${DBUS_ADAPTOR_PARENT_CLASSNAME} ${DBUS_ADAPTOR_BASENAME} ${DBUS_ADAPTOR_CLASSNAME}) + else() + qt5_add_dbus_adaptor(${PROJECT_NAME}_DBUS_ADAPTOR_FILES ${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME} ${DBUS_ADAPTOR_INCLUDE} ${DBUS_ADAPTOR_PARENT_CLASSNAME} ${DBUS_ADAPTOR_BASENAME}) + endif() + else() + qt5_add_dbus_adaptor(${PROJECT_NAME}_DBUS_ADAPTOR_FILES ${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME} ${DBUS_ADAPTOR_INCLUDE} ${DBUS_ADAPTOR_PARENT_CLASSNAME}) + endif() +endforeach() + +foreach(DBUS_INTERFACE ${${PROJECT_NAME}_DBUS_INTERFACES}) + get_filename_component(DBUS_INTERFACE_FILENAME ${DBUS_INTERFACE} NAME) + configure_file( + ${DBUS_INTERFACE} + ${CMAKE_CURRENT_BINARY_DIR}/${DBUS_INTERFACE_FILENAME} + @ONLY + ) + get_source_file_property(DBUS_INTERFACE_BASENAME ${DBUS_INTERFACE} BASENAME) + get_source_file_property(DBUS_INTERFACE_INCLUDE ${DBUS_INTERFACE} INCLUDE) + get_source_file_property(DBUS_INTERFACE_CLASSNAME ${DBUS_INTERFACE} CLASSNAME) + get_source_file_property(DBUS_INTERFACE_NO_NAMESPACE ${DBUS_INTERFACE} NO_NAMESPACE) + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${DBUS_INTERFACE_FILENAME} PROPERTIES + INCLUDE ${DBUS_INTERFACE_INCLUDE} + CLASSNAME ${DBUS_INTERFACE_CLASSNAME} + NO_NAMESPACE ${DBUS_INTERFACE_NO_NAMESPACE} + ) + qt5_add_dbus_interface(${PROJECT_NAME}_DBUS_INTERFACE_FILES ${CMAKE_CURRENT_BINARY_DIR}/${DBUS_INTERFACE_FILENAME} ${DBUS_INTERFACE_BASENAME}) +endforeach() + +qt5_add_resources(${PROJECT_NAME}_RESOURCE_FILES ${${PROJECT_NAME}_RESOURCES}) +qt5_wrap_ui(${PROJECT_NAME}_FORM_FILES ${${PROJECT_NAME}_FORMS}) + +set(${PROJECT_NAME}_GENERATED_FILES + ${${PROJECT_NAME}_FORM_FILES} + ${${PROJECT_NAME}_RESOURCE_FILES} + ${${PROJECT_NAME}_QM_FILES} + ${${PROJECT_NAME}_DBUS_INTERFACE_FILES} + ${${PROJECT_NAME}_DBUS_ADAPTOR_FILES} +) + +set(${PROJECT_NAME}_ALL_FILES + ${${PROJECT_NAME}_SOURCES} + ${${PROJECT_NAME}_HEADERS} + ${${PROJECT_NAME}_GENERATED_FILES} +) + +add_executable(${PROJECT_NAME} ${${PROJECT_NAME}_ALL_FILES}) +target_link_libraries(${PROJECT_NAME} ${X11_LIBRARIES} Qt5::Widgets Qt5::DBus) + +install(TARGETS ${PROJECT_NAME} RUNTIME DESTINATION bin) diff --git a/daemon/base_action.cpp b/daemon/base_action.cpp new file mode 100644 index 0000000..4f0b181 --- /dev/null +++ b/daemon/base_action.cpp @@ -0,0 +1,41 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "base_action.h" +#include "log_target.h" + + +BaseAction::BaseAction(LogTarget *logTarget, const QString &description) + : mLogTarget(logTarget) + , mDescription(description) + , mEnabled(true) +{ +} + +BaseAction::~BaseAction() +{ +} diff --git a/daemon/base_action.h b/daemon/base_action.h new file mode 100644 index 0000000..f8a638f --- /dev/null +++ b/daemon/base_action.h @@ -0,0 +1,62 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_DAEMON__BASE_ACTION__INCLUDED +#define GLOBAL_ACTION_DAEMON__BASE_ACTION__INCLUDED + + +#include + +class LogTarget; + +class BaseAction +{ +public: + BaseAction(LogTarget *logTarget, const QString &description); + virtual ~BaseAction(); + + virtual const char *type() const = 0; + + virtual bool call() = 0; + + const QString &description() const { return mDescription; } + void setDescription(const QString &description) { mDescription = description; } + + void setEnabled(bool value = true) { mEnabled = value; } + void setDisabled(bool value = true) { mEnabled = !value; } + bool isEnabled() const { return mEnabled; } + +protected: + LogTarget *mLogTarget; + +private: + QString mDescription; + + bool mEnabled; +}; + +#endif // GLOBAL_ACTION_DAEMON__BASE_ACTION__INCLUDED diff --git a/daemon/client_action.cpp b/daemon/client_action.cpp new file mode 100644 index 0000000..0baf2bd --- /dev/null +++ b/daemon/client_action.cpp @@ -0,0 +1,94 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "client_action.h" +#include "client_proxy.h" +#include "log_target.h" + + +ClientAction::ClientAction(LogTarget *logTarget, const QDBusObjectPath &path, const QString &description) + : BaseAction(logTarget, description) + , mProxy(0) + , mPath(path) +{ +} + +ClientAction::ClientAction(LogTarget *logTarget, const QDBusConnection &connection, const QString &service, const QDBusObjectPath &path, const QString &description) + : BaseAction(logTarget, description) + , mProxy(0) + , mPath(path) +{ + appeared(connection, service); +} + +ClientAction::~ClientAction() +{ + delete mProxy; +} + +bool ClientAction::call() +{ + if (!isEnabled()) + { + return false; + } + + if (!mProxy) + { + mLogTarget->log(LOG_WARNING, "No native client: \"%s\"", qPrintable(mService)); + return false; + } + + mProxy->emitActivated(); + + return true; +} + +void ClientAction::appeared(const QDBusConnection &connection, const QString &service) +{ + if (mProxy) // should never happen + { + return; + } + mService = service; + mProxy = new ClientProxy(mService, QDBusObjectPath("/global_key_shortcuts" + mPath.path()), connection); +} + +void ClientAction::disappeared() +{ + mService.clear(); + delete mProxy; + mProxy = 0; +} + +void ClientAction::shortcutChanged(const QString &oldShortcut, const QString &newShortcut) +{ + if (mProxy) + { + mProxy->emitShortcutChanged(oldShortcut, newShortcut); + } +} diff --git a/daemon/client_action.h b/daemon/client_action.h new file mode 100644 index 0000000..2926517 --- /dev/null +++ b/daemon/client_action.h @@ -0,0 +1,71 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_DAEMON__DBUS_ACTION__INCLUDED +#define GLOBAL_ACTION_DAEMON__DBUS_ACTION__INCLUDED + + +#include "base_action.h" + +#include +#include +#include + + +class ClientProxy; + +class ClientAction : public BaseAction +{ +public: + ClientAction(LogTarget *logTarget, const QDBusObjectPath &path, const QString &description); + ClientAction(LogTarget *logTarget, const QDBusConnection &connection, const QString &service, const QDBusObjectPath &path, const QString &description); + ~ClientAction(); + + static const char *id() { return "client"; } + + virtual const char *type() const { return id(); } + + virtual bool call(); + + void shortcutChanged(const QString &oldShortcut, const QString &newShortcut); + + const QString &service() const { return mService; } + const QDBusObjectPath &path() const { return mPath; } + + void appeared(const QDBusConnection &connection, const QString &service); + void disappeared(); + + bool isPresent() const { return mProxy; } + +private: + ClientProxy *mProxy; + + QString mService; + QDBusObjectPath mPath; +}; + +#endif // GLOBAL_ACTION_DAEMON__DBUS_ACTION__INCLUDED diff --git a/daemon/client_proxy.cpp b/daemon/client_proxy.cpp new file mode 100644 index 0000000..513fcaf --- /dev/null +++ b/daemon/client_proxy.cpp @@ -0,0 +1,50 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "client_proxy.h" + + +#include "org.lxqt.global_key_shortcuts.client.h" + + +ClientProxy::ClientProxy(const QString &service, const QDBusObjectPath &path, const QDBusConnection &connection, QObject *parent) + : QObject(parent) +{ + org::lxqt::global_key_shortcuts::client *iface = new org::lxqt::global_key_shortcuts::client(service, path.path(), connection, this); + connect(this, SIGNAL(activated()), iface, SLOT(activated())); + connect(this, SIGNAL(shortcutChanged(QString, QString)), iface, SLOT(shortcutChanged(QString, QString))); +} + +void ClientProxy::emitActivated() +{ + emit activated(); +} + +void ClientProxy::emitShortcutChanged(const QString &oldShortcut, const QString &newShortcut) +{ + emit shortcutChanged(oldShortcut, newShortcut); +} diff --git a/daemon/client_proxy.h b/daemon/client_proxy.h new file mode 100644 index 0000000..a9c4648 --- /dev/null +++ b/daemon/client_proxy.h @@ -0,0 +1,57 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_DAEMON__CLIENT_PROXY__INCLUDED +#define GLOBAL_ACTION_DAEMON__CLIENT_PROXY__INCLUDED + + +#include +#include +#include + + +class ClientAction; + +class ClientProxy : public QObject +{ + Q_OBJECT + + friend class ClientAction; + +public: + ClientProxy(const QString &service, const QDBusObjectPath &path, const QDBusConnection &connection, QObject *parent = 0); + +signals: + void activated(); + void shortcutChanged(const QString &oldShortcut, const QString &newShortcut); + +protected: + void emitActivated(); + void emitShortcutChanged(const QString &oldShortcut, const QString &newShortcut); +}; + +#endif // GLOBAL_ACTION_DAEMON__CLIENT_PROXY__INCLUDED diff --git a/daemon/command_action.cpp b/daemon/command_action.cpp new file mode 100644 index 0000000..236f832 --- /dev/null +++ b/daemon/command_action.cpp @@ -0,0 +1,60 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "command_action.h" + +#include + +#include +#include + +#include "log_target.h" +#include "string_utils.h" + + +CommandAction::CommandAction(LogTarget *logTarget, const QString &command, const QStringList &args, const QString &description) + : BaseAction(logTarget, description) + , mCommand(command) + , mArgs(args) +{ +} + +bool CommandAction::call() +{ + if (!isEnabled()) + { + return false; + } + + bool result = QProcess::startDetached(mCommand, mArgs); + if (!result) + { + mLogTarget->log(LOG_WARNING, "Failed to launch command \"%s\"%s", qPrintable(mCommand), qPrintable(joinToString(mArgs, " \"", "\" \"", "\""))); + } + + return result; +} diff --git a/daemon/command_action.h b/daemon/command_action.h new file mode 100644 index 0000000..f816447 --- /dev/null +++ b/daemon/command_action.h @@ -0,0 +1,58 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_DAEMON__COMMAND_ACTION__INCLUDED +#define GLOBAL_ACTION_DAEMON__COMMAND_ACTION__INCLUDED + + +#include "base_action.h" + +#include +#include + + +class CommandAction : public BaseAction +{ +public: + CommandAction(LogTarget *logTarget, const QString &command, const QStringList &args, const QString &description); + + static const char *id() { return "command"; } + + virtual const char *type() const { return id(); } + + virtual bool call(); + + QString command() const { return mCommand; } + + QStringList args() const { return mArgs; } + +private: + QString mCommand; + QStringList mArgs; +}; + +#endif // GLOBAL_ACTION_DAEMON__COMMAND_ACTION__INCLUDED diff --git a/daemon/core.cpp b/daemon/core.cpp new file mode 100644 index 0000000..da7a0e9 --- /dev/null +++ b/daemon/core.cpp @@ -0,0 +1,3297 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "pipe_utils.h" +#include "string_utils.h" +#include "daemon_adaptor.h" +#include "native_adaptor.h" +#include "base_action.h" +#include "method_action.h" +#include "client_action.h" +#include "command_action.h" + +#include "core.h" + + +enum +{ + X11_OP_StringToKeycode, + X11_OP_KeycodeToString, + X11_OP_XGrabKey, + X11_OP_XUngrabKey, + X11_OP_XGrabKeyboard, + X11_OP_XUngrabKeyboard +}; + + +static Core *s_Core = 0; + + +void unixSignalHandler(int signalNumber) +{ + if (s_Core) + { + s_Core->unixSignalHandler(signalNumber); + } +} + +int x11ErrorHandler(Display *display, XErrorEvent *errorEvent) +{ + if (s_Core) + { + return s_Core->x11ErrorHandler(display, errorEvent); + } + return 0; +} + +const char *x11opcodeToString(unsigned char opcode) +{ + switch (opcode) + { + case X_CreateWindow: + return "CreateWindow"; + case X_ChangeWindowAttributes: + return "ChangeWindowAttributes"; + case X_GetWindowAttributes: + return "GetWindowAttributes"; + case X_DestroyWindow: + return "DestroyWindow"; + case X_DestroySubwindows: + return "DestroySubwindows"; + case X_ChangeSaveSet: + return "ChangeSaveSet"; + case X_ReparentWindow: + return "ReparentWindow"; + case X_MapWindow: + return "MapWindow"; + case X_MapSubwindows: + return "MapSubwindows"; + case X_UnmapWindow: + return "UnmapWindow"; + case X_UnmapSubwindows: + return "UnmapSubwindows"; + case X_ConfigureWindow: + return "ConfigureWindow"; + case X_CirculateWindow: + return "CirculateWindow"; + case X_GetGeometry: + return "GetGeometry"; + case X_QueryTree: + return "QueryTree"; + case X_InternAtom: + return "InternAtom"; + case X_GetAtomName: + return "GetAtomName"; + case X_ChangeProperty: + return "ChangeProperty"; + case X_DeleteProperty: + return "DeleteProperty"; + case X_GetProperty: + return "GetProperty"; + case X_ListProperties: + return "ListProperties"; + case X_SetSelectionOwner: + return "SetSelectionOwner"; + case X_GetSelectionOwner: + return "GetSelectionOwner"; + case X_ConvertSelection: + return "ConvertSelection"; + case X_SendEvent: + return "SendEvent"; + case X_GrabPointer: + return "GrabPointer"; + case X_UngrabPointer: + return "UngrabPointer"; + case X_GrabButton: + return "GrabButton"; + case X_UngrabButton: + return "UngrabButton"; + case X_ChangeActivePointerGrab: + return "ChangeActivePointerGrab"; + case X_GrabKeyboard: + return "GrabKeyboard"; + case X_UngrabKeyboard: + return "UngrabKeyboard"; + case X_GrabKey: + return "GrabKey"; + case X_UngrabKey: + return "UngrabKey"; + case X_AllowEvents: + return "AllowEvents"; + case X_GrabServer: + return "GrabServer"; + case X_UngrabServer: + return "UngrabServer"; + case X_QueryPointer: + return "QueryPointer"; + case X_GetMotionEvents: + return "GetMotionEvents"; + case X_TranslateCoords: + return "TranslateCoords"; + case X_WarpPointer: + return "WarpPointer"; + case X_SetInputFocus: + return "SetInputFocus"; + case X_GetInputFocus: + return "GetInputFocus"; + case X_QueryKeymap: + return "QueryKeymap"; + case X_OpenFont: + return "OpenFont"; + case X_CloseFont: + return "CloseFont"; + case X_QueryFont: + return "QueryFont"; + case X_QueryTextExtents: + return "QueryTextExtents"; + case X_ListFonts: + return "ListFonts"; + case X_ListFontsWithInfo: + return "ListFontsWithInfo"; + case X_SetFontPath: + return "SetFontPath"; + case X_GetFontPath: + return "GetFontPath"; + case X_CreatePixmap: + return "CreatePixmap"; + case X_FreePixmap: + return "FreePixmap"; + case X_CreateGC: + return "CreateGC"; + case X_ChangeGC: + return "ChangeGC"; + case X_CopyGC: + return "CopyGC"; + case X_SetDashes: + return "SetDashes"; + case X_SetClipRectangles: + return "SetClipRectangles"; + case X_FreeGC: + return "FreeGC"; + case X_ClearArea: + return "ClearArea"; + case X_CopyArea: + return "CopyArea"; + case X_CopyPlane: + return "CopyPlane"; + case X_PolyPoint: + return "PolyPoint"; + case X_PolyLine: + return "PolyLine"; + case X_PolySegment: + return "PolySegment"; + case X_PolyRectangle: + return "PolyRectangle"; + case X_PolyArc: + return "PolyArc"; + case X_FillPoly: + return "FillPoly"; + case X_PolyFillRectangle: + return "PolyFillRectangle"; + case X_PolyFillArc: + return "PolyFillArc"; + case X_PutImage: + return "PutImage"; + case X_GetImage: + return "GetImage"; + case X_PolyText8: + return "PolyText8"; + case X_PolyText16: + return "PolyText16"; + case X_ImageText8: + return "ImageText8"; + case X_ImageText16: + return "ImageText16"; + case X_CreateColormap: + return "CreateColormap"; + case X_FreeColormap: + return "FreeColormap"; + case X_CopyColormapAndFree: + return "CopyColormapAndFree"; + case X_InstallColormap: + return "InstallColormap"; + case X_UninstallColormap: + return "UninstallColormap"; + case X_ListInstalledColormaps: + return "ListInstalledColormaps"; + case X_AllocColor: + return "AllocColor"; + case X_AllocNamedColor: + return "AllocNamedColor"; + case X_AllocColorCells: + return "AllocColorCells"; + case X_AllocColorPlanes: + return "AllocColorPlanes"; + case X_FreeColors: + return "FreeColors"; + case X_StoreColors: + return "StoreColors"; + case X_StoreNamedColor: + return "StoreNamedColor"; + case X_QueryColors: + return "QueryColors"; + case X_LookupColor: + return "LookupColor"; + case X_CreateCursor: + return "CreateCursor"; + case X_CreateGlyphCursor: + return "CreateGlyphCursor"; + case X_FreeCursor: + return "FreeCursor"; + case X_RecolorCursor: + return "RecolorCursor"; + case X_QueryBestSize: + return "QueryBestSize"; + case X_QueryExtension: + return "QueryExtension"; + case X_ListExtensions: + return "ListExtensions"; + case X_ChangeKeyboardMapping: + return "ChangeKeyboardMapping"; + case X_GetKeyboardMapping: + return "GetKeyboardMapping"; + case X_ChangeKeyboardControl: + return "ChangeKeyboardControl"; + case X_GetKeyboardControl: + return "GetKeyboardControl"; + case X_Bell: + return "Bell"; + case X_ChangePointerControl: + return "ChangePointerControl"; + case X_GetPointerControl: + return "GetPointerControl"; + case X_SetScreenSaver: + return "SetScreenSaver"; + case X_GetScreenSaver: + return "GetScreenSaver"; + case X_ChangeHosts: + return "ChangeHosts"; + case X_ListHosts: + return "ListHosts"; + case X_SetAccessControl: + return "SetAccessControl"; + case X_SetCloseDownMode: + return "SetCloseDownMode"; + case X_KillClient: + return "KillClient"; + case X_RotateProperties: + return "RotateProperties"; + case X_ForceScreenSaver: + return "ForceScreenSaver"; + case X_SetPointerMapping: + return "SetPointerMapping"; + case X_GetPointerMapping: + return "GetPointerMapping"; + case X_SetModifierMapping: + return "SetModifierMapping"; + case X_GetModifierMapping: + return "GetModifierMapping"; + case X_NoOperation: + return "NoOperation"; + } + return ""; +} + +static const char *strLevel(int level) +{ + switch (level) + { + case LOG_EMERG: + return "Emergency"; + + case LOG_ALERT: + return "Alert"; + + case LOG_CRIT: + return "Critical"; + + case LOG_ERR: + return "Error"; + + case LOG_WARNING: + return "Warning"; + + case LOG_NOTICE: + return "Notice"; + + case LOG_INFO: + return "Info"; + + case LOG_DEBUG: + return "Debug"; + + default: + return ""; + } +} + + +Core::Core(bool useSyslog, bool minLogLevelSet, int minLogLevel, const QStringList &configFiles, bool multipleActionsBehaviourSet, MultipleActionsBehaviour multipleActionsBehaviour, QObject *parent) + : QThread(parent) + , LogTarget() + , mReady(false) + , mUseSyslog(useSyslog) + , mMinLogLevel(minLogLevel) + , mDisplay(0) + , mInterClientCommunicationWindow(0) + , mDaemonAdaptor(0) + , mNativeAdaptor(0) + , mLastId(0ull) + , mGrabbingShortcut(false) + , AltMask(Mod1Mask) + , MetaMask(Mod4Mask) + , Level3Mask(Mod5Mask) + , Level5Mask(Mod3Mask) + , mMultipleActionsBehaviour(multipleActionsBehaviour) + , mAllowGrabLocks(false) + , mAllowGrabBaseSpecial(false) + , mAllowGrabMiscSpecial(true) + , mAllowGrabBaseKeypad(true) + , mAllowGrabMiscKeypad(true) + , mAllowGrabPrintable(false) + + , mSaveAllowed(false) + + , mShortcutGrabTimeout(new QTimer(this)) + , mShortcutGrabRequested(false) +{ + s_Core = this; + + initBothPipeEnds(mX11ErrorPipe); + initBothPipeEnds(mX11RequestPipe); + initBothPipeEnds(mX11ResponsePipe); + + mConfigFile = QString(getenv("HOME")) + "/.config/global_key_shortcutss.ini"; + + try + { + error_t c_error; + + openlog("lxqt-global-action-daemon", LOG_PID, LOG_USER); + + ::signal(SIGTERM, ::unixSignalHandler); + ::signal(SIGINT, ::unixSignalHandler); + + + if (!QDBusConnection::sessionBus().registerService("org.lxqt.global_key_shortcuts")) + { + throw std::runtime_error(std::string("Cannot register service 'org.lxqt.global_key_shortcuts'")); + } + + + if ((c_error = createPipe(mX11ErrorPipe))) + { + throw std::runtime_error(std::string("Cannot create error signal pipe: ") + std::string(strerror(c_error))); + } + + if ((c_error = createPipe(mX11RequestPipe))) + { + throw std::runtime_error(std::string("Cannot create X11 request pipe: ") + std::string(strerror(c_error))); + } + + if ((c_error = createPipe(mX11ResponsePipe))) + { + throw std::runtime_error(std::string("Cannot create X11 response pipe: ") + std::string(strerror(c_error))); + } + + + start(); + + + char signal; + error_t error = readAll(mX11ResponsePipe[STDIN_FILENO], &signal, sizeof(signal)); + if (error > 0) + { + throw std::runtime_error(std::string("Cannot read X11 start signal: ") + std::string(strerror(c_error))); + } + if (error < 0) + { + throw std::runtime_error(std::string("Cannot read X11 start signal")); + } + if (signal) + { + throw std::runtime_error(std::string("Cannot start X11 thread")); + } + + + { + size_t fm = configFiles.size(); + for (size_t fi = 0; fi < fm; ++fi) + { + mConfigFile = configFiles[fi]; + + QSettings settings(mConfigFile, QSettings::IniFormat, this); + + QString iniValue; + + if (!minLogLevelSet) + { + iniValue = settings.value(/* General/ */"LogLevel").toString(); + if (!iniValue.isEmpty()) + { + if (iniValue == "error") + { + mMinLogLevel = LOG_ERR; + } + else if (iniValue == "warning") + { + mMinLogLevel = LOG_WARNING; + } + else if (iniValue == "notice") + { + mMinLogLevel = LOG_NOTICE; + } + else if (iniValue == "info") + { + mMinLogLevel = LOG_INFO; + } + else if (iniValue == "debug") + { + mMinLogLevel = LOG_DEBUG; + } + } + } + + if (!multipleActionsBehaviourSet) + { + iniValue = settings.value(/* General/ */"MultipleActionsBehaviour").toString(); + if (!iniValue.isEmpty()) + { + if (iniValue == "first") + { + mMultipleActionsBehaviour = MULTIPLE_ACTIONS_BEHAVIOUR_FIRST; + } + else if (iniValue == "last") + { + mMultipleActionsBehaviour = MULTIPLE_ACTIONS_BEHAVIOUR_LAST; + } + else if (iniValue == "all") + { + mMultipleActionsBehaviour = MULTIPLE_ACTIONS_BEHAVIOUR_ALL; + } + else if (iniValue == "none") + { + mMultipleActionsBehaviour = MULTIPLE_ACTIONS_BEHAVIOUR_NONE; + } + } + } + + mAllowGrabLocks = settings.value(/* General/ */"AllowGrabLocks", mAllowGrabLocks).toBool(); + mAllowGrabBaseSpecial = settings.value(/* General/ */"AllowGrabBaseSpecial", mAllowGrabBaseSpecial).toBool(); + mAllowGrabMiscSpecial = settings.value(/* General/ */"AllowGrabMiscSpecial", mAllowGrabMiscSpecial).toBool(); + mAllowGrabBaseKeypad = settings.value(/* General/ */"AllowGrabBaseKeypad", mAllowGrabBaseKeypad).toBool(); + mAllowGrabMiscKeypad = settings.value(/* General/ */"AllowGrabMiscKeypad", mAllowGrabMiscKeypad).toBool(); + + foreach(QString section, settings.childGroups()) + { + if (section != "General") + { + settings.beginGroup(section); + + QString shortcut = section; + int pos = shortcut.indexOf('.'); + if (pos != -1) + { + shortcut = shortcut.left(pos); + } + + qulonglong id = 0ull; + + bool enabled = settings.value("Enabled", true).toBool(); + + QString description = settings.value("Comment").toString(); + + if (settings.contains("Exec")) + { + QStringList values = settings.value("Exec").toStringList(); + id = registerCommandAction(shortcut, values[0], values.mid(1), description); + } + else + { + iniValue = settings.value("path").toString(); + if (!iniValue.isEmpty()) + { + QString path = iniValue; + + if (settings.contains("interface")) + { + QString interface = settings.value("interface").toString(); + + iniValue = settings.value("service").toString(); + if (!iniValue.isEmpty()) + { + QString service = iniValue; + + iniValue = settings.value("method").toString(); + if (!iniValue.isEmpty()) + { + QString method = iniValue; + + id = registerMethodAction(shortcut, service, QDBusObjectPath(path), interface, method, description); + } + } + } + else + { + id = registerClientAction(shortcut, QDBusObjectPath(path), description); + } + } + } + if (id) + { + mShortcutAndActionById[id].second->setEnabled(enabled); + } + + settings.endGroup(); + } + } + } + } + log(LOG_DEBUG, "Config file: %s", qPrintable(mConfigFile)); + + + log(LOG_DEBUG, "MinLogLevel: %s", strLevel(mMinLogLevel)); + switch (mMultipleActionsBehaviour) + { + case MULTIPLE_ACTIONS_BEHAVIOUR_FIRST: + log(LOG_DEBUG, "MultipleActionsBehaviour: first"); + break; + + case MULTIPLE_ACTIONS_BEHAVIOUR_LAST: + log(LOG_DEBUG, "MultipleActionsBehaviour: last"); + break; + + case MULTIPLE_ACTIONS_BEHAVIOUR_ALL: + log(LOG_DEBUG, "MultipleActionsBehaviour: all"); + break; + + case MULTIPLE_ACTIONS_BEHAVIOUR_NONE: + log(LOG_DEBUG, "MultipleActionsBehaviour: none"); + break; + + default: + ; + } + log(LOG_DEBUG, "AllowGrabLocks: %s", mAllowGrabLocks ? "true" : "false"); + log(LOG_DEBUG, "AllowGrabBaseSpecial: %s", mAllowGrabBaseSpecial ? "true" : "false"); + log(LOG_DEBUG, "AllowGrabMiscSpecial: %s", mAllowGrabMiscSpecial ? "true" : "false"); + log(LOG_DEBUG, "AllowGrabBaseKeypad: %s", mAllowGrabBaseKeypad ? "true" : "false"); + log(LOG_DEBUG, "AllowGrabMiscKeypad: %s", mAllowGrabMiscKeypad ? "true" : "false"); + + mSaveAllowed = true; + saveConfig(); + + + + mDaemonAdaptor = new DaemonAdaptor(this); + if (!QDBusConnection::sessionBus().registerObject("/daemon", mDaemonAdaptor)) + { + throw std::runtime_error(std::string("Cannot create daemon adaptor")); + } + + mNativeAdaptor = new NativeAdaptor(this); + if (!QDBusConnection::sessionBus().registerObject("/native", mNativeAdaptor)) + { + throw std::runtime_error(std::string("Cannot create daemon native client adaptor")); + } + + connect(QDBusConnection::sessionBus().interface(), SIGNAL(serviceOwnerChanged(QString, QString, QString)), this, SLOT(serviceOwnerChanged(QString, QString, QString))); + + connect(mDaemonAdaptor, SIGNAL(onAddMethodAction(QPair&, QString, QString, QDBusObjectPath, QString, QString, QString)), this, SLOT(addMethodAction(QPair&, QString, QString, QDBusObjectPath, QString, QString, QString))); + connect(mDaemonAdaptor, SIGNAL(onAddCommandAction(QPair&, QString, QString, QStringList, QString)), this, SLOT(addCommandAction(QPair&, QString, QString, QStringList, QString))); + connect(mDaemonAdaptor, SIGNAL(onModifyActionDescription(bool &, qulonglong, QString)), this, SLOT(modifyActionDescription(bool &, qulonglong, QString))); + connect(mDaemonAdaptor, SIGNAL(onModifyMethodAction(bool &, qulonglong, QString, QDBusObjectPath, QString, QString, QString)), this, SLOT(modifyMethodAction(bool &, qulonglong, QString, QDBusObjectPath, QString, QString, QString))); + connect(mDaemonAdaptor, SIGNAL(onModifyCommandAction(bool &, qulonglong, QString, QStringList, QString)), this, SLOT(modifyCommandAction(bool &, qulonglong, QString, QStringList, QString))); + connect(mDaemonAdaptor, SIGNAL(onEnableAction(bool &, qulonglong, bool)), this, SLOT(enableAction(bool &, qulonglong, bool))); + connect(mDaemonAdaptor, SIGNAL(onIsActionEnabled(bool &, qulonglong)), this, SLOT(isActionEnabled(bool &, qulonglong))); + connect(mDaemonAdaptor, SIGNAL(onGetClientActionSender(QString &, qulonglong)), this, SLOT(getClientActionSender(QString &, qulonglong))); + connect(mDaemonAdaptor, SIGNAL(onChangeShortcut(QString &, qulonglong, QString)), this, SLOT(changeShortcut(QString &, qulonglong, QString))); + connect(mDaemonAdaptor, SIGNAL(onSwapActions(bool &, qulonglong, qulonglong)), this, SLOT(swapActions(bool &, qulonglong, qulonglong))); + connect(mDaemonAdaptor, SIGNAL(onRemoveAction(bool &, qulonglong)), this, SLOT(removeAction(bool &, qulonglong))); + connect(mDaemonAdaptor, SIGNAL(onSetMultipleActionsBehaviour(MultipleActionsBehaviour)), this, SLOT(setMultipleActionsBehaviour(MultipleActionsBehaviour))); + connect(mDaemonAdaptor, SIGNAL(onGetMultipleActionsBehaviour(MultipleActionsBehaviour &)), this, SLOT(getMultipleActionsBehaviour(MultipleActionsBehaviour &))); + connect(mDaemonAdaptor, SIGNAL(onGetAllActionIds(QList&)), this, SLOT(getAllActionIds(QList&))); + connect(mDaemonAdaptor, SIGNAL(onGetActionById(QPair&, qulonglong)), this, SLOT(getActionById(QPair&, qulonglong))); + connect(mDaemonAdaptor, SIGNAL(onGetAllActions(QMap&)), this, SLOT(getAllActions(QMap&))); + connect(mDaemonAdaptor, SIGNAL(onGetClientActionInfoById(QPair&, qulonglong)), this, SLOT(getClientActionInfoById(QPair&, qulonglong))); + connect(mDaemonAdaptor, SIGNAL(onGetMethodActionInfoById(QPair&, qulonglong)), this, SLOT(getMethodActionInfoById(QPair&, qulonglong))); + connect(mDaemonAdaptor, SIGNAL(onGetCommandActionInfoById(QPair&, qulonglong)), this, SLOT(getCommandActionInfoById(QPair&, qulonglong))); + connect(mDaemonAdaptor, SIGNAL(onGrabShortcut(uint, QString &, bool &, bool &, bool &, QDBusMessage)), this, SLOT(grabShortcut(uint, QString &, bool &, bool &, bool &, QDBusMessage))); + connect(mDaemonAdaptor, SIGNAL(onCancelShortcutGrab()), this, SLOT(cancelShortcutGrab())); + connect(mDaemonAdaptor, SIGNAL(onQuit()), qApp, SLOT(quit())); + + connect(mNativeAdaptor, SIGNAL(onAddClientAction(QPair&, QString, QDBusObjectPath, QString, QString)), this, SLOT(addClientAction(QPair&, QString, QDBusObjectPath, QString, QString))); + connect(mNativeAdaptor, SIGNAL(onModifyClientAction(qulonglong &, QDBusObjectPath, QString, QString)), this, SLOT(modifyClientAction(qulonglong &, QDBusObjectPath, QString, QString))); + connect(mNativeAdaptor, SIGNAL(onEnableClientAction(bool &, QDBusObjectPath, bool, QString)), this, SLOT(enableClientAction(bool &, QDBusObjectPath, bool, QString))); + connect(mNativeAdaptor, SIGNAL(onIsClientActionEnabled(bool &, QDBusObjectPath, QString)), this, SLOT(isClientActionEnabled(bool &, QDBusObjectPath, QString))); + connect(mNativeAdaptor, SIGNAL(onChangeClientActionShortcut(QPair&, QDBusObjectPath, QString, QString)), this, SLOT(changeClientActionShortcut(QPair&, QDBusObjectPath, QString, QString))); + connect(mNativeAdaptor, SIGNAL(onRemoveClientAction(bool &, QDBusObjectPath, QString)), this, SLOT(removeClientAction(bool &, QDBusObjectPath, QString))); + connect(mNativeAdaptor, SIGNAL(onDeactivateClientAction(bool &, QDBusObjectPath, QString)), this, SLOT(deactivateClientAction(bool &, QDBusObjectPath, QString))); + connect(mNativeAdaptor, SIGNAL(onGrabShortcut(uint, QString &, bool &, bool &, bool &, QDBusMessage)), this, SLOT(grabShortcut(uint, QString &, bool &, bool &, bool &, QDBusMessage))); + connect(mNativeAdaptor, SIGNAL(onCancelShortcutGrab()), this, SLOT(cancelShortcutGrab())); + + mShortcutGrabTimeout->setSingleShot(true); + + connect(this, SIGNAL(onShortcutGrabbed()), this, SLOT(shortcutGrabbed()), Qt::QueuedConnection); + connect(mShortcutGrabTimeout, SIGNAL(timeout()), this, SLOT(shortcutGrabTimedout())); + + + log(LOG_NOTICE, "Started"); + + mReady = true; + } + catch (const std::exception &err) + { + log(LOG_CRIT, "%s", err.what()); + } +} + +Core::~Core() +{ + log(LOG_INFO, "Stopping"); + + closeBothPipeEnds(mX11ErrorPipe); + closeBothPipeEnds(mX11RequestPipe); + closeBothPipeEnds(mX11ResponsePipe); + + mX11EventLoopActive = false; + wakeX11Thread(); + wait(); + + delete mDaemonAdaptor; + + ShortcutAndActionById::iterator lastShortcutAndActionById = mShortcutAndActionById.end(); + for (ShortcutAndActionById::iterator shortcutAndActionById = mShortcutAndActionById.begin(); shortcutAndActionById != lastShortcutAndActionById; ++shortcutAndActionById) + { + delete shortcutAndActionById.value().second; + } + + log(LOG_NOTICE, "Stopped"); + + closelog(); +} + +void Core::saveConfig() +{ + if (!mSaveAllowed) + { + return; + } + + QSettings settings(mConfigFile, QSettings::IniFormat); + + settings.clear(); + + switch (mMultipleActionsBehaviour) + { + case MULTIPLE_ACTIONS_BEHAVIOUR_FIRST: + settings.setValue(/* General/ */"MultipleActionsBehaviour", "first"); + break; + + case MULTIPLE_ACTIONS_BEHAVIOUR_LAST: + settings.setValue(/* General/ */"MultipleActionsBehaviour", "last"); + break; + + case MULTIPLE_ACTIONS_BEHAVIOUR_ALL: + settings.setValue(/* General/ */"MultipleActionsBehaviour", "all"); + break; + + case MULTIPLE_ACTIONS_BEHAVIOUR_NONE: + settings.setValue(/* General/ */"MultipleActionsBehaviour", "none"); + break; + + default: + ; + } + + settings.setValue(/* General/ */"AllowGrabLocks", mAllowGrabLocks); + settings.setValue(/* General/ */"AllowGrabBaseSpecial", mAllowGrabBaseSpecial); + settings.setValue(/* General/ */"AllowGrabMiscSpecial", mAllowGrabMiscSpecial); + settings.setValue(/* General/ */"AllowGrabBaseKeypad", mAllowGrabBaseKeypad); + settings.setValue(/* General/ */"AllowGrabMiscKeypad", mAllowGrabMiscKeypad); + + ShortcutAndActionById::const_iterator lastShortcutAndActionById = mShortcutAndActionById.end(); + for (ShortcutAndActionById::const_iterator shortcutAndActionById = mShortcutAndActionById.begin(); shortcutAndActionById != lastShortcutAndActionById; ++shortcutAndActionById) + { + const BaseAction *action = shortcutAndActionById.value().second; + QString section = shortcutAndActionById.value().first + "." + QString::number(shortcutAndActionById.key()); + + settings.beginGroup(section); + + settings.setValue("Enabled", action->isEnabled()); + settings.setValue("Comment", action->description()); + + if (!strcmp(action->type(), CommandAction::id())) + { + const CommandAction *commandAction = dynamic_cast(action); + settings.setValue("Exec", QVariant(QStringList() << commandAction->command() += commandAction->args())); + } + else if (!strcmp(action->type(), MethodAction::id())) + { + const MethodAction *methodAction = dynamic_cast(action); + settings.setValue("service", methodAction->service()); + settings.setValue("path", methodAction->path().path()); + settings.setValue("interface", methodAction->interface()); + settings.setValue("method", methodAction->method()); + } + else if (!strcmp(action->type(), ClientAction::id())) + { + const ClientAction *clientAction = dynamic_cast(action); + settings.setValue("path", clientAction->path().path()); + } + + settings.endGroup(); + } +} + +void Core::unixSignalHandler(int signalNumber) +{ + log(LOG_INFO, "Signal #%d received", signalNumber); + qApp->quit(); +} + +void Core::log(int level, const char *format, ...) const +{ + if (level > mMinLogLevel) + { + return; + } + + va_list ap; + va_start(ap, format); + if (mUseSyslog) + { + vsyslog(LOG_MAKEPRI(LOG_USER, level), format, ap); + } + else + { + fprintf(stderr, "[%s] ", strLevel(level)); + vfprintf(stderr, format, ap); + fprintf(stderr, "\n"); + } + va_end(ap); +} + +int Core::x11ErrorHandler(Display */*display*/, XErrorEvent *errorEvent) +{ + if (error_t error = writeAll(mX11ErrorPipe[STDOUT_FILENO], errorEvent, sizeof(XErrorEvent))) + { + log(LOG_CRIT, "Cannot write to error signal pipe: %s", strerror(error)); + qApp->quit(); + return 0; + } + + return 0; +} + +bool Core::waitForX11Error(int level, uint timeout) +{ + pollfd *fds = new pollfd[1]; + fds[0].fd = mX11ErrorPipe[STDIN_FILENO]; + fds[0].events = POLLIN | POLLERR | POLLHUP; + if (poll(fds, 1, timeout) < 0) + { + return true; + } + + bool result = false; + + while (fds[0].revents & POLLIN) + { + XErrorEvent errorEvent; + if (error_t error = readAll(mX11ErrorPipe[STDIN_FILENO], &errorEvent, sizeof(errorEvent))) + { + log(LOG_CRIT, "Cannot read from error signal pipe: %s", strerror(error)); + qApp->quit(); + } + + char errorString[1024]; + XGetErrorText(errorEvent.display, errorEvent.error_code, errorString, 1023); + log(level, "X11 error: type: %d, serial: %lu, error_code: %d '%s', request_code: %d (%s), minor_code: %d, resourceid: %lu", errorEvent.type, errorEvent.serial, errorEvent.error_code, errorString, errorEvent.request_code, x11opcodeToString(errorEvent.request_code), errorEvent.minor_code, errorEvent.resourceid); + + result = true; + + if (poll(fds, 1, 0) < 0) + { + return true; + } + } + + return result; +} + +void Core::lockX11Error() +{ + mX11ErrorMutex.lock(); + waitForX11Error(false, 0); +} + +bool Core::checkX11Error(int level, uint timeout) +{ +// unsigned long serial = NextRequest(mDisplay); +// log(LOG_DEBUG, "X11 error: serial: %lu", serial); + + bool result = waitForX11Error(level, timeout); + mX11ErrorMutex.unlock(); + return result; +} + +void Core::wakeX11Thread() +{ + if (mInterClientCommunicationWindow) + { + XClientMessageEvent dummyEvent; + memset(&dummyEvent, 0, sizeof(dummyEvent)); + dummyEvent.type = ClientMessage; + dummyEvent.window = mInterClientCommunicationWindow; + dummyEvent.format = 32; + + lockX11Error(); + XSendEvent(mDisplay, mInterClientCommunicationWindow, 0, 0, reinterpret_cast(&dummyEvent)); + checkX11Error(); + XFlush(mDisplay); + } +} + +bool Core::isEscape(KeySym keySym, unsigned int modifiers) +{ + return ((keySym == XK_Escape) && (!modifiers)); +} + +bool Core::isModifier(KeySym keySym) +{ + switch (keySym) + { + case XK_Shift_L: + case XK_Shift_R: + case XK_Control_L: + case XK_Control_R: + case XK_Meta_L: + case XK_Meta_R: + case XK_Alt_L: + case XK_Alt_R: + case XK_Super_L: + case XK_Super_R: + case XK_Hyper_L: + case XK_Hyper_R: + case XK_ISO_Level3_Shift: + case XK_ISO_Level5_Shift: + case XK_ISO_Group_Shift: + return true; + + } + return false; +} + +bool Core::isAllowed(KeySym keySym, unsigned int modifiers) +{ + switch (keySym) + { + case XK_Scroll_Lock: + case XK_Num_Lock: + case XK_Caps_Lock: + case XK_ISO_Lock: + case XK_ISO_Level3_Lock: + case XK_ISO_Level5_Lock: + case XK_ISO_Group_Lock: + case XK_ISO_Next_Group_Lock: + case XK_ISO_Prev_Group_Lock: + case XK_ISO_First_Group_Lock: + case XK_ISO_Last_Group_Lock: + if (!modifiers) + { + return mAllowGrabLocks; + } + break; + + case XK_Home: + case XK_Left: + case XK_Up: + case XK_Right: + case XK_Down: + case XK_Page_Up: + case XK_Page_Down: + case XK_End: + case XK_Delete: + case XK_Insert: + case XK_BackSpace: + case XK_Tab: + case XK_Return: + case XK_space: + if (!modifiers) + { + return mAllowGrabBaseSpecial; + } + break; + + case XK_Pause: + case XK_Print: + case XK_Linefeed: + case XK_Clear: + case XK_Multi_key: + case XK_Codeinput: + case XK_SingleCandidate: + case XK_MultipleCandidate: + case XK_PreviousCandidate: + case XK_Begin: + case XK_Select: + case XK_Execute: + case XK_Undo: + case XK_Redo: + case XK_Menu: + case XK_Find: + case XK_Cancel: + case XK_Help: + case XK_Sys_Req: + case XK_Break: + if (!modifiers) + { + return mAllowGrabMiscSpecial; + } + break; + + case XK_KP_Enter: + case XK_KP_Home: + case XK_KP_Left: + case XK_KP_Up: + case XK_KP_Right: + case XK_KP_Down: + case XK_KP_Page_Up: + case XK_KP_Page_Down: + case XK_KP_End: + case XK_KP_Begin: + case XK_KP_Insert: + case XK_KP_Delete: + case XK_KP_Multiply: + case XK_KP_Add: + case XK_KP_Subtract: + case XK_KP_Decimal: + case XK_KP_Divide: + case XK_KP_0: + case XK_KP_1: + case XK_KP_2: + case XK_KP_3: + case XK_KP_4: + case XK_KP_5: + case XK_KP_6: + case XK_KP_7: + case XK_KP_8: + case XK_KP_9: + if (!modifiers) + { + return mAllowGrabBaseKeypad; + } + break; + + case XK_KP_Space: + case XK_KP_Tab: + case XK_KP_F1: + case XK_KP_F2: + case XK_KP_F3: + case XK_KP_F4: + case XK_KP_Equal: + case XK_KP_Separator: + if (!modifiers) + { + return mAllowGrabMiscKeypad; + } + break; + + case XK_grave: + case XK_1: + case XK_2: + case XK_3: + case XK_4: + case XK_5: + case XK_6: + case XK_7: + case XK_8: + case XK_9: + case XK_0: + case XK_minus: + case XK_equal: + case XK_Q: + case XK_W: + case XK_E: + case XK_R: + case XK_T: + case XK_Y: + case XK_U: + case XK_I: + case XK_O: + case XK_P: + case XK_bracketleft: + case XK_bracketright: + case XK_backslash: + case XK_A: + case XK_S: + case XK_D: + case XK_F: + case XK_G: + case XK_H: + case XK_J: + case XK_K: + case XK_L: + case XK_semicolon: + case XK_apostrophe: + case XK_Z: + case XK_X: + case XK_C: + case XK_V: + case XK_B: + case XK_N: + case XK_M: + case XK_comma: + case XK_period: + case XK_slash: + if (!(modifiers & ~(ShiftMask | Level3Mask | Level5Mask))) + { + return mAllowGrabPrintable; + } + break; + + } + return true; +} + +void Core::run() +{ + mX11EventLoopActive = true; + + XInitThreads(); + + int (*oldx11ErrorHandler)(Display * display, XErrorEvent * errorEvent) = XSetErrorHandler(::x11ErrorHandler); + + mDisplay = XOpenDisplay(NULL); + XSynchronize(mDisplay, True); + + lockX11Error(); + + Window rootWindow = DefaultRootWindow(mDisplay); + + XSelectInput(mDisplay, rootWindow, KeyPressMask); + + mInterClientCommunicationWindow = XCreateSimpleWindow(mDisplay, rootWindow, 0, 0, 1, 1, 0, 0, 0); + + XSelectInput(mDisplay, mInterClientCommunicationWindow, StructureNotifyMask); + + if (checkX11Error()) + { + return; + } + + QSet allModifiers; + unsigned int allShifts = ShiftMask | ControlMask | AltMask | MetaMask | Level3Mask | Level5Mask; + unsigned int ignoreMask = 0xff ^ allShifts; + for (unsigned int i = 0; i < 0x100; ++i) + { + unsigned int ignoreLocks = i & ignoreMask; + allModifiers.insert(ignoreLocks); + } + + + char signal = 0; + if (write(mX11ResponsePipe[STDOUT_FILENO], &signal, sizeof(signal)) == sizeof(signal)) + { + XEvent event; + while (mX11EventLoopActive) + { + XNextEvent(mDisplay, &event); + if (!mX11EventLoopActive) + { + break; + } + + switch (event.type) + { + case KeyPress: + { + QMutexLocker lock(&mDataMutex); + + if (mGrabbingShortcut) + { +// log(LOG_DEBUG, "KeyPress %08x %08x", event.xkey.state, event.xkey.keycode); + + bool ignoreKey = false; + bool cancel = false; + QString shortcut; + + int keysymsPerKeycode; + lockX11Error(); + KeySym *keySyms = XGetKeyboardMapping(mDisplay, event.xkey.keycode, 1, &keysymsPerKeycode); + checkX11Error(); + + if (keysymsPerKeycode) + { + if (keySyms[0]) + { + KeySym keySym = 0; + +// log(LOG_DEBUG, "keysymsPerKeycode %d", keysymsPerKeycode); + +// for (int i = 0; i < keysymsPerKeycode; ++i) +// log(LOG_DEBUG, "keySym #%d %08x", i, keySyms[i]); + + if ((keysymsPerKeycode >= 2) && keySyms[1] && (keySyms[0] >= XK_a) && (keySyms[0] <= XK_z)) + { + keySym = keySyms[1]; + } + else if (keysymsPerKeycode >= 1) + { + keySym = keySyms[0]; + } + + if (keySym) + { + if (isEscape(keySym, event.xkey.state & allShifts)) + { + cancel = true; + } + else + { + if (isModifier(keySym) || !isAllowed(keySym, event.xkey.state & allShifts)) + { + ignoreKey = true; + } + else + { + char *str = XKeysymToString(keySym); + + if (str && *str) + { + if (event.xkey.state & ShiftMask) + { + shortcut += "Shift+"; + } + if (event.xkey.state & ControlMask) + { + shortcut += "Control+"; + } + if (event.xkey.state & AltMask) + { + shortcut += "Alt+"; + } + if (event.xkey.state & MetaMask) + { + shortcut += "Meta+"; + } + if (event.xkey.state & Level3Mask) + { + shortcut += "Level3+"; + } + if (event.xkey.state & Level5Mask) + { + shortcut += "Level5+"; + } + + shortcut += str; + } + } + } + } + } + } + if (!ignoreKey) + { + IdsByShortcut::iterator idsByShortcut = mIdsByShortcut.find(shortcut); + if ((idsByShortcut == mIdsByShortcut.end()) || (idsByShortcut.value().isEmpty())) + { + log(LOG_DEBUG, "grabShortcut: checking %s", qPrintable(shortcut)); + lockX11Error(); + XUngrabKeyboard(mDisplay, CurrentTime); + checkX11Error(); + + QSet::const_iterator lastAllModifiers = allModifiers.end(); + for (QSet::const_iterator modifiers = allModifiers.begin(); modifiers != lastAllModifiers; ++modifiers) + { + log(LOG_DEBUG, "grabShortcut: checking %02x + %02x", event.xkey.keycode, event.xkey.state | *modifiers); + lockX11Error(); + XGrabKey(mDisplay, event.xkey.keycode, event.xkey.state | *modifiers, rootWindow, False, GrabModeAsync, GrabModeAsync); + ignoreKey |= checkX11Error(LOG_DEBUG); + } + + lockX11Error(); + XUngrabKey(mDisplay, event.xkey.keycode, event.xkey.state, rootWindow); + checkX11Error(); + + if (ignoreKey) + { + lockX11Error(); + XGrabKeyboard(mDisplay, rootWindow, False, GrabModeAsync, GrabModeAsync, CurrentTime); + checkX11Error(); + } + } + else + { + log(LOG_DEBUG, "grabShortcut: already grabbed %s", qPrintable(shortcut)); + lockX11Error(); + XUngrabKeyboard(mDisplay, CurrentTime); + checkX11Error(); + } + } + if (!ignoreKey) + { + mGrabbingShortcut = false; + + if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &cancel, sizeof(cancel))) + { + log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); + close(mX11RequestPipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + if (!cancel) + { + size_t length = shortcut.length(); + if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &length, sizeof(length))) + { + log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); + close(mX11RequestPipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], qPrintable(shortcut), length)) + { + log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); + close(mX11RequestPipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + } + + emit onShortcutGrabbed(); + } + } + else + { + QString shortcut = mShortcutByX11[qMakePair(static_cast(event.xkey.keycode), event.xkey.state & allShifts)]; + log(LOG_DEBUG, "KeyPress %08x %08x %s", event.xkey.state & allShifts, event.xkey.keycode, qPrintable(shortcut)); + + IdsByShortcut::iterator idsByShortcut = mIdsByShortcut.find(shortcut); + if (idsByShortcut != mIdsByShortcut.end()) + { + Ids &ids = idsByShortcut.value(); + switch (mMultipleActionsBehaviour) + { + case MULTIPLE_ACTIONS_BEHAVIOUR_FIRST: + { + Ids::iterator lastIds = ids.end(); + for (Ids::iterator idi = ids.begin(); idi != lastIds; ++idi) + if (mShortcutAndActionById[*idi].second->call()) + { + break; + } + } + break; + + case MULTIPLE_ACTIONS_BEHAVIOUR_LAST: + { + Ids::iterator firstIds = ids.begin(); + for (Ids::iterator idi = ids.end(); idi != firstIds;) + { + --idi; + if (mShortcutAndActionById[*idi].second->call()) + { + break; + } + } + } + break; + + case MULTIPLE_ACTIONS_BEHAVIOUR_NONE: + if (ids.size() == 1) + { + mShortcutAndActionById[*(ids.begin())].second->call(); + } + break; + + case MULTIPLE_ACTIONS_BEHAVIOUR_ALL: + { + Ids::iterator lastIds = ids.end(); + for (Ids::iterator idi = ids.begin(); idi != lastIds; ++idi) + { + mShortcutAndActionById[*idi].second->call(); + } + } + break; + + default: + ; + } + } + } + } + break; + + default: + { + pollfd *fds = new pollfd[1]; + fds[0].fd = mX11RequestPipe[STDIN_FILENO]; + fds[0].events = POLLIN | POLLERR | POLLHUP; + if (poll(fds, 1, 0) >= 0) + { + if (fds[0].revents & POLLIN) + { + size_t X11Operation; + if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], &X11Operation, sizeof(X11Operation))) + { + log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); + close(mX11ResponsePipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } +// log(LOG_DEBUG, "X11Operation: %d", X11Operation); + + switch (X11Operation) + { + case X11_OP_StringToKeycode: + { + bool x11Error = false; + KeyCode keyCode = 0; + size_t length; + if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], &length, sizeof(length))) + { + log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); + close(mX11ResponsePipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + if (length) + { + char *str = new char[length + 1]; + str[length] = '\0'; + if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], str, length)) + { + log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); + close(mX11ResponsePipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + KeySym keySym = XStringToKeysym(str); + delete[] str; + lockX11Error(); + keyCode = XKeysymToKeycode(mDisplay, keySym); + x11Error = checkX11Error(); + } + + signal = x11Error ? 1 : 0; + if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &signal, sizeof(signal))) + { + log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); + close(mX11RequestPipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + + if (!x11Error) + if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &keyCode, sizeof(keyCode))) + { + log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); + close(mX11RequestPipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + } + break; + + case X11_OP_KeycodeToString: + { + KeyCode keyCode; + bool x11Error = false; + if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], &keyCode, sizeof(keyCode))) + { + log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); + close(mX11ResponsePipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + int keysymsPerKeycode; + lockX11Error(); + KeySym *keySyms = XGetKeyboardMapping(mDisplay, keyCode, 1, &keysymsPerKeycode); + x11Error = checkX11Error(); + char *str = NULL; + + if (!x11Error) + { + KeySym keySym = 0; + if ((keysymsPerKeycode >= 2) && keySyms[1] && (keySyms[0] >= XK_a) && (keySyms[0] <= XK_z)) + { + keySym = keySyms[1]; + } + else if (keysymsPerKeycode >= 1) + { + keySym = keySyms[0]; + } + + if (keySym) + { + str = XKeysymToString(keySym); + } + } + + signal = x11Error ? 1 : 0; + if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &signal, sizeof(signal))) + { + log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); + close(mX11RequestPipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + + if (!x11Error) + { + size_t length = 0; + if (str) + { + length = strlen(str); + } + if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &length, sizeof(length))) + { + log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); + close(mX11RequestPipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + if (length) + { + if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], str, length)) + { + log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); + close(mX11RequestPipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + } + } + } + break; + + case X11_OP_XGrabKey: + { + X11Shortcut X11shortcut; + bool x11Error = false; + if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], &X11shortcut.first, sizeof(X11shortcut.first))) + { + log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); + close(mX11ResponsePipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], &X11shortcut.second, sizeof(X11shortcut.second))) + { + log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); + close(mX11ResponsePipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + + QSet::const_iterator lastAllModifiers = allModifiers.end(); + for (QSet::const_iterator modifiers = allModifiers.begin(); modifiers != lastAllModifiers; ++modifiers) + { + lockX11Error(); + XGrabKey(mDisplay, X11shortcut.first, X11shortcut.second | *modifiers, rootWindow, False, GrabModeAsync, GrabModeAsync); + bool x11e = checkX11Error(); + if (x11e) + { + log(LOG_DEBUG, "XGrabKey: %02x + %02x", X11shortcut.first, X11shortcut.second | *modifiers); + } + x11Error |= x11e; + } + + signal = x11Error ? 1 : 0; + if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &signal, sizeof(signal))) + { + log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); + close(mX11RequestPipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + } + break; + + case X11_OP_XUngrabKey: + { + X11Shortcut X11shortcut; + bool x11Error = false; + if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], &X11shortcut.first, sizeof(X11shortcut.first))) + { + log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); + close(mX11ResponsePipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + if (error_t error = readAll(mX11RequestPipe[STDIN_FILENO], &X11shortcut.second, sizeof(X11shortcut.second))) + { + log(LOG_CRIT, "Cannot read from X11 request pipe: %s", strerror(error)); + close(mX11ResponsePipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + + lockX11Error(); + QSet::const_iterator lastAllModifiers = allModifiers.end(); + for (QSet::const_iterator modifiers = allModifiers.begin(); modifiers != lastAllModifiers; ++modifiers) + { + XUngrabKey(mDisplay, X11shortcut.first, X11shortcut.second | *modifiers, rootWindow); + } + x11Error = checkX11Error(); + + signal = x11Error ? 1 : 0; + if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &signal, sizeof(signal))) + { + log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); + close(mX11RequestPipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + } + break; + + case X11_OP_XGrabKeyboard: + { + lockX11Error(); + int result = XGrabKeyboard(mDisplay, rootWindow, False, GrabModeAsync, GrabModeAsync, CurrentTime); + bool x11Error = checkX11Error(); + if (!result && x11Error) + { + result = -1; + } + + if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &result, sizeof(result))) + { + log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); + close(mX11RequestPipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + mDataMutex.lock(); + mGrabbingShortcut = true; + mDataMutex.unlock(); + } + break; + + case X11_OP_XUngrabKeyboard: + { + lockX11Error(); + XUngrabKeyboard(mDisplay, CurrentTime); + bool x11Error = checkX11Error(); + + signal = x11Error ? 1 : 0; + if (error_t error = writeAll(mX11ResponsePipe[STDOUT_FILENO], &signal, sizeof(signal))) + { + log(LOG_CRIT, "Cannot write to X11 response pipe: %s", strerror(error)); + close(mX11RequestPipe[STDIN_FILENO]); + mX11EventLoopActive = false; + break; + } + + mDataMutex.lock(); + mGrabbingShortcut = false; + mDataMutex.unlock(); + } + break; + + } + } + } + } + } + } + } + + lockX11Error(); + XUngrabKey(mDisplay, AnyKey, AnyModifier, rootWindow); + XSetErrorHandler(oldx11ErrorHandler); + XCloseDisplay(mDisplay); + checkX11Error(0); +} + +void Core::serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner) +{ + if (!oldOwner.isEmpty() && newOwner.isEmpty()) + { + serviceDisappeared(oldOwner); + } +} + +void Core::serviceDisappeared(const QString &sender) +{ + log(LOG_DEBUG, "serviceDisappeared '%s'", qPrintable(sender)); + + QMutexLocker lock(&mDataMutex); + + ClientPathsBySender::iterator clientPathsBySender = mClientPathsBySender.find(sender); + if (clientPathsBySender != mClientPathsBySender.end()) + { + ClientPaths::const_iterator lastClientPath = clientPathsBySender.value().end(); + for (ClientPaths::const_iterator clientPath = clientPathsBySender.value().begin(); clientPath != lastClientPath; ++clientPath) + { + const QDBusObjectPath &path = *clientPath; + + IdByClientPath::iterator idByClientPath = mIdByClientPath.find(path); + if (idByClientPath != mIdByClientPath.end()) + { + const qulonglong &id = idByClientPath.value(); + + log(LOG_INFO, "Disactivating client action for '%s' @ %s", qPrintable(sender), qPrintable(path.path())); + + ShortcutAndActionById::iterator shortcutAndActionById = mShortcutAndActionById.find(id); + if (shortcutAndActionById != mShortcutAndActionById.end()) + { + const QString &shortcut = shortcutAndActionById.value().first; + + dynamic_cast(shortcutAndActionById.value().second)->disappeared(); + mDaemonAdaptor->emit_clientActionSenderChanged(id, QString()); + + X11Shortcut X11shortcut = mX11ByShortcut[shortcut]; + + IdsByShortcut::iterator idsByShortcut = mIdsByShortcut.find(shortcut); + if (idsByShortcut != mIdsByShortcut.end()) + { + idsByShortcut.value().remove(id); + if (idsByShortcut.value().isEmpty()) + { + mIdsByShortcut.erase(idsByShortcut); + + if (!remoteXUngrabKey(X11shortcut)) + { + log(LOG_WARNING, "Cannot ungrab shortcut '%s'", qPrintable(shortcut)); + } + } + } + } + } + mSenderByClientPath.remove(path); + } + mClientPathsBySender.erase(clientPathsBySender); + } +} + +KeyCode Core::remoteStringToKeycode(const QString &str) +{ + size_t X11Operation = X11_OP_StringToKeycode; + size_t length = str.length(); + if (error_t error = writeAll(mX11RequestPipe[STDOUT_FILENO], &X11Operation, sizeof(X11Operation))) + { + log(LOG_CRIT, "Cannot write to X11 request pipe: %s", strerror(error)); + qApp->quit(); + return 0; + } + if (error_t error = writeAll(mX11RequestPipe[STDOUT_FILENO], &length, sizeof(length))) + { + log(LOG_CRIT, "Cannot write to X11 request pipe: %s", strerror(error)); + qApp->quit(); + return 0; + } + if (length) + { + if (error_t error = writeAll(mX11RequestPipe[STDOUT_FILENO], qPrintable(str), length)) + { + log(LOG_CRIT, "Cannot write to X11 request pipe: %s", strerror(error)); + qApp->quit(); + return 0; + } + } + wakeX11Thread(); + + char signal; + if (error_t error = readAll(mX11ResponsePipe[STDIN_FILENO], &signal, sizeof(signal))) + { + log(LOG_CRIT, "Cannot read from X11 response pipe: %s", strerror(error)); + qApp->quit(); + return 0; + } + if (signal) + { + return 0; + } + + KeyCode keyCode; + if (error_t error = readAll(mX11ResponsePipe[STDIN_FILENO], &keyCode, sizeof(keyCode))) + { + log(LOG_CRIT, "Cannot read from X11 response pipe: %s", strerror(error)); + qApp->quit(); + return 0; + } + return keyCode; +} + +QString Core::remoteKeycodeToString(KeyCode keyCode) +{ + QString result; + + size_t X11Operation = X11_OP_KeycodeToString; + if (error_t error = writeAll(mX11RequestPipe[STDOUT_FILENO], &X11Operation, sizeof(X11Operation))) + { + log(LOG_CRIT, "Cannot write to X11 request pipe: %s", strerror(error)); + qApp->quit(); + return QString(); + } + if (error_t error = writeAll(mX11RequestPipe[STDOUT_FILENO], &keyCode, sizeof(keyCode))) + { + log(LOG_CRIT, "Cannot write to X11 request pipe: %s", strerror(error)); + qApp->quit(); + return QString(); + } + wakeX11Thread(); + + char signal; + if (error_t error = readAll(mX11ResponsePipe[STDIN_FILENO], &signal, sizeof(signal))) + { + log(LOG_CRIT, "Cannot read from X11 response pipe: %s", strerror(error)); + qApp->quit(); + return QString(); + } + if (signal) + { + return QString(); + } + + size_t length; + if (error_t error = readAll(mX11ResponsePipe[STDIN_FILENO], &length, sizeof(length))) + { + log(LOG_CRIT, "Cannot read from X11 response pipe: %s", strerror(error)); + qApp->quit(); + return QString(); + } + if (length) + { + char *str = new char[length + 1]; + str[length] = '\0'; + if (error_t error = readAll(mX11ResponsePipe[STDIN_FILENO], str, length)) + { + log(LOG_CRIT, "Cannot read from X11 response pipe: %s", strerror(error)); + qApp->quit(); + return QString(); + } + result = str; + + delete[] str; + } + + return result; +} + +bool Core::remoteXGrabKey(const X11Shortcut &X11shortcut) +{ + size_t X11Operation = X11_OP_XGrabKey; + if (error_t error = writeAll(mX11RequestPipe[STDOUT_FILENO], &X11Operation, sizeof(X11Operation))) + { + log(LOG_CRIT, "Cannot write to X11 request pipe: %s", strerror(error)); + qApp->quit(); + return false; + } + if (error_t error = writeAll(mX11RequestPipe[STDOUT_FILENO], &X11shortcut.first, sizeof(X11shortcut.first))) + { + log(LOG_CRIT, "Cannot write to X11 request pipe: %s", strerror(error)); + qApp->quit(); + return false; + } + if (error_t error = writeAll(mX11RequestPipe[STDOUT_FILENO], &X11shortcut.second, sizeof(X11shortcut.second))) + { + log(LOG_CRIT, "Cannot write to X11 request pipe: %s", strerror(error)); + qApp->quit(); + return false; + } + wakeX11Thread(); + + char signal; + if (error_t error = readAll(mX11ResponsePipe[STDIN_FILENO], &signal, sizeof(signal))) + { + log(LOG_CRIT, "Cannot read from X11 response pipe: %s", strerror(error)); + qApp->quit(); + return false; + } + if (signal) + { + return false; + } + + return true; +} + +bool Core::remoteXUngrabKey(const X11Shortcut &X11shortcut) +{ + size_t X11Operation = X11_OP_XUngrabKey; + if (error_t error = writeAll(mX11RequestPipe[STDOUT_FILENO], &X11Operation, sizeof(X11Operation))) + { + log(LOG_CRIT, "Cannot write to X11 request pipe: %s", strerror(error)); + qApp->quit(); + return false; + } + if (error_t error = writeAll(mX11RequestPipe[STDOUT_FILENO], &X11shortcut.first, sizeof(X11shortcut.first))) + { + log(LOG_CRIT, "Cannot write to X11 request pipe: %s", strerror(error)); + qApp->quit(); + return false; + } + if (error_t error = writeAll(mX11RequestPipe[STDOUT_FILENO], &X11shortcut.second, sizeof(X11shortcut.second))) + { + log(LOG_CRIT, "Cannot write to X11 request pipe: %s", strerror(error)); + qApp->quit(); + return false; + } + wakeX11Thread(); + + char signal; + if (error_t error = readAll(mX11ResponsePipe[STDIN_FILENO], &signal, sizeof(signal))) + { + log(LOG_CRIT, "Cannot read from X11 response pipe: %s", strerror(error)); + qApp->quit(); + return false; + } + if (signal) + { + return false; + } + + return true; +} + +QString Core::grabOrReuseKey(const X11Shortcut &X11shortcut, const QString &shortcut) +{ + IdsByShortcut::iterator idsByShortcut = mIdsByShortcut.find(shortcut); + if ((idsByShortcut != mIdsByShortcut.end()) && (!idsByShortcut.value().isEmpty())) + { + return shortcut; + } + + if (!remoteXGrabKey(X11shortcut)) + { + log(LOG_WARNING, "Cannot grab shortcut '%s'", qPrintable(shortcut)); + return QString(); + } + + return shortcut; +} + + +Core::X11Shortcut Core::ShortcutToX11(const QString &shortcut) +{ + X11Shortcut result(0, 0); + + QStringList parts = shortcut.split('+'); + + size_t m = parts.size(); + for (size_t i = 0; i < m - 1; ++i) + { + if (parts[i] == "Shift") + { + result.second |= ShiftMask; + } + else if (parts[i] == "Control") + { + result.second |= ControlMask; + } + else if (parts[i] == "Alt") + { + result.second |= AltMask; + } + else if (parts[i] == "Meta") + { + result.second |= MetaMask; + } + else if (parts[i] == "Level3") + { + result.second |= Level3Mask; + } + else if (parts[i] == "Level5") + { + result.second |= Level5Mask; + } + else + { + throw false; + } + } + if (m) + { + KeyCode keyCode = remoteStringToKeycode(parts[m - 1]); + if (!keyCode) + { + throw false; + } + + result.first = keyCode; + } + + return result; +} + +QString Core::X11ToShortcut(const X11Shortcut &X11shortcut) +{ + QString result; + + if (X11shortcut.second & ShiftMask) + { + result += "Shift+"; + } + if (X11shortcut.second & ControlMask) + { + result += "Control+"; + } + if (X11shortcut.second & AltMask) + { + result += "Alt+"; + } + if (X11shortcut.second & MetaMask) + { + result += "Meta+"; + } + if (X11shortcut.second & Level3Mask) + { + result += "Level3+"; + } + if (X11shortcut.second & Level5Mask) + { + result += "Level5+"; + } + + QString key = remoteKeycodeToString(X11shortcut.first); + if (key.isEmpty()) + { + throw false; + } + + result += key; + + return result; +} + +QString Core::checkShortcut(const QString &shortcut, X11Shortcut &X11shortcut) +{ + if (shortcut.isEmpty()) + return QString(); + + QString usedShortcut; + + try + { + X11shortcut = ShortcutToX11(shortcut); + } + catch (bool) + { + log(LOG_WARNING, "Cannot extract keycode and modifiers from shortcut '%s'", qPrintable(shortcut)); + return QString(); + } + + try + { + ShortcutByX11::const_iterator shortcutByX11 = mShortcutByX11.find(X11shortcut); + if (shortcutByX11 != mShortcutByX11.end()) + { + usedShortcut = shortcutByX11.value(); + } + else + { + usedShortcut = X11ToShortcut(X11shortcut); + mShortcutByX11[X11shortcut] = usedShortcut; + } + } + catch (bool) + { + log(LOG_WARNING, "Cannot get back shortcut '%s'", qPrintable(shortcut)); + return QString(); + } + + if (shortcut != usedShortcut) + { + log(LOG_INFO, "Using shortcut '%s' instead of '%s'", qPrintable(usedShortcut), qPrintable(shortcut)); + } + + X11ByShortcut::const_iterator x11ByShortcut = mX11ByShortcut.find(usedShortcut); + if (x11ByShortcut == mX11ByShortcut.end()) + { + mX11ByShortcut[usedShortcut] = X11shortcut; + } + + return usedShortcut; +} + +QPair Core::addOrRegisterClientAction(const QString &shortcut, const QDBusObjectPath &path, const QString &description, const QString &sender) +{ + X11Shortcut X11shortcut; + + QString newShortcut = checkShortcut(shortcut, X11shortcut); +// if (newShortcut.isEmpty()) +// { +// return qMakePair(QString(), 0ull); +// } + + IdByClientPath::iterator idByNativeClient = mIdByClientPath.find(path); + if (idByNativeClient != mIdByClientPath.end()) + { + qulonglong id = idByNativeClient.value(); + ShortcutAndAction &shortcutAndAction = mShortcutAndActionById[id]; + if (newShortcut != shortcutAndAction.first) + { + mShortcutAndActionById[id].first = newShortcut; + } + + if (!newShortcut.isEmpty()) + { + newShortcut = grabOrReuseKey(X11shortcut, newShortcut); + mIdsByShortcut[newShortcut].insert(id); + } + + dynamic_cast(shortcutAndAction.second)->appeared(QDBusConnection::sessionBus(), sender); + + return qMakePair(newShortcut, id); + } + + qulonglong id = ++mLastId; + + if (!sender.isEmpty() && !newShortcut.isEmpty()) + { + newShortcut = grabOrReuseKey(X11shortcut, newShortcut); + mIdsByShortcut[newShortcut].insert(id); + } + + mIdByClientPath[path] = id; + ClientAction *clientAction = sender.isEmpty() ? new ClientAction(this, path, description) : new ClientAction(this, QDBusConnection::sessionBus(), sender, path, description); + mShortcutAndActionById[id] = qMakePair(newShortcut, clientAction); + + log(LOG_INFO, "addClientAction shortcut:'%s' id:%llu", qPrintable(newShortcut), id); + + return qMakePair(newShortcut, id); +} + +void Core::addClientAction(QPair &result, const QString &shortcut, const QDBusObjectPath &path, const QString &description, const QString &sender) +{ + log(LOG_INFO, "addClientAction shortcut:'%s' path:'%s' description:'%s' sender:'%s'", qPrintable(shortcut), qPrintable(path.path()), qPrintable(description), qPrintable(sender)); + + QMutexLocker lock(&mDataMutex); + + SenderByClientPath::iterator senderByClientPath = mSenderByClientPath.find(path); + if (senderByClientPath != mSenderByClientPath.end()) + { + log(LOG_WARNING, "Action already registered for '%s' (sender: %s)", qPrintable(path.path()), qPrintable(sender)); + result = qMakePair(QString(), 0ull); + return; + } + + QString useShortcut = shortcut; + if (shortcut.isEmpty()) + { + IdByClientPath::ConstIterator idByClientPath = mIdByClientPath.find(path); + if (idByClientPath != mIdByClientPath.constEnd()) + { + useShortcut = mShortcutAndActionById[idByClientPath.value()].first;; + } + } + + mSenderByClientPath[path] = sender; + + mClientPathsBySender[sender].insert(path); + + result = addOrRegisterClientAction(useShortcut, path, description, sender); + + saveConfig(); + + mDaemonAdaptor->emit_clientActionSenderChanged(result.second, sender); + + mDaemonAdaptor->emit_actionAdded(result.second); +} + +qulonglong Core::registerClientAction(const QString &shortcut, const QDBusObjectPath &path, const QString &description) +{ + log(LOG_INFO, "registerClientAction shortcut:'%s' path:'%s' description:'%s'", qPrintable(shortcut), qPrintable(path.path()), qPrintable(description)); + + QMutexLocker lock(&mDataMutex); + + return addOrRegisterClientAction(shortcut, path, description, QString()).second; +} + +void Core::addMethodAction(QPair &result, const QString &shortcut, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description) +{ + log(LOG_INFO, "addMethodAction shortcut:'%s' service:'%s' path:'%s' interface:'%s' method:'%s' description:'%s'", qPrintable(shortcut), qPrintable(service), qPrintable(path.path()), qPrintable(interface), qPrintable(method), qPrintable(description)); + + QMutexLocker lock(&mDataMutex); + + X11Shortcut X11shortcut; + QString newShortcut = checkShortcut(shortcut, X11shortcut); + if (newShortcut.isEmpty()) + { + result = qMakePair(QString(), 0ull); + return; + } + + newShortcut = grabOrReuseKey(X11shortcut, newShortcut); + if (newShortcut.isEmpty()) + { + result = qMakePair(QString(), 0ull); + return; + } + + + qulonglong id = ++mLastId; + + mIdsByShortcut[newShortcut].insert(id); + mShortcutAndActionById[id] = qMakePair(newShortcut, new MethodAction(this, QDBusConnection::sessionBus(), service, path, interface, method, description)); + + log(LOG_INFO, "addMethodAction shortcut:'%s' id:%llu", qPrintable(newShortcut), id); + + saveConfig(); + + result = qMakePair(newShortcut, id); +} + +qulonglong Core::registerMethodAction(const QString &shortcut, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description) +{ + QPair result; + addMethodAction(result, shortcut, service, path, interface, method, description); + return result.second; +} + +void Core::addCommandAction(QPair &result, const QString &shortcut, const QString &command, const QStringList &arguments, const QString &description) +{ + log(LOG_INFO, "addCommandAction shortcut:'%s' command:'%s' arguments:'%s' description:'%s'", qPrintable(shortcut), qPrintable(command), qPrintable(joinToString(arguments, "", "' '", "")), qPrintable(description)); + + QMutexLocker lock(&mDataMutex); + + X11Shortcut X11shortcut; + QString newShortcut = checkShortcut(shortcut, X11shortcut); + if (newShortcut.isEmpty()) + { + result = qMakePair(QString(), 0ull); + return; + } + + newShortcut = grabOrReuseKey(X11shortcut, newShortcut); + if (newShortcut.isEmpty()) + { + result = qMakePair(QString(), 0ull); + return; + } + + + qulonglong id = ++mLastId; + + mIdsByShortcut[newShortcut].insert(id); + mShortcutAndActionById[id] = qMakePair(newShortcut, new CommandAction(this, command, arguments, description)); + + log(LOG_INFO, "addCommandAction shortcut:'%s' id:%llu", qPrintable(newShortcut), id); + + saveConfig(); + + result = qMakePair(newShortcut, id); +} + +qulonglong Core::registerCommandAction(const QString &shortcut, const QString &command, const QStringList &arguments, const QString &description) +{ + QPair result; + addCommandAction(result, shortcut, command, arguments, description); + return result.second; +} + +void Core::modifyClientAction(qulonglong &result, const QDBusObjectPath &path, const QString &description, const QString &sender) +{ + log(LOG_INFO, "modifyClientAction path:'%s' description:'%s' sender:'%s'", qPrintable(path.path()), qPrintable(description), qPrintable(sender)); + + QMutexLocker lock(&mDataMutex); + + IdByClientPath::iterator idByNativeClient = mIdByClientPath.find(path); + if (idByNativeClient == mIdByClientPath.end()) + { + log(LOG_WARNING, "No action registered for '%s' (sender: %s)", qPrintable(path.path()), qPrintable(sender)); + result = 0ull; + return; + } + + SenderByClientPath::Iterator senderByClientPath = mSenderByClientPath.find(path); + if (senderByClientPath == mSenderByClientPath.end()) + { + log(LOG_WARNING, "No action activated for '%s' (sender: %s)", qPrintable(path.path()), qPrintable(sender)); + result = 0ull; + return; + } + + if (senderByClientPath.value() != sender) + { + log(LOG_WARNING, "Sender mismatch: caller: %s owner: %s", qPrintable(senderByClientPath.value()), qPrintable(sender)); + result = 0ull; + return; + } + + qulonglong id = idByNativeClient.value(); + + mShortcutAndActionById[id].second->setDescription(description); + + saveConfig(); + + result = id; + + mDaemonAdaptor->emit_actionModified(result); +} + +void Core::modifyActionDescription(bool &result, const qulonglong &id, const QString &description) +{ + log(LOG_INFO, "modifyActionDescription id:%llu description:'%s'", id, qPrintable(description)); + + QMutexLocker lock(&mDataMutex); + + ShortcutAndActionById::iterator shortcutAndActionById = mShortcutAndActionById.find(id); + if (shortcutAndActionById == mShortcutAndActionById.end()) + { + log(LOG_WARNING, "No action registered with id #%llu", id); + result = false; + return; + } + + BaseAction *action = shortcutAndActionById.value().second; + + if ((strcmp(action->type(), MethodAction::id())) && (strcmp(action->type(), CommandAction::id()))) + { + log(LOG_WARNING, "modifyActionDescription attempts to modify action of type '%s'", action->type()); + result = false; + return; + } + + action->setDescription(description); + + saveConfig(); + + result = true; +} + +void Core::modifyMethodAction(bool &result, const qulonglong &id, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description) +{ + log(LOG_INFO, "modifyMethodAction id:%llu service:'%s' path:'%s' interface:'%s' method:'%s' description:'%s'", id, qPrintable(service), qPrintable(path.path()), qPrintable(interface), qPrintable(method), qPrintable(description)); + + QMutexLocker lock(&mDataMutex); + + ShortcutAndActionById::iterator shortcutAndActionById = mShortcutAndActionById.find(id); + if (shortcutAndActionById == mShortcutAndActionById.end()) + { + log(LOG_WARNING, "No action registered with id #%llu", id); + result = false; + return; + } + + BaseAction *action = shortcutAndActionById.value().second; + + if (strcmp(action->type(), MethodAction::id())) + { + log(LOG_WARNING, "modifyMethodAction attempts to modify action of type '%s'", action->type()); + result = false; + return; + } + + delete action; + shortcutAndActionById.value().second = new MethodAction(this, QDBusConnection::sessionBus(), service, path, interface, method, description); + + saveConfig(); + + result = true; +} + +void Core::modifyCommandAction(bool &result, const qulonglong &id, const QString &command, const QStringList &arguments, const QString &description) +{ + log(LOG_INFO, "modifyCommandAction id:%llu command:'%s' arguments:'%s' description:'%s'", id, qPrintable(command), qPrintable(joinToString(arguments, "", "' '", "")), qPrintable(description)); + + QMutexLocker lock(&mDataMutex); + + ShortcutAndActionById::iterator shortcutAndActionById = mShortcutAndActionById.find(id); + if (shortcutAndActionById == mShortcutAndActionById.end()) + { + log(LOG_WARNING, "No action registered with id #%llu", id); + result = false; + return; + } + + BaseAction *action = shortcutAndActionById.value().second; + + if (strcmp(action->type(), CommandAction::id())) + { + log(LOG_WARNING, "modifyMethodAction attempts to modify action of type '%s'", action->type()); + result = false; + return; + } + + delete action; + shortcutAndActionById.value().second = new CommandAction(this, command, arguments, description); + + saveConfig(); + + result = true; +} + +void Core::enableClientAction(bool &result, const QDBusObjectPath &path, bool enabled, const QString &sender) +{ + log(LOG_INFO, "enableClientAction path:'%s' enabled:%s sender:'%s'", qPrintable(path.path()), enabled ? " true" : "false", qPrintable(sender)); + + QMutexLocker lock(&mDataMutex); + + IdByClientPath::iterator idByNativeClient = mIdByClientPath.find(path); + if (idByNativeClient == mIdByClientPath.end()) + { + log(LOG_WARNING, "No action registered for '%s' (sender: %s)", qPrintable(path.path()), qPrintable(sender)); + result = false; + return; + } + + SenderByClientPath::Iterator senderByClientPath = mSenderByClientPath.find(path); + if (senderByClientPath == mSenderByClientPath.end()) + { + log(LOG_WARNING, "No action activated for '%s' (sender: %s)", qPrintable(path.path()), qPrintable(sender)); + result = false; + return; + } + + if (senderByClientPath.value() != sender) + { + log(LOG_WARNING, "Sender mismatch: caller: %s owner: %s", qPrintable(senderByClientPath.value()), qPrintable(sender)); + result = false; + return; + } + + qulonglong id = idByNativeClient.value(); + + mShortcutAndActionById[id].second->setEnabled(enabled); + + saveConfig(); + + result = true; + + mDaemonAdaptor->emit_actionEnabled(id, result); +} + +void Core::isClientActionEnabled(bool &enabled, const QDBusObjectPath &path, const QString &sender) +{ + log(LOG_INFO, "isClientActionEnabled path:'%s' sender:'%s'", qPrintable(path.path()), qPrintable(sender)); + + enabled = false; + + QMutexLocker lock(&mDataMutex); + IdByClientPath::iterator idByNativeClient = mIdByClientPath.find(path); + if (idByNativeClient == mIdByClientPath.end()) + { + log(LOG_WARNING, "No action registered for '%s' (sender: %s)", qPrintable(path.path()), qPrintable(sender)); + return; + } + + SenderByClientPath::Iterator senderByClientPath = mSenderByClientPath.find(path); + if (senderByClientPath == mSenderByClientPath.end()) + { + log(LOG_WARNING, "No action activated for '%s' (sender: %s)", qPrintable(path.path()), qPrintable(sender)); + return; + } + + if (senderByClientPath.value() != sender) + { + log(LOG_WARNING, "Sender mismatch: caller: %s owner: %s", qPrintable(senderByClientPath.value()), qPrintable(sender)); + return; + } + + enabled = mShortcutAndActionById[idByNativeClient.value()].second->isEnabled(); +} + +void Core::enableAction(bool &result, qulonglong id, bool enabled) +{ + log(LOG_INFO, "enableAction id:%llu enabled:%s", id, enabled ? "true" : " false"); + + QMutexLocker lock(&mDataMutex); + + ShortcutAndActionById::iterator shortcutAndActionById = mShortcutAndActionById.find(id); + if (shortcutAndActionById == mShortcutAndActionById.end()) + { + log(LOG_WARNING, "No action registered with id #%llu", id); + result = false; + return; + } + + shortcutAndActionById.value().second->setEnabled(enabled); + + saveConfig(); + + result = true; +} + +void Core::isActionEnabled(bool &enabled, qulonglong id) +{ + log(LOG_INFO, "isActionEnabled id:%llu", id); + + enabled = false; + + QMutexLocker lock(&mDataMutex); + + ShortcutAndActionById::iterator shortcutAndActionById = mShortcutAndActionById.find(id); + if (shortcutAndActionById == mShortcutAndActionById.end()) + { + log(LOG_WARNING, "No action registered with id #%llu", id); + return; + } + + enabled = shortcutAndActionById.value().second->isEnabled(); +} + +void Core::getClientActionSender(QString &sender, qulonglong id) +{ + log(LOG_INFO, "getClientActionSender id:'%llu'", id); + + sender.clear(); + + QMutexLocker lock(&mDataMutex); + + ShortcutAndActionById::iterator shortcutAndActionById = mShortcutAndActionById.find(id); + if (shortcutAndActionById == mShortcutAndActionById.end()) + { + log(LOG_WARNING, "No action registered with id #%llu", id); + return; + } + + BaseAction *action = shortcutAndActionById.value().second; + if (!strcmp(action->type(), ClientAction::id())) + { + sender = dynamic_cast(action)->service(); + } +} + +void Core::changeClientActionShortcut(QPair &result, const QDBusObjectPath &path, const QString &shortcut, const QString &sender) +{ + log(LOG_INFO, "changeClientActionShortcut path:'%s' shortcut:'%s' sender:'%s'", qPrintable(path.path()), qPrintable(shortcut), qPrintable(sender)); + + if (shortcut.isEmpty()) + { + result = qMakePair(QString(), 0ull); + return; + } + + QMutexLocker lock(&mDataMutex); + + IdByClientPath::iterator idByNativeClient = mIdByClientPath.find(path); + if (idByNativeClient == mIdByClientPath.end()) + { + log(LOG_WARNING, "No action registered for '%s' (sender: %s)", qPrintable(path.path()), qPrintable(sender)); + result = qMakePair(QString(), 0ull); + return; + } + + SenderByClientPath::Iterator senderByClientPath = mSenderByClientPath.find(path); + if (senderByClientPath == mSenderByClientPath.end()) + { + log(LOG_WARNING, "No action activated for '%s' (sender: %s)", qPrintable(path.path()), qPrintable(sender)); + result = qMakePair(QString(), 0ull); + return; + } + + if (senderByClientPath.value() != sender) + { + log(LOG_WARNING, "Sender mismatch: caller: %s owner: %s", qPrintable(senderByClientPath.value()), qPrintable(sender)); + result = qMakePair(QString(), 0ull); + return; + } + + qulonglong id = idByNativeClient.value(); + + X11Shortcut X11shortcut; + QString newShortcut = checkShortcut(shortcut, X11shortcut); + if (newShortcut.isEmpty()) + { + result = qMakePair(QString(), id); + return; + } + + ShortcutAndActionById::iterator shortcutAndActionById = mShortcutAndActionById.find(id); + + QString oldShortcut = shortcutAndActionById.value().first; + + if (oldShortcut != newShortcut) + { + newShortcut = grabOrReuseKey(X11shortcut, newShortcut); + if (newShortcut.isEmpty()) + { + result = qMakePair(QString(), id); + return; + } + + IdsByShortcut::iterator idsByShortcut = mIdsByShortcut.find(oldShortcut); + if (idsByShortcut != mIdsByShortcut.end()) + { + idsByShortcut.value().remove(id); + if (idsByShortcut.value().isEmpty()) + { + mIdsByShortcut.erase(idsByShortcut); + + if (!remoteXUngrabKey(mX11ByShortcut[oldShortcut])) + { + log(LOG_WARNING, "Cannot ungrab shortcut '%s'", qPrintable(shortcut)); + } + } + } + + mIdsByShortcut[newShortcut].insert(id); + shortcutAndActionById.value().first = newShortcut; + } + + saveConfig(); + + dynamic_cast(shortcutAndActionById.value().second)->shortcutChanged(oldShortcut, newShortcut); + + mDaemonAdaptor->emit_actionShortcutChanged(id); + + result = qMakePair(newShortcut, id); +} + +void Core::changeShortcut(QString &result, const qulonglong &id, const QString &shortcut) +{ + log(LOG_INFO, "changeShortcut id:%llu shortcut:'%s'", id, qPrintable(shortcut)); + + if (shortcut.isEmpty()) + { + result = QString(); + return; + } + + QMutexLocker lock(&mDataMutex); + + ShortcutAndActionById::iterator shortcutAndActionById = mShortcutAndActionById.find(id); + if (shortcutAndActionById == mShortcutAndActionById.end()) + { + log(LOG_WARNING, "No action registered with id #%llu", id); + result = QString(); + return; + } + + X11Shortcut X11shortcut; + QString newShortcut = checkShortcut(shortcut, X11shortcut); + if (newShortcut.isEmpty()) + { + result = QString(); + return; + } + + QString oldShortcut = shortcutAndActionById.value().first; + + if (oldShortcut != newShortcut) + { + newShortcut = grabOrReuseKey(X11shortcut, newShortcut); + if (newShortcut.isEmpty()) + { + result = QString(); + return; + } + + IdsByShortcut::iterator idsByShortcut = mIdsByShortcut.find(oldShortcut); + if (idsByShortcut != mIdsByShortcut.end()) + { + idsByShortcut.value().remove(id); + if (idsByShortcut.value().isEmpty()) + { + mIdsByShortcut.erase(idsByShortcut); + + if (!remoteXUngrabKey(mX11ByShortcut[oldShortcut])) + { + log(LOG_WARNING, "Cannot ungrab shortcut '%s'", qPrintable(shortcut)); + } + } + } + + mIdsByShortcut[newShortcut].insert(id); + shortcutAndActionById.value().first = newShortcut; + + if (!strcmp(shortcutAndActionById.value().second->type(), ClientAction::id())) + { + dynamic_cast(shortcutAndActionById.value().second)->shortcutChanged(oldShortcut, newShortcut); + } + } + + saveConfig(); + + result = newShortcut; +} + +void Core::swapActions(bool &result, const qulonglong &id1, const qulonglong &id2) +{ + log(LOG_INFO, "swapActions id1:%llu id2:%llu", id1, id2); + + QMutexLocker lock(&mDataMutex); + + ShortcutAndActionById::iterator shortcutAndActionById1 = mShortcutAndActionById.find(id1); + if (shortcutAndActionById1 == mShortcutAndActionById.end()) + { + log(LOG_WARNING, "No action registered with id #%llu", id1); + result = false; + return; + } + + ShortcutAndActionById::iterator shortcutAndActionById2 = mShortcutAndActionById.find(id2); + if (shortcutAndActionById2 == mShortcutAndActionById.end()) + { + log(LOG_WARNING, "No action registered with id #%llu", id2); + result = false; + return; + } + + if (shortcutAndActionById1.value().first != shortcutAndActionById2.value().first) + { + log(LOG_WARNING, "swapActions attempts to swap action assigned to different shortcuts"); + result = false; + return; + } + + std::swap(shortcutAndActionById1.value().second, shortcutAndActionById2.value().second); + + saveConfig(); + + result = true; +} + +void Core::removeClientAction(bool &result, const QDBusObjectPath &path, const QString &sender) +{ + log(LOG_INFO, "removeClientAction path:'%s' sender:'%s'", qPrintable(path.path()), qPrintable(sender)); + + QMutexLocker lock(&mDataMutex); + + IdByClientPath::iterator idByNativeClient = mIdByClientPath.find(path); + if (idByNativeClient == mIdByClientPath.end()) + { + log(LOG_WARNING, "No action registered for '%s' (sender: %s)", qPrintable(path.path()), qPrintable(sender)); + result = false; + return; + } + + SenderByClientPath::Iterator senderByClientPath = mSenderByClientPath.find(path); + if (senderByClientPath == mSenderByClientPath.end()) + { + log(LOG_WARNING, "No action activated for '%s' (sender: %s)", qPrintable(path.path()), qPrintable(sender)); + result = false; + return; + } + + if (senderByClientPath.value() != sender) + { + log(LOG_WARNING, "Sender mismatch: caller: %s owner: %s", qPrintable(senderByClientPath.value()), qPrintable(sender)); + result = false; + return; + } + + qulonglong id = idByNativeClient.value(); + + ShortcutAndActionById::iterator shortcutAndActionById = mShortcutAndActionById.find(id); + QString shortcut = shortcutAndActionById.value().first; + + X11Shortcut X11shortcut = mX11ByShortcut[shortcut]; + + delete shortcutAndActionById.value().second; + mShortcutAndActionById.erase(shortcutAndActionById); + mIdByClientPath.remove(path); + + IdsByShortcut::iterator idsByShortcut = mIdsByShortcut.find(shortcut); + if (idsByShortcut != mIdsByShortcut.end()) + { + idsByShortcut.value().remove(id); + if (idsByShortcut.value().isEmpty()) + { + mIdsByShortcut.erase(idsByShortcut); + + if (!remoteXUngrabKey(X11shortcut)) + { + log(LOG_WARNING, "Cannot ungrab shortcut '%s'", qPrintable(shortcut)); + } + } + } + + mSenderByClientPath.remove(path); + + mClientPathsBySender[sender].remove(path); + if (mClientPathsBySender[sender].isEmpty()) + mClientPathsBySender.remove(sender); + + saveConfig(); + + result = true; + + mDaemonAdaptor->emit_actionRemoved(id); +} + +void Core::removeAction(bool &result, const qulonglong &id) +{ + log(LOG_INFO, "removeAction id:%llu", id); + + QMutexLocker lock(&mDataMutex); + + ShortcutAndActionById::iterator shortcutAndActionById = mShortcutAndActionById.find(id); + if (shortcutAndActionById == mShortcutAndActionById.end()) + { + log(LOG_WARNING, "No action registered with id #%llu", id); + result = false; + return; + } + + BaseAction *action = shortcutAndActionById.value().second; + + bool isClientAction = !strcmp(action->type(), ClientAction::id()); + + if (isClientAction) + { + ClientAction *clientAction = dynamic_cast(action); + if (clientAction->isPresent()) + { + log(LOG_WARNING, "Cannot remove active client action by id"); + result = false; + return; + } + else + { + const QDBusObjectPath &path = clientAction->path(); + + SenderByClientPath::Iterator senderByClientPath = mSenderByClientPath.find(path); + if (senderByClientPath != mSenderByClientPath.end()) + { + log(LOG_WARNING, "Action is currently registered for '%s'", qPrintable(path.path())); + result = false; + return; + } + } + } + + QString shortcut = shortcutAndActionById.value().first; + + X11Shortcut X11shortcut = mX11ByShortcut[shortcut]; + + delete action; + mShortcutAndActionById.erase(shortcutAndActionById); + + IdsByShortcut::iterator idsByShortcut = mIdsByShortcut.find(shortcut); + if (idsByShortcut != mIdsByShortcut.end()) + { + idsByShortcut.value().remove(id); + if (idsByShortcut.value().isEmpty()) + { + mIdsByShortcut.erase(idsByShortcut); + + if (!remoteXUngrabKey(X11shortcut)) + { + log(LOG_WARNING, "Cannot ungrab shortcut '%s'", qPrintable(shortcut)); + } + } + } + + saveConfig(); + + result = true; +} + +void Core::deactivateClientAction(bool &result, const QDBusObjectPath &path, const QString &sender) +{ + log(LOG_INFO, "deactivateClientAction path:'%s' sender:'%s'", qPrintable(path.path()), qPrintable(sender)); + + QMutexLocker lock(&mDataMutex); + + IdByClientPath::iterator idByNativeClient = mIdByClientPath.find(path); + if (idByNativeClient == mIdByClientPath.end()) + { + log(LOG_WARNING, "No action registered for '%s' (sender: %s)", qPrintable(path.path()), qPrintable(sender)); + result = false; + return; + } + + SenderByClientPath::Iterator senderByClientPath = mSenderByClientPath.find(path); + if (senderByClientPath == mSenderByClientPath.end()) + { + log(LOG_WARNING, "No action activated for '%s' (sender: %s)", qPrintable(path.path()), qPrintable(sender)); + result = false; + return; + } + + if (senderByClientPath.value() != sender) + { + log(LOG_WARNING, "Sender mismatch: caller: %s owner: %s", qPrintable(senderByClientPath.value()), qPrintable(sender)); + result = false; + return; + } + + qulonglong id = idByNativeClient.value(); + + ShortcutAndActionById::iterator shortcutAndActionById = mShortcutAndActionById.find(id); + QString shortcut = shortcutAndActionById.value().first; + + dynamic_cast(shortcutAndActionById.value().second)->disappeared(); + + IdsByShortcut::iterator idsByShortcut = mIdsByShortcut.find(shortcut); + if (idsByShortcut != mIdsByShortcut.end()) + { + idsByShortcut.value().remove(id); + if (idsByShortcut.value().isEmpty()) + { + mIdsByShortcut.erase(idsByShortcut); + + if (!remoteXUngrabKey(mX11ByShortcut[shortcut])) + { + log(LOG_WARNING, "Cannot ungrab shortcut '%s'", qPrintable(shortcut)); + } + } + } + + mSenderByClientPath.remove(path); + + mClientPathsBySender[sender].remove(path); + if (mClientPathsBySender[sender].isEmpty()) + mClientPathsBySender.remove(sender); + + result = true; + + mDaemonAdaptor->emit_clientActionSenderChanged(id, QString()); +} + +void Core::setMultipleActionsBehaviour(const MultipleActionsBehaviour &behaviour) +{ + QMutexLocker lock(&mDataMutex); + + mMultipleActionsBehaviour = behaviour; + + saveConfig(); +} + +void Core::getMultipleActionsBehaviour(MultipleActionsBehaviour &result) const +{ + QMutexLocker lock(&mDataMutex); + + result = mMultipleActionsBehaviour; +} + +void Core::getAllActionIds(QList &result) const +{ + QMutexLocker lock(&mDataMutex); + + result.clear(); + result.reserve(mShortcutAndActionById.size()); + + ShortcutAndActionById::const_iterator lastShortcutAndActionById = mShortcutAndActionById.end(); + for (ShortcutAndActionById::const_iterator shortcutAndActionById = mShortcutAndActionById.begin(); shortcutAndActionById != lastShortcutAndActionById; ++shortcutAndActionById) + { + result.push_back(shortcutAndActionById.key()); + } +} + +GeneralActionInfo Core::actionInfo(const ShortcutAndAction &shortcutAndAction) const +{ + GeneralActionInfo result; + + result.shortcut = shortcutAndAction.first; + + const BaseAction *action = shortcutAndAction.second; + + result.description = action->description(); + result.enabled = action->isEnabled(); + + result.type = action->type(); + + if (result.type == ClientAction::id()) + { + const ClientAction *clientAction = dynamic_cast(action); + result.info = clientAction->path().path(); + } + else if (result.type == MethodAction::id()) + { + const MethodAction *methodAction = dynamic_cast(action); + result.info = methodAction->service() + " " + + methodAction->path().path() + " " + + methodAction->interface() + " " + + methodAction->method(); + } + else if (result.type == CommandAction::id()) + { + const CommandAction *commandAction = dynamic_cast(action); + result.info = joinCommandLine(commandAction->command(), commandAction->args()); + } + + return result; +} + +void Core::getActionById(QPair &result, const qulonglong &id) const +{ + log(LOG_INFO, "getActionById id:%llu", id); + + QMutexLocker lock(&mDataMutex); + + ShortcutAndActionById::const_iterator shortcutAndActionById = mShortcutAndActionById.find(id); + if (shortcutAndActionById == mShortcutAndActionById.end()) + { + log(LOG_WARNING, "No action registered with id #%llu", id); + result = qMakePair(false, GeneralActionInfo()); + return; + } + + result = qMakePair(true, actionInfo(shortcutAndActionById.value())); +} + +void Core::getAllActions(QMap &result) const +{ + QMutexLocker lock(&mDataMutex); + + result.clear(); + + ShortcutAndActionById::const_iterator lastShortcutAndActionById = mShortcutAndActionById.end(); + for (ShortcutAndActionById::const_iterator shortcutAndActionById = mShortcutAndActionById.begin(); shortcutAndActionById != lastShortcutAndActionById; ++shortcutAndActionById) + { + result[shortcutAndActionById.key()] = actionInfo(shortcutAndActionById.value()); + } +} + +void Core::getClientActionInfoById(QPair &result, const qulonglong &id) const +{ + log(LOG_INFO, "getClientActionInfoById id:%llu", id); + + ClientActionInfo info; + + QMutexLocker lock(&mDataMutex); + + ShortcutAndActionById::const_iterator shortcutAndActionById = mShortcutAndActionById.find(id); + if (shortcutAndActionById == mShortcutAndActionById.end()) + { + log(LOG_WARNING, "No action registered with id #%llu", id); + result = qMakePair(false, info); + return; + } + + const BaseAction *action = shortcutAndActionById.value().second; + + if (strcmp(action->type(), ClientAction::id())) + { + log(LOG_WARNING, "getClientActionInfoById attempts to request action of type '%s'", action->type()); + result = qMakePair(false, info); + return; + } + + info.shortcut = shortcutAndActionById.value().first; + info.description = action->description(); + info.enabled = action->isEnabled(); + + const ClientAction *clientAction = dynamic_cast(action); + info.path = clientAction->path(); + + result = qMakePair(true, info); +} + +void Core::getMethodActionInfoById(QPair &result, const qulonglong &id) const +{ + log(LOG_INFO, "getMethodActionInfoById id:%llu", id); + + MethodActionInfo info; + + QMutexLocker lock(&mDataMutex); + + ShortcutAndActionById::const_iterator shortcutAndActionById = mShortcutAndActionById.find(id); + if (shortcutAndActionById == mShortcutAndActionById.end()) + { + log(LOG_WARNING, "No action registered with id #%llu", id); + result = qMakePair(false, info); + return; + } + + const BaseAction *action = shortcutAndActionById.value().second; + + if (strcmp(action->type(), MethodAction::id())) + { + log(LOG_WARNING, "getMethodActionInfoById attempts to request action of type '%s'", action->type()); + result = qMakePair(false, info); + return; + } + + info.shortcut = shortcutAndActionById.value().first; + info.description = action->description(); + info.enabled = action->isEnabled(); + + const MethodAction *methodAction = dynamic_cast(action); + info.service = methodAction->service(); + info.path = methodAction->path(); + info.interface = methodAction->interface(); + info.method = methodAction->method(); + + result = qMakePair(true, info); +} + +void Core::getCommandActionInfoById(QPair &result, const qulonglong &id) const +{ + log(LOG_INFO, "getCommandActionInfoById id:%llu", id); + + CommandActionInfo info; + + QMutexLocker lock(&mDataMutex); + + ShortcutAndActionById::const_iterator shortcutAndActionById = mShortcutAndActionById.find(id); + if (shortcutAndActionById == mShortcutAndActionById.end()) + { + log(LOG_WARNING, "No action registered with id #%llu", id); + result = qMakePair(false, info); + return; + } + + const BaseAction *action = shortcutAndActionById.value().second; + + if (strcmp(action->type(), CommandAction::id())) + { + log(LOG_WARNING, "getCommandActionInfoById attempts to request action of type '%s'", action->type()); + result = qMakePair(false, info); + return; + } + + info.shortcut = shortcutAndActionById.value().first; + info.description = action->description(); + info.enabled = action->isEnabled(); + + const CommandAction *commandAction = dynamic_cast(action); + info.command = commandAction->command(); + info.arguments = commandAction->args(); + + result = qMakePair(true, info); +} + +void Core::grabShortcut(const uint &timeout, QString &/*shortcut*/, bool &failed, bool &cancelled, bool &timedout, const QDBusMessage &message) +{ + log(LOG_INFO, "grabShortcut timeout:%u", timeout); + + failed = false; + cancelled = false; + timedout = false; + + QMutexLocker lock(&mDataMutex); + + if (mGrabbingShortcut) + { + failed = true; + log(LOG_DEBUG, "grabShortcut failed: already grabbing"); + return; + } + + if ((timeout > 60000) || (timeout < 1000)) + { + timedout = true; + log(LOG_DEBUG, "grabShortcut wrong timedout"); + return; + } + + size_t X11Operation = X11_OP_XGrabKeyboard; + if (error_t error = writeAll(mX11RequestPipe[STDOUT_FILENO], &X11Operation, sizeof(X11Operation))) + { + log(LOG_CRIT, "Cannot write to X11 request pipe: %s", strerror(error)); + qApp->quit(); + return; + } + wakeX11Thread(); + + int x11result; + if (error_t error = readAll(mX11ResponsePipe[STDIN_FILENO], &x11result, sizeof(x11result))) + { + log(LOG_CRIT, "Cannot read from X11 response pipe: %s", strerror(error)); + qApp->quit(); + return; + } + if (x11result) + { + failed = true; + log(LOG_DEBUG, "grabShortcut failed: grab failed"); + return; + } + + mShortcutGrabRequested = true; + + mShortcutGrabTimeout->setInterval(timeout); + mShortcutGrabTimeout->start(); + + message.setDelayedReply(true); + mShortcutGrabRequest = message.createReply(); + + log(LOG_DEBUG, "grabShortcut delayed"); +} + +void Core::shortcutGrabbed() +{ + log(LOG_INFO, "shortcutGrabbed"); + + QString shortcut; + bool failed = false; + bool cancelled = false; + bool timedout = false; + + QMutexLocker lock(&mDataMutex); + + mShortcutGrabTimeout->stop(); + + if (!mShortcutGrabRequested) + { + return; + } + + if (error_t error = readAll(mX11ResponsePipe[STDIN_FILENO], &cancelled, sizeof(cancelled))) + { + log(LOG_CRIT, "Cannot read from X11 response pipe: %s", strerror(error)); + qApp->quit(); + return; + } + if (!cancelled) + { + size_t length; + if (error_t error = readAll(mX11ResponsePipe[STDIN_FILENO], &length, sizeof(length))) + { + log(LOG_CRIT, "Cannot read from X11 response pipe: %s", strerror(error)); + qApp->quit(); + return; + } + if (length) + { + char *str = new char[length + 1]; + str[length] = '\0'; + if (error_t error = readAll(mX11ResponsePipe[STDIN_FILENO], str, length)) + { + log(LOG_CRIT, "Cannot read from X11 response pipe: %s", strerror(error)); + qApp->quit(); + return; + } + shortcut = str; + + delete[] str; + } + } + + if (cancelled) + { + log(LOG_DEBUG, "grabShortcut: cancelled"); + } + else + { + log(LOG_DEBUG, "grabShortcut: shortcut:%s", qPrintable(shortcut)); + } + + mShortcutGrabRequest << shortcut << failed << cancelled << timedout; + QDBusConnection::sessionBus().send(mShortcutGrabRequest); + mShortcutGrabRequested = false; +} + +void Core::shortcutGrabTimedout() +{ + log(LOG_INFO, "shortcutGrabTimedout"); + + QString shortcut; + bool failed = false; + bool cancelled = false; + bool timedout = true; + + QMutexLocker lock(&mDataMutex); + + if (!mShortcutGrabRequested) + { + return; + } + + size_t X11Operation = X11_OP_XUngrabKeyboard; + if (error_t error = writeAll(mX11RequestPipe[STDOUT_FILENO], &X11Operation, sizeof(X11Operation))) + { + log(LOG_CRIT, "Cannot write to X11 request pipe: %s", strerror(error)); + qApp->quit(); + return; + } + wakeX11Thread(); + char signal; + if (error_t error = readAll(mX11ResponsePipe[STDIN_FILENO], &signal, sizeof(signal))) + { + log(LOG_CRIT, "Cannot read from X11 response pipe: %s", strerror(error)); + qApp->quit(); + return; + } + if (signal) + { + failed = true; + } + + log(LOG_DEBUG, "shortcutGrabTimedout: failed:%s", failed ? "true" : "false"); + + mShortcutGrabRequest << shortcut << failed << cancelled << timedout; + QDBusConnection::sessionBus().send(mShortcutGrabRequest); + mShortcutGrabRequested = false; +} + +void Core::cancelShortcutGrab() +{ + log(LOG_INFO, "cancelShortcutGrab"); + + QString shortcut; + bool failed = false; + bool cancelled = true; + bool timedout = false; + + QMutexLocker lock(&mDataMutex); + + if (!mGrabbingShortcut) + { + log(LOG_DEBUG, "cancelShortcutGrab failed: not grabbing"); + return; + } + + mShortcutGrabTimeout->stop(); + + if (!mShortcutGrabRequested) + { + return; + } + + size_t X11Operation = X11_OP_XUngrabKeyboard; + if (error_t error = writeAll(mX11RequestPipe[STDOUT_FILENO], &X11Operation, sizeof(X11Operation))) + { + log(LOG_CRIT, "Cannot write to X11 request pipe: %s", strerror(error)); + qApp->quit(); + return; + } + wakeX11Thread(); + char signal; + if (error_t error = readAll(mX11ResponsePipe[STDIN_FILENO], &signal, sizeof(signal))) + { + log(LOG_CRIT, "Cannot read from X11 response pipe: %s", strerror(error)); + qApp->quit(); + return; + } + if (signal) + { + failed = true; + } + + log(LOG_DEBUG, "cancelShortcutGrab: failed:%s", failed ? "true" : "false"); + + mShortcutGrabRequest << shortcut << failed << cancelled << timedout; + QDBusConnection::sessionBus().send(mShortcutGrabRequest); + mShortcutGrabRequested = false; +} diff --git a/daemon/core.h b/daemon/core.h new file mode 100644 index 0000000..baccee9 --- /dev/null +++ b/daemon/core.h @@ -0,0 +1,254 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_DAEMON__CORE__INCLUDED +#define GLOBAL_ACTION_DAEMON__CORE__INCLUDED + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "meta_types.h" +#include "log_target.h" + +extern "C" { +#include +#include +#include +#include +#undef Bool +} + + +class QTimer; +class DaemonAdaptor; +class NativeAdaptor; +class DBusProxy; +class BaseAction; + +template +class QOrderedSet : public QMap +{ +public: + typename QMap::iterator insert(const Key &akey) + { + return QMap::insert(akey, akey); + } +}; + +class Core : public QThread, public LogTarget +{ + Q_OBJECT +public: + Core(bool useSyslog, bool minLogLevelSet, int minLogLevel, const QStringList &configFiles, bool multipleActionsBehaviourSet, MultipleActionsBehaviour multipleActionsBehaviour, QObject *parent = 0); + ~Core(); + + bool ready() const { return mReady; } + + virtual void log(int level, const char *format, ...) const; + +signals: + void onShortcutGrabbed(); + +private: + Core(const Core &); + Core &operator = (const Core &); + +private: + typedef QPair X11Shortcut; + typedef QMap ShortcutByX11; + typedef QMap X11ByShortcut; + typedef QOrderedSet Ids; + typedef QMap IdsByShortcut; + typedef QDBusObjectPath ClientPath; + typedef QMap IdByClientPath; + typedef QPair ShortcutAndAction; + typedef QMap ShortcutAndActionById; + typedef QMap SenderByClientPath; + typedef QSet ClientPaths; + typedef QMap ClientPathsBySender; + +private slots: + void serviceOwnerChanged(const QString &name, const QString &oldOwner, const QString &newOwner); + void serviceDisappeared(const QString &sender); + + void addClientAction(QPair &result, const QString &shortcut, const QDBusObjectPath &path, const QString &description, const QString &sender); + void addMethodAction(QPair &result, const QString &shortcut, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description); + void addCommandAction(QPair &result, const QString &shortcut, const QString &command, const QStringList &arguments, const QString &description); + + void modifyClientAction(qulonglong &result, const QDBusObjectPath &path, const QString &description, const QString &sender); + void modifyActionDescription(bool &result, const qulonglong &id, const QString &description); + void modifyMethodAction(bool &result, const qulonglong &id, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description); + void modifyCommandAction(bool &result, const qulonglong &id, const QString &command, const QStringList &arguments, const QString &description); + + void enableClientAction(bool &result, const QDBusObjectPath &path, bool enabled, const QString &sender); + void isClientActionEnabled(bool &enabled, const QDBusObjectPath &path, const QString &sender); + void enableAction(bool &result, qulonglong id, bool enabled); + void isActionEnabled(bool &enabled, qulonglong id); + + void getClientActionSender(QString &sender, qulonglong id); + + + void changeClientActionShortcut(QPair &result, const QDBusObjectPath &path, const QString &shortcut, const QString &sender); + void changeShortcut(QString &result, const qulonglong &id, const QString &shortcut); + + void swapActions(bool &result, const qulonglong &id1, const qulonglong &id2); + + void removeClientAction(bool &result, const QDBusObjectPath &path, const QString &sender); + void removeAction(bool &result, const qulonglong &id); + + void deactivateClientAction(bool &result, const QDBusObjectPath &path, const QString &sender); + + void setMultipleActionsBehaviour(const MultipleActionsBehaviour &behaviour); + void getMultipleActionsBehaviour(MultipleActionsBehaviour &result) const; + + void getAllActionIds(QList &result) const; + void getActionById(QPair &result, const qulonglong &id) const; + void getAllActions(QMap &result) const; + + void getClientActionInfoById(QPair &result, const qulonglong &id) const; + void getMethodActionInfoById(QPair &result, const qulonglong &id) const; + void getCommandActionInfoById(QPair &result, const qulonglong &id) const; + + void grabShortcut(const uint &timeout, QString &shortcut, bool &failed, bool &cancelled, bool &timedout, const QDBusMessage &message); + void cancelShortcutGrab(); + + void shortcutGrabbed(); + void shortcutGrabTimedout(); + +private: + QPair addOrRegisterClientAction(const QString &shortcut, const QDBusObjectPath &path, const QString &description, const QString &sender); + qulonglong registerClientAction(const QString &shortcut, const QDBusObjectPath &path, const QString &description); + qulonglong registerMethodAction(const QString &shortcut, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description); + qulonglong registerCommandAction(const QString &shortcut, const QString &command, const QStringList &arguments, const QString &description); + + GeneralActionInfo actionInfo(const ShortcutAndAction &shortcutAndAction) const; + + friend void unixSignalHandler(int signalNumber); + void unixSignalHandler(int signalNumber); + + friend int x11ErrorHandler(Display *display, XErrorEvent *errorEvent); + int x11ErrorHandler(Display *display, XErrorEvent *errorEvent); + + X11Shortcut ShortcutToX11(const QString &shortcut); + QString X11ToShortcut(const X11Shortcut &X11shortcut); + + void wakeX11Thread(); + + void run(); + + KeyCode remoteStringToKeycode(const QString &str); + QString remoteKeycodeToString(KeyCode keyCode); + bool remoteXGrabKey(const X11Shortcut &X11shortcut); + bool remoteXUngrabKey(const X11Shortcut &X11shortcut); + + QString grabOrReuseKey(const X11Shortcut &X11shortcut, const QString &shortcut); + + QString checkShortcut(const QString &shortcut, X11Shortcut &X11shortcut); + + bool isEscape(KeySym keySym, unsigned int modifiers); + bool isModifier(KeySym keySym); + bool isAllowed(KeySym keySym, unsigned int modifiers); + + void saveConfig(); + + void lockX11Error(); + bool checkX11Error(int level = LOG_NOTICE, uint timeout = 10); + + bool waitForX11Error(int level, uint timeout); + +private: + bool mReady; + bool mUseSyslog; + + int mMinLogLevel; + + int mX11ErrorPipe[2]; + int mX11RequestPipe[2]; + int mX11ResponsePipe[2]; + Display *mDisplay; + Window mInterClientCommunicationWindow; + bool mX11EventLoopActive; + + mutable QMutex mX11ErrorMutex; + + QDBusConnection *mSessionConnection; + DaemonAdaptor *mDaemonAdaptor; + NativeAdaptor *mNativeAdaptor; + + mutable QMutex mDataMutex; + + qulonglong mLastId; + + bool mGrabbingShortcut; + + X11ByShortcut mX11ByShortcut; + ShortcutByX11 mShortcutByX11; + IdsByShortcut mIdsByShortcut; + ShortcutAndActionById mShortcutAndActionById; + IdByClientPath mIdByClientPath; + SenderByClientPath mSenderByClientPath; // add: path->sender + ClientPathsBySender mClientPathsBySender; // disappear: sender->[path] + + + unsigned int NumLockMask; + unsigned int ScrollLockMask; + unsigned int CapsLockMask; + unsigned int AltMask; + unsigned int MetaMask; + unsigned int Level3Mask; + unsigned int Level5Mask; + + MultipleActionsBehaviour mMultipleActionsBehaviour; + + bool mAllowGrabLocks; + bool mAllowGrabBaseSpecial; + bool mAllowGrabMiscSpecial; + bool mAllowGrabBaseKeypad; + bool mAllowGrabMiscKeypad; + bool mAllowGrabPrintable; + + QString mConfigFile; + bool mSaveAllowed; + + QTimer *mShortcutGrabTimeout; + QDBusMessage mShortcutGrabRequest; + bool mShortcutGrabRequested; + + bool mSuppressX11ErrorMessages; +}; + +#endif // GLOBAL_ACTION_DAEMON__CORE__INCLUDED diff --git a/daemon/daemon_adaptor.cpp b/daemon/daemon_adaptor.cpp new file mode 100644 index 0000000..fa229d4 --- /dev/null +++ b/daemon/daemon_adaptor.cpp @@ -0,0 +1,299 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "daemon_adaptor.h" + +#include "org.lxqt.global_key_shortcuts.daemon.h" + + +DaemonAdaptor::DaemonAdaptor(QObject *parent) + : QObject(parent) + , QDBusContext() +{ + new OrgLxqtGlobalActionDaemonAdaptor(this); +} + +QString DaemonAdaptor::addMethodAction(const QString &shortcut, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description, qulonglong &id) +{ + QPair result; + emit onAddMethodAction(result, shortcut, service, path, interface, method, description); + QString usedShortcut = result.first; + id = result.second; + if (id) + { + emit actionAdded(id); + } + return usedShortcut; +} + +QString DaemonAdaptor::addCommandAction(const QString &shortcut, const QString &command, const QStringList &arguments, const QString &description, qulonglong &id) +{ + QPair result; + emit onAddCommandAction(result, shortcut, command, arguments, description); + QString usedShortcut = result.first; + id = result.second; + if (id) + { + emit actionAdded(id); + } + return usedShortcut; +} + +bool DaemonAdaptor::modifyActionDescription(qulonglong id, const QString &description) +{ + bool result; + emit onModifyActionDescription(result, id, description); + if (result) + { + emit actionModified(id); + } + return result; +} + +bool DaemonAdaptor::modifyMethodAction(qulonglong id, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description) +{ + bool result; + emit onModifyMethodAction(result, id, service, path, interface, method, description); + if (result) + { + emit actionModified(id); + } + return result; +} + +bool DaemonAdaptor::modifyCommandAction(qulonglong id, const QString &command, const QStringList &arguments, const QString &description) +{ + bool result; + emit onModifyCommandAction(result, id, command, arguments, description); + if (result) + { + emit actionModified(id); + } + return result; +} + +bool DaemonAdaptor::enableAction(qulonglong id, bool enabled) +{ + bool result; + emit onEnableAction(result, id, enabled); + if (result) + { + emit actionEnabled(id, enabled); + } + return result; +} + +bool DaemonAdaptor::isActionEnabled(qulonglong id) +{ + bool enabled; + emit onIsActionEnabled(enabled, id); + return enabled; +} + +QString DaemonAdaptor::getClientActionSender(qulonglong id) +{ + QString sender; + emit onGetClientActionSender(sender, id); + return sender; +} + +QString DaemonAdaptor::changeShortcut(qulonglong id, const QString &shortcut) +{ + QString result; + emit onChangeShortcut(result, id, shortcut); + if (!result.isEmpty()) + { + emit actionShortcutChanged(id); + } + return result; +} + +bool DaemonAdaptor::swapActions(qulonglong id1, qulonglong id2) +{ + bool result; + emit onSwapActions(result, id1, id2); + if (result) + { + emit actionsSwapped(id1, id2); + } + return result; +} + +bool DaemonAdaptor::removeAction(qulonglong id) +{ + bool result; + emit onRemoveAction(result, id); + if (result) + { + emit actionRemoved(id); + } + return result; +} + +bool DaemonAdaptor::setMultipleActionsBehaviour(uint behaviour) +{ + if (behaviour >= MULTIPLE_ACTIONS_BEHAVIOUR__COUNT) + { + return false; + } + emit onSetMultipleActionsBehaviour(static_cast(behaviour)); + emit multipleActionsBehaviourChanged(behaviour); + return true; +} + +uint DaemonAdaptor::getMultipleActionsBehaviour() +{ + MultipleActionsBehaviour result; + emit onGetMultipleActionsBehaviour(result); + return result; +} + +QList DaemonAdaptor::getAllActionIds() +{ + QList result; + emit onGetAllActionIds(result); + return result; +} + +bool DaemonAdaptor::getActionById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QString &type, QString &info) +{ + QPair result; + emit onGetActionById(result, id); + bool success = result.first; + if (success) + { + shortcut = result.second.shortcut; + description = result.second.description; + enabled = result.second.enabled; + type = result.second.type; + info = result.second.info; + } + return success; +} + +QMap DaemonAdaptor::getAllActions() +{ + QMap result; + emit onGetAllActions(result); + return result; +} + +bool DaemonAdaptor::getClientActionInfoById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QDBusObjectPath &path) +{ + QPair result; + emit onGetClientActionInfoById(result, id); + bool success = result.first; + if (success) + { + shortcut = result.second.shortcut; + description = result.second.description; + enabled = result.second.enabled; + path = result.second.path; + } + return success; +} + +bool DaemonAdaptor::getMethodActionInfoById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QString &service, QDBusObjectPath &path, QString &interface, QString &method) +{ + QPair result; + emit onGetMethodActionInfoById(result, id); + bool success = result.first; + if (success) + { + shortcut = result.second.shortcut; + description = result.second.description; + enabled = result.second.enabled; + service = result.second.service; + path = result.second.path; + interface = result.second.interface; + method = result.second.method; + } + return success; +} + +bool DaemonAdaptor::getCommandActionInfoById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QString &command, QStringList &arguments) +{ + QPair result; + emit onGetCommandActionInfoById(result, id); + bool success = result.first; + if (success) + { + shortcut = result.second.shortcut; + description = result.second.description; + enabled = result.second.enabled; + command = result.second.command; + arguments = result.second.arguments; + } + return success; +} + +QString DaemonAdaptor::grabShortcut(uint timeout, bool &failed, bool &cancelled, bool &timedout) +{ + QString shortcut; + emit onGrabShortcut(timeout, shortcut, failed, cancelled, timedout, message()); + return shortcut; +} + +void DaemonAdaptor::cancelShortcutGrab() +{ + emit onCancelShortcutGrab(); +} + +void DaemonAdaptor::quit() +{ + emit onQuit(); +} + +void DaemonAdaptor::emit_actionAdded(qulonglong id) +{ + emit actionAdded(id); +} + +void DaemonAdaptor::emit_actionModified(qulonglong id) +{ + emit actionModified(id); +} + +void DaemonAdaptor::emit_actionRemoved(qulonglong id) +{ + emit actionRemoved(id); +} + +void DaemonAdaptor::emit_actionShortcutChanged(qulonglong id) +{ + emit actionShortcutChanged(id); +} + +void DaemonAdaptor::emit_actionEnabled(qulonglong id, bool enabled) +{ + emit actionEnabled(id, enabled); +} + +void DaemonAdaptor::emit_clientActionSenderChanged(qulonglong id, const QString &sender) +{ + emit clientActionSenderChanged(id, sender); +} diff --git a/daemon/daemon_adaptor.h b/daemon/daemon_adaptor.h new file mode 100644 index 0000000..f398d53 --- /dev/null +++ b/daemon/daemon_adaptor.h @@ -0,0 +1,135 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_DAEMON__DAEMON_ADAPTOR__INCLUDED +#define GLOBAL_ACTION_DAEMON__DAEMON_ADAPTOR__INCLUDED + + +#include +#include +#include +#include +#include +#include + +#include "meta_types.h" + + +class DaemonAdaptor : public QObject, protected QDBusContext +{ + Q_OBJECT +public: + DaemonAdaptor(QObject *parent = 0); + +public slots: + QString addMethodAction(const QString &shortcut, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description, qulonglong &id); + QString addCommandAction(const QString &shortcut, const QString &command, const QStringList &arguments, const QString &description, qulonglong &id); + + bool modifyActionDescription(qulonglong id, const QString &description); + bool modifyMethodAction(qulonglong id, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description); + bool modifyCommandAction(qulonglong id, const QString &command, const QStringList &arguments, const QString &description); + + bool enableAction(qulonglong id, bool enabled); + bool isActionEnabled(qulonglong id); + + QString getClientActionSender(qulonglong id); + + QString changeShortcut(qulonglong id, const QString &shortcut); + + bool swapActions(qulonglong id1, qulonglong id2); + + bool removeAction(qulonglong id); + + bool setMultipleActionsBehaviour(uint behaviour); + uint getMultipleActionsBehaviour(); + + QList getAllActionIds(); + QMap getAllActions(); + bool getActionById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QString &type, QString &info); + bool getClientActionInfoById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QDBusObjectPath &path); + bool getMethodActionInfoById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QString &service, QDBusObjectPath &path, QString &interface, QString &method); + bool getCommandActionInfoById(qulonglong id, QString &shortcut, QString &description, bool &enabled, QString &command, QStringList &arguments); + + QString grabShortcut(uint timeout, bool &failed, bool &cancelled, bool &timedout); + void cancelShortcutGrab(); + + void quit(); + + void emit_actionAdded(qulonglong id); + void emit_actionModified(qulonglong id); + void emit_actionRemoved(qulonglong id); + void emit_actionShortcutChanged(qulonglong id); + void emit_actionEnabled(qulonglong id, bool enabled); + void emit_clientActionSenderChanged(qulonglong id, const QString &sender); + +signals: + void actionAdded(qulonglong id); + void actionModified(qulonglong id); + void actionRemoved(qulonglong id); + void actionShortcutChanged(qulonglong id); + void actionEnabled(qulonglong id, bool enabled); + void clientActionSenderChanged(qulonglong id, const QString &sender); + void actionsSwapped(qulonglong id1, qulonglong id2); + void multipleActionsBehaviourChanged(uint behaviour); + +signals: + void onAddMethodAction(QPair &, const QString &, const QString &, const QDBusObjectPath &, const QString &, const QString &, const QString &); + void onAddCommandAction(QPair &, const QString &, const QString &, const QStringList &, const QString &); + + void onModifyActionDescription(bool &, qulonglong, const QString &); + void onModifyMethodAction(bool &, qulonglong, const QString &, const QDBusObjectPath &, const QString &, const QString &, const QString &); + void onModifyCommandAction(bool &, qulonglong, const QString &, const QStringList &, const QString &); + + void onEnableAction(bool &, qulonglong, bool); + void onIsActionEnabled(bool &, qulonglong); + + void onGetClientActionSender(QString &, qulonglong); + + void onChangeShortcut(QString &, qulonglong, const QString &); + + void onSwapActions(bool &, qulonglong, qulonglong); + + void onRemoveAction(bool &, qulonglong); + + void onSetMultipleActionsBehaviour(const MultipleActionsBehaviour &); + void onGetMultipleActionsBehaviour(MultipleActionsBehaviour &); + + void onGetAllActionIds(QList &); + void onGetActionById(QPair &, qulonglong); + void onGetAllActions(QMap &); + + void onGetClientActionInfoById(QPair &, qulonglong); + void onGetMethodActionInfoById(QPair &, qulonglong); + void onGetCommandActionInfoById(QPair &, qulonglong); + + void onGrabShortcut(uint, QString &, bool &, bool &, bool &, const QDBusMessage &); + void onCancelShortcutGrab(); + + void onQuit(); +}; + +#endif // GLOBAL_ACTION_DAEMON__DAEMON_ADAPTOR__INCLUDED diff --git a/daemon/fixed_timeout.h b/daemon/fixed_timeout.h new file mode 100644 index 0000000..2b52c5d --- /dev/null +++ b/daemon/fixed_timeout.h @@ -0,0 +1,50 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_DAEMON__FIXED_TIMEOUT__INCLUDED +#define GLOBAL_ACTION_DAEMON__FIXED_TIMEOUT__INCLUDED + + +#include "dbus_cpp.hpp" + +namespace DBus +{ + +class FixedTimeout : public DefaultTimeout +{ +public: + FixedTimeout(unsigned int interval, DefaultMainLoop *mainLoop); + + bool isExpired() const; + +private: + uint64_t mExpired; +}; + +} + +#endif // GLOBAL_ACTION_DAEMON__FIXED_TIMEOUT__INCLUDED diff --git a/daemon/log_target.cpp b/daemon/log_target.cpp new file mode 100644 index 0000000..6c4ed70 --- /dev/null +++ b/daemon/log_target.cpp @@ -0,0 +1,37 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "log_target.h" + + +LogTarget::LogTarget() +{ +} + +LogTarget::~LogTarget() +{ +} diff --git a/daemon/log_target.h b/daemon/log_target.h new file mode 100644 index 0000000..2a72a69 --- /dev/null +++ b/daemon/log_target.h @@ -0,0 +1,44 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_DAEMON__LOG_TARGET__INCLUDED +#define GLOBAL_ACTION_DAEMON__LOG_TARGET__INCLUDED + + +#include + + +class LogTarget +{ +public: + LogTarget(); + virtual ~LogTarget(); + + virtual void log(int level, const char *format, ...) const = 0; +}; + +#endif // GLOBAL_ACTION_DAEMON__LOG_TARGET__INCLUDED diff --git a/daemon/main.cpp b/daemon/main.cpp new file mode 100644 index 0000000..b15cce3 --- /dev/null +++ b/daemon/main.cpp @@ -0,0 +1,251 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include + +#include +#include + +#include "meta_types.h" +#include "core.h" + +#include +#include +#include +#include +#include +#include +#include +#include // for basename() + +#define DEFAULT_CONFIG ".config/lxqt/globalkeyshortcuts.conf" + + +int main(int argc, char *argv[]) +{ + bool wrongArgs = false; + bool printHelp = false; + bool runAsDaemon = false; + bool useSyslog = false; + bool minLogLevelSet = false; + int minLogLevel = LOG_NOTICE; + bool multipleActionsBehaviourSet = false; + MultipleActionsBehaviour multipleActionsBehaviour = MULTIPLE_ACTIONS_BEHAVIOUR_FIRST; + QStringList configFiles; + + static struct option longOptions[] = + { + {"no-daemon", no_argument, 0, 'n'}, + {"daemon", no_argument, 0, 'd'}, + {"use-syslog", no_argument, 0, 's'}, + {"log-level", required_argument, 0, 'l'}, + {"multiple-actions-behaviour", required_argument, 0, 'm'}, + {"config-file", required_argument, 0, 'f'}, + {"help", no_argument, 0, 'h'}, + {0, 0, 0, 0} + }; + + for (;;) + { + int optionIndex = 0; + + int c = getopt_long(argc, argv, "h?", longOptions, &optionIndex); + + if (c == -1) + { + break; + } + + switch (c) + { + case 'n': + runAsDaemon = false; + break; + + case 'd': + runAsDaemon = true; + break; + + case 's': + useSyslog = true; + break; + + case 'l': + if (!strcmp(optarg, "error")) + { + minLogLevel = LOG_ERR; + } + else if (!strcmp(optarg, "warning")) + { + minLogLevel = LOG_WARNING; + } + else if (!strcmp(optarg, "notice")) + { + minLogLevel = LOG_NOTICE; + } + else if (!strcmp(optarg, "info")) + { + minLogLevel = LOG_INFO; + } + else if (!strcmp(optarg, "debug")) + { + minLogLevel = LOG_DEBUG; + } + else + { + fprintf(stderr, "Invalid minimal log level: %s\n", optarg); + wrongArgs = true; + printHelp = true; + break; + } + minLogLevelSet = true; + break; + + case 'm': + if (!strcmp(optarg, "first")) + { + multipleActionsBehaviour = MULTIPLE_ACTIONS_BEHAVIOUR_FIRST; + } + else if (!strcmp(optarg, "last")) + { + multipleActionsBehaviour = MULTIPLE_ACTIONS_BEHAVIOUR_LAST; + } + else if (!strcmp(optarg, "all")) + { + multipleActionsBehaviour = MULTIPLE_ACTIONS_BEHAVIOUR_ALL; + } + else if (!strcmp(optarg, "none")) + { + multipleActionsBehaviour = MULTIPLE_ACTIONS_BEHAVIOUR_NONE; + } + else + { + fprintf(stderr, "Invalid multiple actions behaviour: %s\n", optarg); + wrongArgs = true; + printHelp = true; + break; + } + multipleActionsBehaviourSet = true; + break; + + case 'f': + configFiles.push_back(QString::fromLocal8Bit(optarg)); + break; + + case '?': + case 'h': + printHelp = true; + break; + + default: + wrongArgs = true; + printHelp = true; + } + } + + if (printHelp) + { + printf("Global key shortcuts daemon\n" + "\n" + "Version: " LXQT_VERSION "\n" + "License: GNU Lesser General Public License version 2.1 or later\n" + "Copyright: (c) 2013 Razor team\n" + "\n" + "Usage %s [OPTIONS]\n" + "\n" + "Possible options are:\n" + "\n" + " --no-daemon\n" + " Run as a usual application, not a daemon\n" + " and print messages to stderr.\n" + "\n" + " --daemon\n" + " Run as a daemon, not a usual application\n" + " and print messages to syslog.\n" + "\n" + " --use-syslog\n" + " Print messages to syslog if run as a usual application.\n" + "\n" + " --log-level=VALUE\n" + " Set minimal log level.\n" + " Possible values are:\n" + " error\n" + " warning\n" + " notice (default)\n" + " info\n" + " debug .\n" + "\n" + " --multiple-actions-behaviour=VALUE\n" + " Set the behaviour for the case of multiple actions\n" + " assigned to the same shortcut.\n" + " Possible values are:\n" + " first (default)\n" + " last\n" + " all\n" + " none .\n" + "\n" + " --config-file=FILENAME\n" + " Use config file FILENAME. Can be used several times.\n" + " The last loaded file is used to save settings.\n" + " Default is: ${HOME}/" DEFAULT_CONFIG "\n" + "\n" + " --help\n" + " -h\n" + " -?\n" + " This help.\n" + , basename(argv[0])); + return wrongArgs ? EXIT_FAILURE : EXIT_SUCCESS; + } + + if (runAsDaemon) + { + if (daemon(0, 0) < 0) + { + fprintf(stderr, "Cannot become a daemon: %s\n", strerror(errno)); + return EXIT_FAILURE; + } + } + + int ignoreIt = chdir("/"); + (void)ignoreIt; + + if (configFiles.empty()) + { + configFiles.push_back(QString::fromLocal8Bit(getenv("HOME")) + "/" DEFAULT_CONFIG); + } + + QCoreApplication app(argc, argv); + + Core core(runAsDaemon || useSyslog, minLogLevelSet, minLogLevel, configFiles, multipleActionsBehaviourSet, multipleActionsBehaviour); + + if (!core.ready()) + { + return EXIT_FAILURE; + } + + return app.exec(); +} diff --git a/daemon/meta_types.cpp b/daemon/meta_types.cpp new file mode 100644 index 0000000..cd25f39 --- /dev/null +++ b/daemon/meta_types.cpp @@ -0,0 +1,65 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "meta_types.h" + + +QDBusArgument &operator << (QDBusArgument &argument, const GeneralActionInfo &generalActionInfo) +{ + argument.beginStructure(); + argument << generalActionInfo.shortcut << generalActionInfo.description << generalActionInfo.enabled << generalActionInfo.type << generalActionInfo.info; + argument.endStructure(); + return argument; +} + +const QDBusArgument &operator >> (const QDBusArgument &argument, GeneralActionInfo &generalActionInfo) +{ + argument.beginStructure(); + argument >> generalActionInfo.shortcut >> generalActionInfo.description >> generalActionInfo.enabled >> generalActionInfo.type >> generalActionInfo.info; + argument.endStructure(); + return argument; +} + +namespace +{ + +class TypeRegistrator +{ +public: + TypeRegistrator() + { + qDBusRegisterMetaType(); + qDBusRegisterMetaType(); + } + + ~TypeRegistrator() + {} +}; + +static TypeRegistrator typeRegistrator; + +} diff --git a/daemon/meta_types.h b/daemon/meta_types.h new file mode 100644 index 0000000..5fd21b0 --- /dev/null +++ b/daemon/meta_types.h @@ -0,0 +1,87 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION__META_TYPES__INCLUDED +#define GLOBAL_ACTION__META_TYPES__INCLUDED + + +#include +#include + + +typedef enum MultipleActionsBehaviour +{ + MULTIPLE_ACTIONS_BEHAVIOUR_FIRST = 0, // queue + MULTIPLE_ACTIONS_BEHAVIOUR_LAST, // stack + MULTIPLE_ACTIONS_BEHAVIOUR_NONE, // qtcreator style + MULTIPLE_ACTIONS_BEHAVIOUR_ALL, // permissive behaviour + MULTIPLE_ACTIONS_BEHAVIOUR__COUNT +} MultipleActionsBehaviour; + +typedef struct CommonActionInfo +{ + QString shortcut; + QString description; + bool enabled; +} CommonActionInfo; + +typedef struct GeneralActionInfo : CommonActionInfo +{ + QString type; + QString info; +} GeneralActionInfo; + +typedef struct ClientActionInfo : CommonActionInfo +{ + QDBusObjectPath path; +} ClientActionInfo; + +typedef struct MethodActionInfo : CommonActionInfo +{ + QString service; + QDBusObjectPath path; + QString interface; + QString method; +} MethodActionInfo; + +typedef struct CommandActionInfo : CommonActionInfo +{ + QString command; + QStringList arguments; +} CommandActionInfo; + + +typedef QMap QMap_qulonglong_GeneralActionInfo; + +Q_DECLARE_METATYPE(GeneralActionInfo) +Q_DECLARE_METATYPE(QMap_qulonglong_GeneralActionInfo) + + +QDBusArgument &operator << (QDBusArgument &argument, const GeneralActionInfo &generalActionInfo); +const QDBusArgument &operator >> (const QDBusArgument &argument, GeneralActionInfo &generalActionInfo); + +#endif // GLOBAL_ACTION_MANAGER__META_TYPES__INCLUDED diff --git a/daemon/method_action.cpp b/daemon/method_action.cpp new file mode 100644 index 0000000..e495413 --- /dev/null +++ b/daemon/method_action.cpp @@ -0,0 +1,56 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "method_action.h" +#include "log_target.h" + + +MethodAction::MethodAction(LogTarget *logTarget, const QDBusConnection &connection, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description) + : BaseAction(logTarget, description) + , mConnection(connection) + , mService(service) + , mPath(path) + , mInterface(interface) + , mMethodName(method) +{ +} + +bool MethodAction::call() +{ + if (!isEnabled()) + { + return false; + } + + bool result = mConnection.call(QDBusMessage::createMethodCall(mService, mPath.path(), mInterface, mMethodName), QDBus::BlockWithGui).type() == QDBusMessage::ReplyMessage; + if (!result) + { + mLogTarget->log(LOG_WARNING, "Failed to call dbus method: service:'%s' path:'%s' interface:'%s' method:'%s'", qPrintable(mService), qPrintable(mPath.path()), qPrintable(mInterface), qPrintable(mMethodName)); + } + + return result; +} diff --git a/daemon/method_action.h b/daemon/method_action.h new file mode 100644 index 0000000..384af61 --- /dev/null +++ b/daemon/method_action.h @@ -0,0 +1,67 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_DAEMON__METHOD_ACTION__INCLUDED +#define GLOBAL_ACTION_DAEMON__METHOD_ACTION__INCLUDED + + +#include "base_action.h" + +#include +#include +#include +#include + + +class MethodAction : public BaseAction +{ +public: + MethodAction(LogTarget *logTarget, const QDBusConnection &connection, const QString &service, const QDBusObjectPath &path, const QString &interface, const QString &method, const QString &description); + + static const char *id() { return "method"; } + + virtual const char *type() const { return id(); } + + virtual bool call(); + + QString service() const { return mService; } + + QDBusObjectPath path() const { return mPath; } + + QString interface() const { return mInterface; } + + QString method() const { return mMethodName; } + +private: + QDBusConnection mConnection; + QString mService; + QDBusObjectPath mPath; + QString mInterface; + QString mMethodName; +}; + +#endif // GLOBAL_ACTION_DAEMON__METHOD_ACTION__INCLUDED diff --git a/daemon/mutex.cpp b/daemon/mutex.cpp new file mode 100644 index 0000000..8661ce2 --- /dev/null +++ b/daemon/mutex.cpp @@ -0,0 +1,41 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "mutex.hpp" + +Mutex::Mutex() +{ + mInitResult = pthread_mutex_init(&mMutex, NULL); +} + +Mutex::~Mutex() +{ + if (!mInitResult) + { + pthread_mutex_destroy(&mMutex); + } +} diff --git a/daemon/mutex.h b/daemon/mutex.h new file mode 100644 index 0000000..f3fec33 --- /dev/null +++ b/daemon/mutex.h @@ -0,0 +1,72 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_DAEMON__MUTEX__INCLUDED +#define GLOBAL_ACTION_DAEMON__MUTEX__INCLUDED + + +#include + + +class Mutex +{ +public: + Mutex(); + ~Mutex(); + + int initResult() const { return mInitResult; } + + int lock() { return pthread_mutex_lock(&mMutex); } + int tryLock() { return pthread_mutex_trylock(&mMutex); } + int unlock() { return pthread_mutex_unlock(&mMutex); } + +private: + int mInitResult; + pthread_mutex_t mMutex; +}; + +class Lock +{ +public: + Lock(Mutex &mutex) + : mMutex(mutex) + { + mutex.lock(); + } + + ~Lock() + { + mMutex.unlock(); + } + +private: + Mutex &mMutex; +}; + +#define LOCK(lock, mutex) Lock lock(mutex); (void)(lock) + +#endif // GLOBAL_ACTION_DAEMON__MUTEX__INCLUDED diff --git a/daemon/native_adaptor.cpp b/daemon/native_adaptor.cpp new file mode 100644 index 0000000..3969345 --- /dev/null +++ b/daemon/native_adaptor.cpp @@ -0,0 +1,102 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "native_adaptor.h" + +#include "org.lxqt.global_key_shortcuts.native.h" + + +NativeAdaptor::NativeAdaptor(QObject *parent) + : QObject(parent) + , QDBusContext() +{ + new OrgLxqtGlobalActionNativeAdaptor(this); +} + +QString NativeAdaptor::addClientAction(const QString &shortcut, const QDBusObjectPath &path, const QString &description, qulonglong &id) +{ + QPair result; + emit onAddClientAction(result, shortcut, path, description, calledFromDBus() ? message().service() : QString()); + QString usedShortcut = result.first; + id = result.second; + return usedShortcut; +} + +bool NativeAdaptor::modifyClientAction(const QDBusObjectPath &path, const QString &description) +{ + qulonglong result; + emit onModifyClientAction(result, path, description, calledFromDBus() ? message().service() : QString()); + return result; +} + +QString NativeAdaptor::changeClientActionShortcut(const QDBusObjectPath &path, const QString &shortcut) +{ + QPair result; + emit onChangeClientActionShortcut(result, path, shortcut, calledFromDBus() ? message().service() : QString()); + QString usedShortcut = result.first; + return usedShortcut; +} + +bool NativeAdaptor::removeClientAction(const QDBusObjectPath &path) +{ + bool result; + emit onRemoveClientAction(result, path, calledFromDBus() ? message().service() : QString()); + return result; +} + +bool NativeAdaptor::deactivateClientAction(const QDBusObjectPath &path) +{ + bool result; + emit onDeactivateClientAction(result, path, calledFromDBus() ? message().service() : QString()); + return result; +} + +bool NativeAdaptor::enableClientAction(const QDBusObjectPath &path, bool enabled) +{ + bool result; + emit onEnableClientAction(result, path, enabled, calledFromDBus() ? message().service() : QString()); + return result; +} + +bool NativeAdaptor::isClientActionEnabled(const QDBusObjectPath &path) +{ + bool enabled; + emit onIsClientActionEnabled(enabled, path, calledFromDBus() ? message().service() : QString()); + return enabled; +} + +QString NativeAdaptor::grabShortcut(uint timeout, bool &failed, bool &cancelled, bool &timedout) +{ + QString shortcut; + emit onGrabShortcut(timeout, shortcut, failed, cancelled, timedout, message()); + return shortcut; +} + +void NativeAdaptor::cancelShortcutGrab() +{ + emit onCancelShortcutGrab(); +} diff --git a/daemon/native_adaptor.h b/daemon/native_adaptor.h new file mode 100644 index 0000000..de726c8 --- /dev/null +++ b/daemon/native_adaptor.h @@ -0,0 +1,69 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_DAEMON__NATIVE_ADAPTOR__INCLUDED +#define GLOBAL_ACTION_DAEMON__NATIVE_ADAPTOR__INCLUDED + + +#include +#include +#include +#include + + +class NativeAdaptor : public QObject, protected QDBusContext +{ + Q_OBJECT +public: + NativeAdaptor(QObject *parent = 0); + + QString addClientAction(const QString &shortcut, const QDBusObjectPath &path, const QString &description, qulonglong &id); + bool modifyClientAction(const QDBusObjectPath &path, const QString &description); + QString changeClientActionShortcut(const QDBusObjectPath &path, const QString &shortcut); + bool removeClientAction(const QDBusObjectPath &path); + bool deactivateClientAction(const QDBusObjectPath &path); + bool enableClientAction(const QDBusObjectPath &path, bool enabled); + bool isClientActionEnabled(const QDBusObjectPath &path); + + QString grabShortcut(uint timeout, bool &failed, bool &cancelled, bool &timedout); + void cancelShortcutGrab(); + +signals: + void onAddClientAction(QPair &, const QString &, const QDBusObjectPath &, const QString &, const QString &); + void onModifyClientAction(qulonglong &, const QDBusObjectPath &, const QString &, const QString &); + void onChangeClientActionShortcut(QPair &, const QDBusObjectPath &, const QString &, const QString &); + void onRemoveClientAction(bool &, const QDBusObjectPath &, const QString &); + void onDeactivateClientAction(bool &, const QDBusObjectPath &, const QString &); + void onEnableClientAction(bool &, const QDBusObjectPath &, bool, const QString &); + void onIsClientActionEnabled(bool &, const QDBusObjectPath &, const QString &); + + void onGrabShortcut(uint, QString &, bool &, bool &, bool &, const QDBusMessage &); + void onCancelShortcutGrab(); + +}; + +#endif // GLOBAL_ACTION_DAEMON__NATIVE_ADAPTOR__INCLUDED diff --git a/daemon/org.lxqt.global_key_shortcuts.client.xml b/daemon/org.lxqt.global_key_shortcuts.client.xml new file mode 100644 index 0000000..b8df8f1 --- /dev/null +++ b/daemon/org.lxqt.global_key_shortcuts.client.xml @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/daemon/org.lxqt.global_key_shortcuts.daemon.xml b/daemon/org.lxqt.global_key_shortcuts.daemon.xml new file mode 100644 index 0000000..edeec7c --- /dev/null +++ b/daemon/org.lxqt.global_key_shortcuts.daemon.xml @@ -0,0 +1,174 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/daemon/org.lxqt.global_key_shortcuts.native.xml b/daemon/org.lxqt.global_key_shortcuts.native.xml new file mode 100644 index 0000000..a68c1ab --- /dev/null +++ b/daemon/org.lxqt.global_key_shortcuts.native.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/daemon/pipe_utils.cpp b/daemon/pipe_utils.cpp new file mode 100644 index 0000000..7ed0b8b --- /dev/null +++ b/daemon/pipe_utils.cpp @@ -0,0 +1,105 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "pipe_utils.h" + +#include +#include + + +void initBothPipeEnds(int fd[2]) +{ + fd[STDIN_FILENO] = -1; + fd[STDOUT_FILENO] = -1; +} + +error_t createPipe(int fd[2]) +{ + error_t result = 0; + if (pipe(fd) < 0) + { + result = errno; + } + if (!result) + { + fcntl(fd[STDIN_FILENO], F_SETFD, FD_CLOEXEC); + fcntl(fd[STDOUT_FILENO], F_SETFD, FD_CLOEXEC); + } + return result; +} + +error_t readAll(int fd, void *data, size_t length) +{ + while (length) + { + ssize_t bytes_read = read(fd, data, length); + if (bytes_read < 0) + { + return errno; + } + if (!bytes_read) + { + return -1; + } + data = reinterpret_cast(data) + bytes_read; + length -= bytes_read; + } + return 0; +} + +error_t writeAll(int fd, const void *data, size_t length) +{ + while (length) + { + ssize_t bytes_written = write(fd, data, length); + if (bytes_written < 0) + { + return errno; + } + if (!bytes_written) + { + return -1; + } + data = reinterpret_cast(data) + bytes_written; + length -= bytes_written; + } + return 0; +} + +void closeBothPipeEnds(int fd[2]) +{ + if (fd[STDIN_FILENO] != -1) + { + close(fd[STDIN_FILENO]); + fd[STDIN_FILENO] = -1; + } + if (fd[STDOUT_FILENO] != -1) + { + close(fd[STDOUT_FILENO]); + fd[STDOUT_FILENO] = -1; + } +} diff --git a/daemon/pipe_utils.h b/daemon/pipe_utils.h new file mode 100644 index 0000000..356d26c --- /dev/null +++ b/daemon/pipe_utils.h @@ -0,0 +1,48 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_DAEMON__PIPE_UTILS__INCLUDED +#define GLOBAL_ACTION_DAEMON__PIPE_UTILS__INCLUDED + +#include +#include + +#ifndef __error_t_defined +// FIXME: error_t seems to be GNU specific? +typedef int error_t; +#endif + +void initBothPipeEnds(int fd[2]); + +error_t createPipe(int fd[2]); + +error_t readAll(int fd, void *data, size_t length); +error_t writeAll(int fd, const void *data, size_t length); + +void closeBothPipeEnds(int fd[2]); + +#endif // GLOBAL_ACTION_DAEMON__PIPE_UTILS__INCLUDED diff --git a/daemon/string_utils.cpp b/daemon/string_utils.cpp new file mode 100644 index 0000000..61f73a7 --- /dev/null +++ b/daemon/string_utils.cpp @@ -0,0 +1,58 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "string_utils.h" + + +QString joinToString(const QStringList &list, const QString &prefix, const QString &joiner, const QString &postfix) +{ + QString result = list.join(joiner); + if (!result.isEmpty()) + { + result = prefix + result + postfix; + } + return result; +} + +QString joinCommandLine(const QString &command, QStringList arguments) +{ + arguments.prepend(command); + int m = arguments.length(); + for (int i = 0; i < m; ++i) + { + QString &item = arguments[i]; + if (item.contains(QRegExp("[ \r\n\t\"']"))) + { + item.prepend("'").append("'"); + } + else if (item.isEmpty()) + { + item = QString("''"); + } + } + return arguments.join(" "); +} diff --git a/daemon/string_utils.h b/daemon/string_utils.h new file mode 100644 index 0000000..e4a514f --- /dev/null +++ b/daemon/string_utils.h @@ -0,0 +1,40 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_ACTION_DAEMON__STRING_UTILS__INCLUDED +#define GLOBAL_ACTION_DAEMON__STRING_UTILS__INCLUDED + + +#include +#include + + +QString joinToString(const QStringList &list, const QString &prefix, const QString &joiner, const QString &postfix); + +QString joinCommandLine(const QString &command, QStringList arguments); + +#endif // GLOBAL_ACTION_DAEMON__STRING_UTILS__INCLUDED diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt new file mode 100644 index 0000000..02f6de8 --- /dev/null +++ b/ui/CMakeLists.txt @@ -0,0 +1,161 @@ +set(PROJECT_NAME ${LXQT_GLOBALKEYS_UI_LIBRARY_NAME}) +project(${PROJECT_NAME}) + +set(QT_DBUS_PREFIX "org.qtproject") + +set(LXQT_GLOBALKEYS_LIBRARY lxqt-globalkeys) +set(${PROJECT_NAME}_HEADER_NAMESPACE "LXQtGlobalKeysUi") + +include_directories( + "${PROJECT_SOURCE_DIR}" + "${CMAKE_CURRENT_BINARY_DIR}" + ${LXQT_INCLUDE_DIRS} +) + +add_definitions(-DSHARED_EXPORT=Q_DECL_EXPORT) + +set(${PROJECT_NAME}_SOURCES + shortcut_selector.cpp +) + +set(${PROJECT_NAME}_MAIN_HEADER +) + +# +# WARNING: Changing stuff here implies changing this: +#install(FILES ${${PROJECT_NAME}_PUBLIC_HEADERS} DESTINATION include/${PROJECT_NAME} COMPONENT development RENAME "shortcutselector.h") +# +set(${PROJECT_NAME}_PUBLIC_CPP_HEADERS + shortcut_selector.h +) + +set(${PROJECT_NAME}_PUBLIC_CLASSES + ShortcutSelector +) + +set(${PROJECT_NAME}_PRIVATE_CPP_HEADERS +) + +set(${PROJECT_NAME}_FORMS +) + +set(${PROJECT_NAME}_RESOURCES +) + +set(${PROJECT_NAME}_TRANSLATIONS +) + +set(${PROJECT_NAME}_DBUS_ADAPTORS +) + +set(${PROJECT_NAME}_DBUS_INTERFACES +) + +set(${PROJECT_NAME}_PUBLIC_HEADERS + ${${PROJECT_NAME}_PUBLIC_CPP_HEADERS} +) + +set(${PROJECT_NAME}_PRIVATE_HEADERS + ${${PROJECT_NAME}_PRIVATE_CPP_HEADERS} +) + +set(${PROJECT_NAME}_CPP_HEADERS + ${${PROJECT_NAME}_PUBLIC_CPP_HEADERS} + ${${PROJECT_NAME}_PRIVATE_CPP_HEADERS} +) + +set(${PROJECT_NAME}_HEADERS + ${${PROJECT_NAME}_PUBLIC_HEADERS} + ${${PROJECT_NAME}_PRIVATE_HEADERS} +) + +set(${PROJECT_NAME}_TRANSLATABLE + ${${PROJECT_NAME}_SOURCES} + ${${PROJECT_NAME}_HEADERS} + ${${PROJECT_NAME}_FORMS} +) + +foreach(DBUS_ADAPTOR ${${PROJECT_NAME}_DBUS_ADAPTORS}) + get_filename_component(DBUS_ADAPTOR_FILENAME ${DBUS_ADAPTOR} NAME) + configure_file( + ${DBUS_ADAPTOR} + "${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME}" + @ONLY + ) + get_source_file_property(DBUS_ADAPTOR_INCLUDE ${DBUS_ADAPTOR} INCLUDE) + get_source_file_property(DBUS_ADAPTOR_PARENT_CLASSNAME ${DBUS_ADAPTOR} PARENT_CLASSNAME) + get_source_file_property(DBUS_ADAPTOR_BASENAME ${DBUS_ADAPTOR} BASENAME) + get_source_file_property(DBUS_ADAPTOR_CLASSNAME ${DBUS_ADAPTOR} CLASSNAME) + if(DBUS_ADAPTOR_BASENAME) + if(DBUS_ADAPTOR_CLASSNAME) + qt5_add_dbus_adaptor(${PROJECT_NAME}_DBUS_ADAPTOR_FILES "${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME}" ${DBUS_ADAPTOR_INCLUDE} ${DBUS_ADAPTOR_PARENT_CLASSNAME} ${DBUS_ADAPTOR_BASENAME} ${DBUS_ADAPTOR_CLASSNAME}) + else() + qt5_add_dbus_adaptor(${PROJECT_NAME}_DBUS_ADAPTOR_FILES "${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME}" ${DBUS_ADAPTOR_INCLUDE} ${DBUS_ADAPTOR_PARENT_CLASSNAME} ${DBUS_ADAPTOR_BASENAME}) + endif() + else() + qt5_add_dbus_adaptor(${PROJECT_NAME}_DBUS_ADAPTOR_FILES "${CMAKE_CURRENT_BINARY_DIR}/${DBUS_ADAPTOR_FILENAME}" ${DBUS_ADAPTOR_INCLUDE} ${DBUS_ADAPTOR_PARENT_CLASSNAME}) + endif() +endforeach() + +foreach(DBUS_INTERFACE ${${PROJECT_NAME}_DBUS_INTERFACES}) + get_filename_component(DBUS_INTERFACE_FILENAME ${DBUS_INTERFACE} NAME) + configure_file( + ${DBUS_INTERFACE} + ${CMAKE_CURRENT_BINARY_DIR}/${DBUS_INTERFACE_FILENAME} + @ONLY + ) + get_source_file_property(DBUS_INTERFACE_BASENAME ${DBUS_INTERFACE} BASENAME) + get_source_file_property(DBUS_INTERFACE_INCLUDE ${DBUS_INTERFACE} INCLUDE) + get_source_file_property(DBUS_INTERFACE_CLASSNAME ${DBUS_INTERFACE} CLASSNAME) + get_source_file_property(DBUS_INTERFACE_NO_NAMESPACE ${DBUS_INTERFACE} NO_NAMESPACE) + set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${DBUS_INTERFACE_FILENAME} PROPERTIES + INCLUDE ${DBUS_INTERFACE_INCLUDE} + CLASSNAME ${DBUS_INTERFACE_CLASSNAME} + NO_NAMESPACE ${DBUS_INTERFACE_NO_NAMESPACE} + ) + qt5_add_dbus_interface(${PROJECT_NAME}_DBUS_INTERFACE_FILES ${CMAKE_CURRENT_BINARY_DIR}/${DBUS_INTERFACE_FILENAME} ${DBUS_INTERFACE_BASENAME}) +endforeach() + +qt5_add_resources(${PROJECT_NAME}_RESOURCE_FILES ${${PROJECT_NAME}_RESOURCES}) +qt5_wrap_ui(${PROJECT_NAME}_FORM_FILES ${${PROJECT_NAME}_FORMS}) + +set(${PROJECT_NAME}_GENERATED_FILES + ${${PROJECT_NAME}_FORM_FILES} + ${${PROJECT_NAME}_RESOURCE_FILES} + ${${PROJECT_NAME}_QM_FILES} + ${${PROJECT_NAME}_DBUS_INTERFACE_FILES} + ${${PROJECT_NAME}_DBUS_ADAPTOR_FILES} +) + +set(${PROJECT_NAME}_ALL_FILES + ${${PROJECT_NAME}_SOURCES} + ${${PROJECT_NAME}_HEADERS} + ${${PROJECT_NAME}_GENERATED_FILES} +) + +add_library(${PROJECT_NAME} SHARED ${${PROJECT_NAME}_ALL_FILES}) +target_link_libraries(${PROJECT_NAME} + Qt5::Widgets + Qt5::DBus + ${LXQT_GLOBALKEYS_LIBRARY} +) +set_target_properties(${PROJECT_NAME} + PROPERTIES + VERSION ${LXQT_VERSION} + SOVERSION ${LXQT_MAJOR_VERSION} +) + +include(create_portable_headers) +create_portable_headers(${PROJECT_NAME}_PORTABLE_HEADERS + NAMESPACE "${${PROJECT_NAME}_HEADER_NAMESPACE}/" + FILENAMES ${${PROJECT_NAME}_PUBLIC_CLASSES} +) + +install(TARGETS ${PROJECT_NAME} DESTINATION ${LIB_INSTALL_DIR} COMPONENT runtime) +install(FILES ${${PROJECT_NAME}_PUBLIC_HEADERS} DESTINATION include/${PROJECT_NAME} COMPONENT development) +install(FILES ${${PROJECT_NAME}_PUBLIC_HEADERS} DESTINATION include/${PROJECT_NAME} COMPONENT development RENAME "shortcutselector.h") +install(FILES ${${PROJECT_NAME}_PORTABLE_HEADERS} DESTINATION include/${PROJECT_NAME}/${${PROJECT_NAME}_HEADER_NAMESPACE} COMPONENT development) + + +include(create_pkgconfig_file) +create_pkgconfig_file(${PROJECT_NAME} "LXQt global key shortcuts GUI support library") diff --git a/ui/shortcut_selector.cpp b/ui/shortcut_selector.cpp new file mode 100644 index 0000000..ec1f7e9 --- /dev/null +++ b/ui/shortcut_selector.cpp @@ -0,0 +1,138 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#include "shortcut_selector.h" + +#include "../client/client.h" +#include "../client/action.h" + +#include +#include +#include + + +ShortcutSelector::ShortcutSelector(QWidget *parent) + : QToolButton(parent) + , mClient(GlobalKeyShortcut::Client::instance()) + , mShortcutTimer(new QTimer(this)) + , mAutoApplyShortcut(false) +{ + setCheckable(true); + setFocusPolicy(Qt::StrongFocus); + + mShortcutTimer->setInterval(1000); + mShortcutTimer->setSingleShot(false); + + connect(this, SIGNAL(clicked()), this, SLOT(grabShortcut())); + + connect(mShortcutTimer, SIGNAL(timeout()), this, SLOT(shortcutTimer_timeout())); + connect(mClient, SIGNAL(grabShortcutCancelled()), this, SLOT(grabShortcut_fail())); + connect(mClient, SIGNAL(grabShortcutTimedout()), this, SLOT(grabShortcut_fail())); + connect(mClient, SIGNAL(grabShortcutFailed()), this, SLOT(grabShortcut_fail())); + connect(mClient, SIGNAL(shortcutGrabbed(QString)), this, SLOT(newShortcutGrabbed(QString))); +} + +void ShortcutSelector::grabShortcut(int timeout) +{ + if (!isChecked()) + { + mClient->cancelShortcutGrab(); + return; + } + + mTimeoutCounter = timeout; + mOldShortcut = text(); + setText(QString::number(mTimeoutCounter)); + mShortcutTimer->start(); + mClient->grabShortcut(mTimeoutCounter * mShortcutTimer->interval()); +} + +void ShortcutSelector::shortcutTimer_timeout() +{ + if (mTimeoutCounter > 0) + { + --mTimeoutCounter; + setText(QString::number(mTimeoutCounter)); + } + else + setText(QString()); +} + +void ShortcutSelector::grabShortcut_fail() +{ + if (isChecked()) + { + setChecked(false); + mShortcutTimer->stop(); + setText(mOldShortcut); + emit shortcutGrabbed(mOldShortcut); + } +} + +void ShortcutSelector::newShortcutGrabbed(const QString &newShortcut) +{ + setChecked(false); + mShortcutTimer->stop(); + if (mAutoApplyShortcut) + setText(newShortcut); + else + setText(QString()); + emit shortcutGrabbed(newShortcut); +} + +void ShortcutSelector::clear() +{ + setText(QString()); +} + +QAction * ShortcutSelector::addMenuAction(const QString &title) +{ + QMenu *subMenu = menu(); + if (!subMenu) + { + setPopupMode(QToolButton::MenuButtonPopup); + subMenu = new QMenu(this); + setMenu(subMenu); + } + QAction *action = new QAction(title, subMenu); + subMenu->addAction(action); + return action; +} + +bool ShortcutSelector::isGrabbing() const +{ + return isChecked(); +} + +void ShortcutSelector::cancelNow() +{ + if (isChecked()) + { + grabShortcut_fail(); + mClient->cancelShortcutGrab(); + } +} diff --git a/ui/shortcut_selector.h b/ui/shortcut_selector.h new file mode 100644 index 0000000..7d71d99 --- /dev/null +++ b/ui/shortcut_selector.h @@ -0,0 +1,80 @@ +/* BEGIN_COMMON_COPYRIGHT_HEADER + * (c)LGPL2+ + * + * LXDE-Qt - a lightweight, Qt based, desktop toolset + * http://razor-qt.org + * + * Copyright: 2013 Razor team + * Authors: + * Kuzma Shapran + * + * This program or 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.1 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., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA + * + * END_COMMON_COPYRIGHT_HEADER */ + +#ifndef GLOBAL_KEY_SHORTCUT_SELECTOR__SHORTCUT_SELECTOR__INCLUDED +#define GLOBAL_KEY_SHORTCUT_SELECTOR__SHORTCUT_SELECTOR__INCLUDED + +#include +#include +#include + + +class QTimer; + +namespace GlobalKeyShortcut +{ +class Client; +} + +class ShortcutSelector : public QToolButton +{ + Q_OBJECT +public: + explicit ShortcutSelector(QWidget *parent = 0); + + QAction *addMenuAction(const QString &title); + + bool shortcutAutoApplied(void) const { return mAutoApplyShortcut; } + + bool isGrabbing() const; + +signals: + void shortcutGrabbed(const QString &); + +public slots: + void grabShortcut(int timeout = 10); + + void clear(); + + void autoApplyShortcut(bool value = true) { mAutoApplyShortcut = value; } + + void cancelNow(); + +private slots: + void shortcutTimer_timeout(); + void grabShortcut_fail(); + void newShortcutGrabbed(const QString &); + +private: + GlobalKeyShortcut::Client *mClient; + QString mOldShortcut; + int mTimeoutCounter; + QTimer *mShortcutTimer; + bool mAutoApplyShortcut; +}; + +#endif // GLOBAL_KEY_SHORTCUT_SELECTOR__SHORTCUT_SELECTOR__INCLUDED