├── AUTHORS ├── CHANGELOG ├── CMakeLists.txt ├── LICENSE ├── README.md └── src ├── CMakeLists.txt ├── lxqtplatformtheme.cpp ├── lxqtplatformtheme.h ├── lxqtplatformtheme.json ├── lxqtsystemtrayicon.cpp ├── lxqtsystemtrayicon.h ├── main.cpp └── statusnotifieritem ├── dbustypes.cpp ├── dbustypes.h ├── org.kde.StatusNotifierItem.xml ├── statusnotifieritem.cpp └── statusnotifieritem.h /AUTHORS: -------------------------------------------------------------------------------- 1 | Upstream Authors: 2 | LXQt team: https://lxqt-project.org 3 | Hong Jen Yee (PCMan) 4 | 5 | Copyright: 6 | Copyright (c) 2013-2017 LXQt team 7 | 8 | License: LGPL-2.1+ 9 | The full text of the licenses can be found in the 'COPYING' file. 10 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | lxqt-qtplugin-2.2.0 / 2025-04-17 2 | ================================= 3 | * Bumped the version to 2.2.0 and updated the dependencies. 4 | 5 | lxqt-qtplugin-2.1.0 / 2024-11-05 6 | ================================= 7 | * Use lxqt-build-tools functionality. 8 | * Remove a comment artifact. 9 | * Don't mix const with non const iterators. 10 | * Avoid possible detach in range-based loop. 11 | * Added tooltip colors to palette. 12 | * Set toolbar icon size. 13 | 14 | lxqt-qtplugin-2.0.0 / 2024-04-17 15 | ================================= 16 | * Ported to Qt6. 17 | * Check the existence of qApp. 18 | * Set mouse cursor theme and size. 19 | * Set hover effect. 20 | * Added link to libdbusmenu-lxqt in `README.md`. 21 | 22 | lxqt-qtplugin-1.4.0 / 2023-11-05 23 | ================================= 24 | * Just corrected a case of code readability and bumped the version. 25 | 26 | lxqt-qtplugin-1.3.0 / 2023-04-15 27 | ================================= 28 | * Bumped the version to 1.3.0 and updated the dependencies. 29 | 30 | lxqt-qtplugin-1.2.0 / 2022-11-05 31 | ================================= 32 | * Bumped the version to 1.2.0 and updated the dependencies. 33 | 34 | lxqt-qtplugin-1.1.0 / 2022-04-15 35 | ================================= 36 | * Just bumped the version to 1.1.0 and updated the dependencies. 37 | 38 | lxqt-qtplugin-1.0.0 / 2021-11-04 39 | ================================= 40 | * Added workarounds for Qt's poor handling of some dark palettes. 41 | * Used the C++11 override specifier. 42 | 43 | lxqt-qtplugin-0.17.0 / 2021-04-15 44 | ================================= 45 | * Handled Qt 5.15 deprecations. 46 | * Don't check before deleting a pointer. 47 | * Don't use implicit conversions. 48 | * Fixed a palette/highlight color related typo. 49 | 50 | lxqt-qtplugin-0.16.0 / 2020-11-01 51 | ================================= 52 | * Do not reset widget palettes on changing style. 53 | * Support more palette colors for better customization. 54 | 55 | lxqt-qtplugin-0.15.1 / 2020-05-31 56 | ================================= 57 | * Fixed Fusion's window color with Qt 5.15. 58 | * Made the window color configurable. 59 | 60 | lxqt-qtplugin-0.15.0 / 2020-04-22 61 | ================================= 62 | * Bumped version to 0.15.0. 63 | * Made libfm-qt a dependency and loaded versioned libfm-qt (for versioned ".so" handling). 64 | * C++11 code updates. 65 | * Use return braced init list. 66 | * Removed (duplicated) string casts definitions. 67 | * Removed deprecated QImage method "byteCount()" and used "sizeInBytes()" instead. 68 | * Fixed "#include" for libdbusmenu-qt. 69 | * Added support for flatpak to StatusNotifierItem. 70 | * Added Category property to StatusNotifierItem to fix working on some DEs. 71 | * Added support for working without context menu to StatusNotifierItem. 72 | 73 | lxqt-qtplugin-0.14.0 / 2019-01-25 74 | ================================= 75 | 76 | * Bumped version to 0.14.0 77 | * Dynamically load libfm-qt on demand to create 78 | the file dialog helper. 79 | * Don't use automatic string conversions 80 | * Improved cmake scripting 81 | - Set cmake_minimum_required to 3.1.0 82 | - Removed locale compile definitons 83 | - Removed the superfluous libfm-qt dependency 84 | 85 | lxqt-qtplugin-0.13.0 / 2018-05-21 86 | ================================= 87 | 88 | * CMake: Prevent in-source builds 89 | * fix http -> https 90 | * Fixed mentions of LXDE 91 | * lxqtplatformtheme: Initialize "folowColorScheme" once 92 | * Fix icon colorizing at startup 93 | * Drop Qt foreach. 94 | * Handle the new hint ShowShortcutsInContextMenus 95 | * Silent unused parameters warnings 96 | * Remember the view mode 97 | 98 | lxqt-qtplugin-0.12.0 / 2017-10-21 99 | ================================= 100 | 101 | * Release 0.12.0: Update changelog 102 | * Set an informal patch version 103 | * Don't export github templates 104 | * Flag unused vars in onServiceOwnerChanged 105 | * Fix missing variable declaration 106 | * Handle QML (as @PCMan suggested) 107 | * Use mime functions added by @PCMan 108 | * Loading and saving window/splitter size 109 | * Central positioning with respect to parent 110 | * Set window title correctly 111 | * Qt version conditions 112 | * Just made it compilable 113 | * Handle mime-type filters for file dialog. Set custom file dialog labels. 114 | * Update options properly. 115 | * Implement a basic file dialog helper using libfm-qt's Fm::FileDialog. 116 | * Avoid using Qt-specific keywords such as signal/slot and foreach. Instead, use macros. 117 | * LXQtPlatformTheme: Make Qt use default palette (#22) 118 | * LXQtPlatformTheme: Add icon FollowColorScheme config (#21) 119 | * Prevent a possible C++11 range for detach (#20) 120 | * Use the CMake MODULE library type. 121 | * LXQtPlatformTheme: Propagate wheelScrollLines 122 | * LXQtPlatformTheme: Do not provide own palette 123 | * Fix multiple StatusNotifierItems. 124 | * Bump year 125 | * Updates lxqt-build-tools required version 126 | 127 | lxqt-qtplugin-0.11.1 / 2016-12-11 128 | ================================= 129 | 130 | * Release 0.11.1: Update changelog 131 | * Use lxqt-build-tools 132 | * Use the new lxqt-build-tools package 133 | * Remove cpack (#14) 134 | 135 | lxqt-qtplugin-0.11.0 / 2016-09-24 136 | ================================= 137 | 138 | * Release 0.11.0: Add changelog 139 | * Add Qt5XdgIconLoader as dependency (#13) 140 | * Update README.md 141 | * statusnotifieritem: Correctly handle menu 142 | * Cleaned up licenses 143 | * Use XdgIconLoader library (#10) 144 | * xdgIconThemePaths: Use QString::SkipEmptyParts 145 | * xdgIconThemePaths: Remove duplicated paths 146 | * Adds $XDG_DATA_HOME to the XdgIconThemePaths 147 | * Bump year in AUTHORS 148 | 149 | lxqt-qtplugin-0.10.0 / 2015-11-02 150 | ================================= 151 | 152 | * Remove unnecessary qDebug calls 153 | * Use markdown for README 154 | * Fix license file 155 | * Fix mixed indents 156 | * Remove Qt >= 5.3 check 157 | * Removed GPL2, added Digia Qt LGPL Exception version 1.1 158 | * SNI: don't create a SNI if support isn't available 159 | * SNI: monitor the watcher in case the host restarts 160 | * SNI: implement scroll, showMessage and improve icons 161 | * SNI: create default context menu 162 | * Support the StatusNotifierItem spec natively 163 | * Use the standard palette of the current Qt theme 164 | * Handles CMake policy CMP0063 165 | * Use the LXQtCompilerSettings CMake module 166 | * Add .gitignore 167 | * Updates the build system to use the Targets infrastructure 168 | * Remove trailing whitespaces 169 | * removed debug (which makes every app too verbose) 170 | 171 | lxqt-qtplugin-0.9.0 / 2015-01-19 172 | ================================ 173 | 174 | * Try to workaround bug #441 - All LXQt::Settings and QSettings file change monitoring stop working. 175 | * - Unify naming for a unique lxqt. No more suffixes 176 | * Qt5: Use XDG_DATA_DIR for themeHint::IconThemeSearchPaths 177 | * CMakeLists.txt cleanuop and drop Qt4 support 178 | 179 | lxqt-qtplugin-0.8.0 / 2014-07-18 180 | ================================ 181 | 182 | * Apply the default application font on program startup. 183 | * Apply the new font to all apps correctly and fix lxde/lxde-qt bug #212 - Changing font or theme in lxqt-config-appearance has no effect. 184 | * Make the code compile with Qt 5.2, fix lxde-qt bug #207. 185 | * Fix lxde-qt bug #207: lxqt-qtplugin fail to build with Qt5. 186 | * Use qmake to query the path of Qt5 plugin dir, which is much more reliable. 187 | * Update README and provide information for usage and debugging. 188 | * Use a more reliable and portable way to get the path of Qt5 plugin dir. 189 | * Support setting default UI font. 190 | * Make "oxygen" the default icon theme name. * Apply new style name and icon theme name when the config file is changed. 191 | * Simplify the Qt5 plugin and remove liblxqt dependency from it. 192 | * Correctly read config values. 193 | * Try to load some Qt settings. 194 | * Implement a very basic Qt5 platform theme plugin. 195 | * Prepare for Qt5 support. 196 | * Use preferred header style. 197 | * Remove unnecessary debug messages. 198 | * Fix int-to-enum casting 199 | 200 | lxqt-qtplugin-0.7.0 / 2014-04-29 201 | ================================ 202 | 203 | * Update AUTHORS and COPYING 204 | * Add CPack rules for creating tarball 205 | * Support toolbar styles and activating items with single click. 206 | * Initial import. 207 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.18.0 FATAL_ERROR) 2 | # CMP0000: Call the cmake_minimum_required() command at the beginning of the top-level 3 | # CMakeLists.txt file even before calling the project() command. 4 | # The cmake_minimum_required(VERSION) command implicitly invokes the cmake_policy(VERSION) 5 | # command to specify that the current project code is written for the given range of CMake 6 | # versions. 7 | project(lxqt-qtplugin) 8 | 9 | include(GNUInstallDirs) 10 | 11 | set(CMAKE_AUTOMOC ON) 12 | set(CMAKE_INCLUDE_CURRENT_DIR ON) 13 | set(CMAKE_POSITION_INDEPENDENT_CODE ON) 14 | 15 | # Minimum Versions 16 | set(LXQTBT_MINIMUM_VERSION "2.2.0") 17 | set(QT_MINIMUM_VERSION "6.6.0") 18 | set(QTXDG_MINIMUM_VERSION "4.2.0") 19 | set(DBUSMENU_MINIMUM_VERSION "0.3.0") 20 | set(FMQT_MINIMUM_VERSION "2.2.0") 21 | 22 | find_package(Qt6DBus ${QT_MINIMUM_VERSION} REQUIRED) 23 | find_package(Qt6LinguistTools ${QT_MINIMUM_VERSION} REQUIRED) 24 | find_package(Qt6Widgets ${QT_MINIMUM_VERSION} REQUIRED) 25 | find_package(Qt6XdgIconLoader ${QTXDG_MINIMUM_VERSION} REQUIRED) 26 | find_package(lxqt2-build-tools ${LXQTBT_MINIMUM_VERSION} REQUIRED) 27 | find_package(dbusmenu-lxqt ${DBUSMENU_MINIMUM_VERSION} REQUIRED) 28 | find_package(fm-qt6 ${FMQT_MINIMUM_VERSION} REQUIRED) 29 | 30 | get_target_property(LIB_FM_QT_CONFIGURATIONS fm-qt6 IMPORTED_CONFIGURATIONS) 31 | if (LIB_FM_QT_CONFIGURATIONS) 32 | # Extract the .soname from the first configuration found. 33 | # We don't use configuration mapping. Any config serves the purpose 34 | list(GET LIB_FM_QT_CONFIGURATIONS 0 LIB_FM_QT_FIRST_CONFIGURATION) 35 | get_target_property(LIB_FM_QT_SONAME fm-qt6 IMPORTED_SONAME_${LIB_FM_QT_FIRST_CONFIGURATION}) 36 | else() 37 | message(ERROR "libfm-qt, but no configuration found. Check your libfm-qt installation.") 38 | endif() 39 | mark_as_advanced(LIB_FM_QT_SONAME) 40 | 41 | include(LXQtPreventInSourceBuilds) 42 | include(LXQtCompilerSettings NO_POLICY_SCOPE) 43 | include(LXQtQueryQt) 44 | 45 | add_subdirectory(src) 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 2.1, February 1999 3 | 4 | Copyright (C) 1991, 1999 Free Software Foundation, Inc. 5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 6 | Everyone is permitted to copy and distribute verbatim copies 7 | of this license document, but changing it is not allowed. 8 | 9 | [This is the first released version of the Lesser GPL. It also counts 10 | as the successor of the GNU Library Public License, version 2, hence 11 | the version number 2.1.] 12 | 13 | Preamble 14 | 15 | The licenses for most software are designed to take away your 16 | freedom to share and change it. By contrast, the GNU General Public 17 | Licenses are intended to guarantee your freedom to share and change 18 | free software--to make sure the software is free for all its users. 19 | 20 | This license, the Lesser General Public License, applies to some 21 | specially designated software packages--typically libraries--of the 22 | Free Software Foundation and other authors who decide to use it. You 23 | can use it too, but we suggest you first think carefully about whether 24 | this license or the ordinary General Public License is the better 25 | strategy to use in any particular case, based on the explanations below. 26 | 27 | When we speak of free software, we are referring to freedom of use, 28 | not price. Our General Public Licenses are designed to make sure that 29 | you have the freedom to distribute copies of free software (and charge 30 | for this service if you wish); that you receive source code or can get 31 | it if you want it; that you can change the software and use pieces of 32 | it in new free programs; and that you are informed that you can do 33 | these things. 34 | 35 | To protect your rights, we need to make restrictions that forbid 36 | distributors to deny you these rights or to ask you to surrender these 37 | rights. These restrictions translate to certain responsibilities for 38 | you if you distribute copies of the library or if you modify it. 39 | 40 | For example, if you distribute copies of the library, whether gratis 41 | or for a fee, you must give the recipients all the rights that we gave 42 | you. You must make sure that they, too, receive or can get the source 43 | code. If you link other code with the library, you must provide 44 | complete object files to the recipients, so that they can relink them 45 | with the library after making changes to the library and recompiling 46 | it. And you must show them these terms so they know their rights. 47 | 48 | We protect your rights with a two-step method: (1) we copyright the 49 | library, and (2) we offer you this license, which gives you legal 50 | permission to copy, distribute and/or modify the library. 51 | 52 | To protect each distributor, we want to make it very clear that 53 | there is no warranty for the free library. Also, if the library is 54 | modified by someone else and passed on, the recipients should know 55 | that what they have is not the original version, so that the original 56 | author's reputation will not be affected by problems that might be 57 | introduced by others. 58 | 59 | Finally, software patents pose a constant threat to the existence of 60 | any free program. We wish to make sure that a company cannot 61 | effectively restrict the users of a free program by obtaining a 62 | restrictive license from a patent holder. Therefore, we insist that 63 | any patent license obtained for a version of the library must be 64 | consistent with the full freedom of use specified in this license. 65 | 66 | Most GNU software, including some libraries, is covered by the 67 | ordinary GNU General Public License. This license, the GNU Lesser 68 | General Public License, applies to certain designated libraries, and 69 | is quite different from the ordinary General Public License. We use 70 | this license for certain libraries in order to permit linking those 71 | libraries into non-free programs. 72 | 73 | When a program is linked with a library, whether statically or using 74 | a shared library, the combination of the two is legally speaking a 75 | combined work, a derivative of the original library. The ordinary 76 | General Public License therefore permits such linking only if the 77 | entire combination fits its criteria of freedom. The Lesser General 78 | Public License permits more lax criteria for linking other code with 79 | the library. 80 | 81 | We call this license the "Lesser" General Public License because it 82 | does Less to protect the user's freedom than the ordinary General 83 | Public License. It also provides other free software developers Less 84 | of an advantage over competing non-free programs. These disadvantages 85 | are the reason we use the ordinary General Public License for many 86 | libraries. However, the Lesser license provides advantages in certain 87 | special circumstances. 88 | 89 | For example, on rare occasions, there may be a special need to 90 | encourage the widest possible use of a certain library, so that it becomes 91 | a de-facto standard. To achieve this, non-free programs must be 92 | allowed to use the library. A more frequent case is that a free 93 | library does the same job as widely used non-free libraries. In this 94 | case, there is little to gain by limiting the free library to free 95 | software only, so we use the Lesser General Public License. 96 | 97 | In other cases, permission to use a particular library in non-free 98 | programs enables a greater number of people to use a large body of 99 | free software. For example, permission to use the GNU C Library in 100 | non-free programs enables many more people to use the whole GNU 101 | operating system, as well as its variant, the GNU/Linux operating 102 | system. 103 | 104 | Although the Lesser General Public License is Less protective of the 105 | users' freedom, it does ensure that the user of a program that is 106 | linked with the Library has the freedom and the wherewithal to run 107 | that program using a modified version of the Library. 108 | 109 | The precise terms and conditions for copying, distribution and 110 | modification follow. Pay close attention to the difference between a 111 | "work based on the library" and a "work that uses the library". The 112 | former contains code derived from the library, whereas the latter must 113 | be combined with the library in order to run. 114 | 115 | GNU LESSER GENERAL PUBLIC LICENSE 116 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 117 | 118 | 0. This License Agreement applies to any software library or other 119 | program which contains a notice placed by the copyright holder or 120 | other authorized party saying it may be distributed under the terms of 121 | this Lesser General Public License (also called "this License"). 122 | Each licensee is addressed as "you". 123 | 124 | A "library" means a collection of software functions and/or data 125 | prepared so as to be conveniently linked with application programs 126 | (which use some of those functions and data) to form executables. 127 | 128 | The "Library", below, refers to any such software library or work 129 | which has been distributed under these terms. A "work based on the 130 | Library" means either the Library or any derivative work under 131 | copyright law: that is to say, a work containing the Library or a 132 | portion of it, either verbatim or with modifications and/or translated 133 | straightforwardly into another language. (Hereinafter, translation is 134 | included without limitation in the term "modification".) 135 | 136 | "Source code" for a work means the preferred form of the work for 137 | making modifications to it. For a library, complete source code means 138 | all the source code for all modules it contains, plus any associated 139 | interface definition files, plus the scripts used to control compilation 140 | and installation of the library. 141 | 142 | Activities other than copying, distribution and modification are not 143 | covered by this License; they are outside its scope. The act of 144 | running a program using the Library is not restricted, and output from 145 | such a program is covered only if its contents constitute a work based 146 | on the Library (independent of the use of the Library in a tool for 147 | writing it). Whether that is true depends on what the Library does 148 | and what the program that uses the Library does. 149 | 150 | 1. You may copy and distribute verbatim copies of the Library's 151 | complete source code as you receive it, in any medium, provided that 152 | you conspicuously and appropriately publish on each copy an 153 | appropriate copyright notice and disclaimer of warranty; keep intact 154 | all the notices that refer to this License and to the absence of any 155 | warranty; and distribute a copy of this License along with the 156 | Library. 157 | 158 | You may charge a fee for the physical act of transferring a copy, 159 | and you may at your option offer warranty protection in exchange for a 160 | fee. 161 | 162 | 2. You may modify your copy or copies of the Library or any portion 163 | of it, thus forming a work based on the Library, and copy and 164 | distribute such modifications or work under the terms of Section 1 165 | above, provided that you also meet all of these conditions: 166 | 167 | a) The modified work must itself be a software library. 168 | 169 | b) You must cause the files modified to carry prominent notices 170 | stating that you changed the files and the date of any change. 171 | 172 | c) You must cause the whole of the work to be licensed at no 173 | charge to all third parties under the terms of this License. 174 | 175 | d) If a facility in the modified Library refers to a function or a 176 | table of data to be supplied by an application program that uses 177 | the facility, other than as an argument passed when the facility 178 | is invoked, then you must make a good faith effort to ensure that, 179 | in the event an application does not supply such function or 180 | table, the facility still operates, and performs whatever part of 181 | its purpose remains meaningful. 182 | 183 | (For example, a function in a library to compute square roots has 184 | a purpose that is entirely well-defined independent of the 185 | application. Therefore, Subsection 2d requires that any 186 | application-supplied function or table used by this function must 187 | be optional: if the application does not supply it, the square 188 | root function must still compute square roots.) 189 | 190 | These requirements apply to the modified work as a whole. If 191 | identifiable sections of that work are not derived from the Library, 192 | and can be reasonably considered independent and separate works in 193 | themselves, then this License, and its terms, do not apply to those 194 | sections when you distribute them as separate works. But when you 195 | distribute the same sections as part of a whole which is a work based 196 | on the Library, the distribution of the whole must be on the terms of 197 | this License, whose permissions for other licensees extend to the 198 | entire whole, and thus to each and every part regardless of who wrote 199 | it. 200 | 201 | Thus, it is not the intent of this section to claim rights or contest 202 | your rights to work written entirely by you; rather, the intent is to 203 | exercise the right to control the distribution of derivative or 204 | collective works based on the Library. 205 | 206 | In addition, mere aggregation of another work not based on the Library 207 | with the Library (or with a work based on the Library) on a volume of 208 | a storage or distribution medium does not bring the other work under 209 | the scope of this License. 210 | 211 | 3. You may opt to apply the terms of the ordinary GNU General Public 212 | License instead of this License to a given copy of the Library. To do 213 | this, you must alter all the notices that refer to this License, so 214 | that they refer to the ordinary GNU General Public License, version 2, 215 | instead of to this License. (If a newer version than version 2 of the 216 | ordinary GNU General Public License has appeared, then you can specify 217 | that version instead if you wish.) Do not make any other change in 218 | these notices. 219 | 220 | Once this change is made in a given copy, it is irreversible for 221 | that copy, so the ordinary GNU General Public License applies to all 222 | subsequent copies and derivative works made from that copy. 223 | 224 | This option is useful when you wish to copy part of the code of 225 | the Library into a program that is not a library. 226 | 227 | 4. You may copy and distribute the Library (or a portion or 228 | derivative of it, under Section 2) in object code or executable form 229 | under the terms of Sections 1 and 2 above provided that you accompany 230 | it with the complete corresponding machine-readable source code, which 231 | must be distributed under the terms of Sections 1 and 2 above on a 232 | medium customarily used for software interchange. 233 | 234 | If distribution of object code is made by offering access to copy 235 | from a designated place, then offering equivalent access to copy the 236 | source code from the same place satisfies the requirement to 237 | distribute the source code, even though third parties are not 238 | compelled to copy the source along with the object code. 239 | 240 | 5. A program that contains no derivative of any portion of the 241 | Library, but is designed to work with the Library by being compiled or 242 | linked with it, is called a "work that uses the Library". Such a 243 | work, in isolation, is not a derivative work of the Library, and 244 | therefore falls outside the scope of this License. 245 | 246 | However, linking a "work that uses the Library" with the Library 247 | creates an executable that is a derivative of the Library (because it 248 | contains portions of the Library), rather than a "work that uses the 249 | library". The executable is therefore covered by this License. 250 | Section 6 states terms for distribution of such executables. 251 | 252 | When a "work that uses the Library" uses material from a header file 253 | that is part of the Library, the object code for the work may be a 254 | derivative work of the Library even though the source code is not. 255 | Whether this is true is especially significant if the work can be 256 | linked without the Library, or if the work is itself a library. The 257 | threshold for this to be true is not precisely defined by law. 258 | 259 | If such an object file uses only numerical parameters, data 260 | structure layouts and accessors, and small macros and small inline 261 | functions (ten lines or less in length), then the use of the object 262 | file is unrestricted, regardless of whether it is legally a derivative 263 | work. (Executables containing this object code plus portions of the 264 | Library will still fall under Section 6.) 265 | 266 | Otherwise, if the work is a derivative of the Library, you may 267 | distribute the object code for the work under the terms of Section 6. 268 | Any executables containing that work also fall under Section 6, 269 | whether or not they are linked directly with the Library itself. 270 | 271 | 6. As an exception to the Sections above, you may also combine or 272 | link a "work that uses the Library" with the Library to produce a 273 | work containing portions of the Library, and distribute that work 274 | under terms of your choice, provided that the terms permit 275 | modification of the work for the customer's own use and reverse 276 | engineering for debugging such modifications. 277 | 278 | You must give prominent notice with each copy of the work that the 279 | Library is used in it and that the Library and its use are covered by 280 | this License. You must supply a copy of this License. If the work 281 | during execution displays copyright notices, you must include the 282 | copyright notice for the Library among them, as well as a reference 283 | directing the user to the copy of this License. Also, you must do one 284 | of these things: 285 | 286 | a) Accompany the work with the complete corresponding 287 | machine-readable source code for the Library including whatever 288 | changes were used in the work (which must be distributed under 289 | Sections 1 and 2 above); and, if the work is an executable linked 290 | with the Library, with the complete machine-readable "work that 291 | uses the Library", as object code and/or source code, so that the 292 | user can modify the Library and then relink to produce a modified 293 | executable containing the modified Library. (It is understood 294 | that the user who changes the contents of definitions files in the 295 | Library will not necessarily be able to recompile the application 296 | to use the modified definitions.) 297 | 298 | b) Use a suitable shared library mechanism for linking with the 299 | Library. A suitable mechanism is one that (1) uses at run time a 300 | copy of the library already present on the user's computer system, 301 | rather than copying library functions into the executable, and (2) 302 | will operate properly with a modified version of the library, if 303 | the user installs one, as long as the modified version is 304 | interface-compatible with the version that the work was made with. 305 | 306 | c) Accompany the work with a written offer, valid for at 307 | least three years, to give the same user the materials 308 | specified in Subsection 6a, above, for a charge no more 309 | than the cost of performing this distribution. 310 | 311 | d) If distribution of the work is made by offering access to copy 312 | from a designated place, offer equivalent access to copy the above 313 | specified materials from the same place. 314 | 315 | e) Verify that the user has already received a copy of these 316 | materials or that you have already sent this user a copy. 317 | 318 | For an executable, the required form of the "work that uses the 319 | Library" must include any data and utility programs needed for 320 | reproducing the executable from it. However, as a special exception, 321 | the materials to be distributed need not include anything that is 322 | normally distributed (in either source or binary form) with the major 323 | components (compiler, kernel, and so on) of the operating system on 324 | which the executable runs, unless that component itself accompanies 325 | the executable. 326 | 327 | It may happen that this requirement contradicts the license 328 | restrictions of other proprietary libraries that do not normally 329 | accompany the operating system. Such a contradiction means you cannot 330 | use both them and the Library together in an executable that you 331 | distribute. 332 | 333 | 7. You may place library facilities that are a work based on the 334 | Library side-by-side in a single library together with other library 335 | facilities not covered by this License, and distribute such a combined 336 | library, provided that the separate distribution of the work based on 337 | the Library and of the other library facilities is otherwise 338 | permitted, and provided that you do these two things: 339 | 340 | a) Accompany the combined library with a copy of the same work 341 | based on the Library, uncombined with any other library 342 | facilities. This must be distributed under the terms of the 343 | Sections above. 344 | 345 | b) Give prominent notice with the combined library of the fact 346 | that part of it is a work based on the Library, and explaining 347 | where to find the accompanying uncombined form of the same work. 348 | 349 | 8. You may not copy, modify, sublicense, link with, or distribute 350 | the Library except as expressly provided under this License. Any 351 | attempt otherwise to copy, modify, sublicense, link with, or 352 | distribute the Library is void, and will automatically terminate your 353 | rights under this License. However, parties who have received copies, 354 | or rights, from you under this License will not have their licenses 355 | terminated so long as such parties remain in full compliance. 356 | 357 | 9. You are not required to accept this License, since you have not 358 | signed it. However, nothing else grants you permission to modify or 359 | distribute the Library or its derivative works. These actions are 360 | prohibited by law if you do not accept this License. Therefore, by 361 | modifying or distributing the Library (or any work based on the 362 | Library), you indicate your acceptance of this License to do so, and 363 | all its terms and conditions for copying, distributing or modifying 364 | the Library or works based on it. 365 | 366 | 10. Each time you redistribute the Library (or any work based on the 367 | Library), the recipient automatically receives a license from the 368 | original licensor to copy, distribute, link with or modify the Library 369 | subject to these terms and conditions. You may not impose any further 370 | restrictions on the recipients' exercise of the rights granted herein. 371 | You are not responsible for enforcing compliance by third parties with 372 | this License. 373 | 374 | 11. If, as a consequence of a court judgment or allegation of patent 375 | infringement or for any other reason (not limited to patent issues), 376 | conditions are imposed on you (whether by court order, agreement or 377 | otherwise) that contradict the conditions of this License, they do not 378 | excuse you from the conditions of this License. If you cannot 379 | distribute so as to satisfy simultaneously your obligations under this 380 | License and any other pertinent obligations, then as a consequence you 381 | may not distribute the Library at all. For example, if a patent 382 | license would not permit royalty-free redistribution of the Library by 383 | all those who receive copies directly or indirectly through you, then 384 | the only way you could satisfy both it and this License would be to 385 | refrain entirely from distribution of the Library. 386 | 387 | If any portion of this section is held invalid or unenforceable under any 388 | particular circumstance, the balance of the section is intended to apply, 389 | and the section as a whole is intended to apply in other circumstances. 390 | 391 | It is not the purpose of this section to induce you to infringe any 392 | patents or other property right claims or to contest validity of any 393 | such claims; this section has the sole purpose of protecting the 394 | integrity of the free software distribution system which is 395 | implemented by public license practices. Many people have made 396 | generous contributions to the wide range of software distributed 397 | through that system in reliance on consistent application of that 398 | system; it is up to the author/donor to decide if he or she is willing 399 | to distribute software through any other system and a licensee cannot 400 | impose that choice. 401 | 402 | This section is intended to make thoroughly clear what is believed to 403 | be a consequence of the rest of this License. 404 | 405 | 12. If the distribution and/or use of the Library is restricted in 406 | certain countries either by patents or by copyrighted interfaces, the 407 | original copyright holder who places the Library under this License may add 408 | an explicit geographical distribution limitation excluding those countries, 409 | so that distribution is permitted only in or among countries not thus 410 | excluded. In such case, this License incorporates the limitation as if 411 | written in the body of this License. 412 | 413 | 13. The Free Software Foundation may publish revised and/or new 414 | versions of the Lesser General Public License from time to time. 415 | Such new versions will be similar in spirit to the present version, 416 | but may differ in detail to address new problems or concerns. 417 | 418 | Each version is given a distinguishing version number. If the Library 419 | specifies a version number of this License which applies to it and 420 | "any later version", you have the option of following the terms and 421 | conditions either of that version or of any later version published by 422 | the Free Software Foundation. If the Library does not specify a 423 | license version number, you may choose any version ever published by 424 | the Free Software Foundation. 425 | 426 | 14. If you wish to incorporate parts of the Library into other free 427 | programs whose distribution conditions are incompatible with these, 428 | write to the author to ask for permission. For software which is 429 | copyrighted by the Free Software Foundation, write to the Free 430 | Software Foundation; we sometimes make exceptions for this. Our 431 | decision will be guided by the two goals of preserving the free status 432 | of all derivatives of our free software and of promoting the sharing 433 | and reuse of software generally. 434 | 435 | NO WARRANTY 436 | 437 | 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO 438 | WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. 439 | EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR 440 | OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY 441 | KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE 442 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 443 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE 444 | LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME 445 | THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 446 | 447 | 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN 448 | WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY 449 | AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU 450 | FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR 451 | CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE 452 | LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING 453 | RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A 454 | FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF 455 | SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 456 | DAMAGES. 457 | 458 | END OF TERMS AND CONDITIONS 459 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lxqt-qtplugin 2 | 3 | ## Overview 4 | 5 | This repository provides the library `libqtlxqt` to integrate Qt with LXQt. 6 | 7 | With this plugin, all Qt-based programs can adopt settings of LXQt, such as the icon theme and Qt style. 8 | 9 | ## Installation 10 | 11 | ### Compiling source code 12 | 13 | Runtime dependencies are [libdbusmenu-lxqt](https://github.com/lxqt/libdbusmenu-lxqt), [libfm-qt](https://github.com/lxqt/libfm-qt) and [libqtxdg](https://github.com/lxqt/libqtxdg). 14 | Additional build dependencies are CMake, [lxqt-build-tools](https://github.com/lxqt/lxqt-build-tools), qt6-tools and optionally Git to pull latest VCS checkouts. 15 | 16 | Code configuration is handled by CMake. CMake variable `CMAKE_INSTALL_PREFIX` has to be set to `/usr` on most operating systems. 17 | 18 | To build run `make`, to install `make install` which accepts variable `DESTDIR` as usual. 19 | 20 | ### Binary packages 21 | 22 | Official binary packages are provided by all major Linux distributions like Arch Linux, Debian, Fedora and openSUSE. Just use your package manager to search for string `lxqt-qtplugin`. 23 | 24 | ## Configuration, Usage 25 | 26 | To use the plugin in Qt, the environment variable `QT_QPA_PLATFORMTHEME=lxqt` has to be exported, such that every Qt application can load the theme plugin. 27 | If, for some unknown reasons, the plugin is not loaded, we can debug the plugin by exporting `QT_DEBUG_PLUGINS=1`. Qt will print detailed information and error messages about all plugins in the console when running any Qt program. 28 | 29 | For Qt5 support please see the [wiki](https://github.com/lxqt/lxqt/wiki/Building-from-source#qt5-support). 30 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories( 2 | ${Qt6Gui_PRIVATE_INCLUDE_DIRS} 3 | "${CMAKE_CURRENT_BINARY_DIR}" 4 | ) 5 | 6 | set(qtlxqt_HDRS 7 | lxqtplatformtheme.h 8 | lxqtsystemtrayicon.h 9 | statusnotifieritem/statusnotifieritem.h 10 | statusnotifieritem/dbustypes.h 11 | ) 12 | 13 | set(qtlxqt_SRCS 14 | main.cpp 15 | lxqtplatformtheme.cpp 16 | lxqtsystemtrayicon.cpp 17 | statusnotifieritem/statusnotifieritem.cpp 18 | statusnotifieritem/dbustypes.cpp 19 | ) 20 | 21 | qt6_add_dbus_adaptor(qtlxqt_SRCS 22 | statusnotifieritem/org.kde.StatusNotifierItem.xml 23 | statusnotifieritem/statusnotifieritem.h 24 | StatusNotifierItem 25 | ) 26 | 27 | add_library(qtlxqt MODULE ${qtlxqt_HDRS} ${qtlxqt_SRCS}) 28 | 29 | target_compile_definitions(qtlxqt 30 | PRIVATE 31 | "QT_NO_FOREACH" 32 | "LIB_FM_QT_SONAME=\"${LIB_FM_QT_SONAME}\"" 33 | ) 34 | 35 | target_link_libraries(qtlxqt 36 | Qt6::Widgets 37 | Qt6::DBus 38 | dbusmenu-lxqt 39 | Qt6XdgIconLoader 40 | ) 41 | 42 | lxqt_query_qt(_QT_PLUGINS_DIR QT_INSTALL_PLUGINS) 43 | 44 | if (NOT _QT_PLUGINS_DIR) 45 | message(FATAL_ERROR "Qt6 plugin directory cannot be detected.") 46 | endif() 47 | set(QT_PLUGINS_DIR "${_QT_PLUGINS_DIR}") 48 | message(STATUS "Qt6 plugin directory:" "${QT_PLUGINS_DIR}") 49 | 50 | install(TARGETS qtlxqt LIBRARY DESTINATION "${QT_PLUGINS_DIR}/platformthemes") 51 | -------------------------------------------------------------------------------- /src/lxqtplatformtheme.cpp: -------------------------------------------------------------------------------- 1 | /* BEGIN_COMMON_COPYRIGHT_HEADER 2 | * (c)LGPL2+ 3 | * 4 | * LXQt - a lightweight, Qt based, desktop toolset 5 | * https://lxqt.org/ 6 | * 7 | * Copyright: 2014 LXQt team 8 | * Authors: 9 | * Hong Jen Yee (PCMan) 10 | * 11 | * This program or library is free software; you can redistribute it 12 | * and/or modify it under the terms of the GNU Lesser General Public 13 | * License as published by the Free Software Foundation; either 14 | * version 2.1 of the License, or (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | * Lesser General Public License for more details. 20 | 21 | * You should have received a copy of the GNU Lesser General 22 | * Public License along with this library; if not, write to the 23 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 | * Boston, MA 02110-1301 USA 25 | * 26 | * END_COMMON_COPYRIGHT_HEADER */ 27 | 28 | #include "lxqtplatformtheme.h" 29 | #include 30 | #include 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | #include 51 | 52 | 53 | // Function to create a new Fm::FileDialogHelper object. 54 | // This is dynamically loaded at runtime on demand from libfm-qt. 55 | typedef QPlatformDialogHelper* (*CreateFileDialogHelperFunc)(); 56 | static CreateFileDialogHelperFunc createFileDialogHelper = nullptr; 57 | 58 | 59 | LXQtPlatformTheme::LXQtPlatformTheme(): 60 | iconFollowColorScheme_(true) 61 | , settingsWatcher_(nullptr) 62 | , LXQtPalette_(nullptr) 63 | { 64 | loadSettings(); 65 | // Note: When the plugin is loaded, it seems that the app is not yet running and 66 | // QThread environment is not completely set up. So creating filesystem watcher 67 | // does not work since it uses QSocketNotifier internally which can only be 68 | // created within QThread thread. So let's schedule a idle handler to initialize 69 | // the watcher in the main event loop. 70 | 71 | // Note2: the QTimer::singleShot with also needs a QThread environment 72 | // (there is a workaround in qtcore for the 0-timer with the old SLOT notation) 73 | 74 | // TODO: use the "PointerToMemberFunction" overloads of invokeMethod(), but they 75 | // are available from Qt v5.10 76 | //QMetaObject::ivokeMethod(this, &LXQtPlatformTheme::lazyInit, Qt::QueuedConnection); 77 | QMetaObject::invokeMethod(this, "lazyInit", Qt::QueuedConnection); 78 | } 79 | 80 | LXQtPlatformTheme::~LXQtPlatformTheme() { 81 | delete LXQtPalette_; 82 | delete settingsWatcher_; 83 | } 84 | 85 | void LXQtPlatformTheme::lazyInit() 86 | { 87 | settingsWatcher_ = new QFileSystemWatcher(); 88 | settingsWatcher_->addPath(settingsFile_); 89 | connect(settingsWatcher_, &QFileSystemWatcher::fileChanged, this, &LXQtPlatformTheme::onSettingsChanged); 90 | 91 | XdgIconLoader::instance()->setFollowColorScheme(iconFollowColorScheme_); 92 | } 93 | 94 | void LXQtPlatformTheme::loadSettings() { 95 | // QSettings is really handy. It tries to read from /etc/xdg/lxqt/lxqt.conf 96 | // as a fallback if a key is missing from the user config file ~/.config/lxqt/lxqt.conf. 97 | // So we can customize the default values in /etc/xdg/lxqt/lxqt.conf and does 98 | // not necessarily to hard code the default values here. 99 | QSettings settings(QSettings::UserScope, QLatin1String("lxqt"), QLatin1String("lxqt")); 100 | settingsFile_ = settings.fileName(); 101 | 102 | // icon theme 103 | iconTheme_ = settings.value(QLatin1String("icon_theme"), QLatin1String("oxygen")).toString(); 104 | iconFollowColorScheme_ = settings.value(QLatin1String("icon_follow_color_scheme"), iconFollowColorScheme_).toBool(); 105 | 106 | // read other widget related settings form LxQt settings. 107 | QByteArray tb_style = settings.value(QLatin1String("tool_button_style")).toByteArray(); 108 | // convert toolbar style name to value 109 | QMetaEnum me = QToolBar::staticMetaObject.property(QToolBar::staticMetaObject.indexOfProperty("toolButtonStyle")).enumerator(); 110 | int value = me.keyToValue(tb_style.constData()); 111 | if(value == -1) 112 | toolButtonStyle_ = Qt::ToolButtonTextBesideIcon; 113 | else 114 | toolButtonStyle_ = static_cast(value); 115 | 116 | // toolbar icon size 117 | toolBarIconSize_ = qBound(0, settings.value(QLatin1String("tool_bar_icon_size")).toInt(), 48); 118 | if (toolBarIconSize_ < 16) 119 | toolBarIconSize_ = 0; // let the style decide 120 | 121 | // single click activation 122 | singleClickActivate_ = settings.value(QLatin1String("single_click_activate")).toBool(); 123 | 124 | // palette 125 | settings.beginGroup(QLatin1String("Palette")); 126 | paletteChanged_ = false; 127 | 128 | QColor color = winColor_; 129 | winColor_ = QColor::fromString(settings.value(QLatin1String("window_color"), QLatin1String("#efefef")).toString()); 130 | if(!winColor_.isValid()) 131 | winColor_ = QColor::fromString(QStringLiteral("#efefef")); 132 | paletteChanged_ = color != winColor_; 133 | 134 | color = baseColor_; 135 | baseColor_ = QColor::fromString(settings.value(QLatin1String("base_color")).toString()); 136 | if (!paletteChanged_) 137 | paletteChanged_ = baseColor_.isValid() && color != baseColor_; 138 | 139 | color = highlightColor_; 140 | highlightColor_ = QColor::fromString(settings.value(QLatin1String("highlight_color")).toString()); 141 | if (!paletteChanged_) 142 | paletteChanged_ = highlightColor_.isValid() && color != highlightColor_; 143 | 144 | color = winTextColor_; 145 | winTextColor_ = QColor::fromString(settings.value(QLatin1String("window_text_color")).toString()); 146 | if (!paletteChanged_) 147 | paletteChanged_ = winTextColor_.isValid() && color != winTextColor_; 148 | 149 | color = textColor_; 150 | textColor_ = QColor::fromString(settings.value(QLatin1String("text_color")).toString()); 151 | if (!paletteChanged_) 152 | paletteChanged_ = textColor_.isValid() && color != textColor_; 153 | 154 | color = highlightedTextColor_; 155 | highlightedTextColor_ = QColor::fromString(settings.value(QLatin1String("highlighted_text_color")).toString()); 156 | if (!paletteChanged_) 157 | paletteChanged_ = highlightedTextColor_.isValid() && color != highlightedTextColor_; 158 | 159 | color = linkColor_; 160 | linkColor_ = QColor::fromString(settings.value(QLatin1String("link_color")).toString()); 161 | if (!paletteChanged_) 162 | paletteChanged_ = linkColor_.isValid() && color != linkColor_; 163 | 164 | color = linkVisitedColor_; 165 | linkVisitedColor_ = QColor::fromString(settings.value(QLatin1String("link_visited_color")).toString()); 166 | if (!paletteChanged_) 167 | paletteChanged_ = linkVisitedColor_.isValid() && color != linkVisitedColor_; 168 | 169 | color = tooltipBaseCol_; 170 | tooltipBaseCol_ = QColor::fromString(settings.value(QLatin1String("tooltip_base_color")).toString()); 171 | if (!paletteChanged_) 172 | paletteChanged_ = tooltipBaseCol_.isValid() && color != tooltipBaseCol_; 173 | 174 | color = tooltipTextCol_; 175 | tooltipTextCol_ = QColor::fromString(settings.value(QLatin1String("tooltip_text_color")).toString()); 176 | if (!paletteChanged_) 177 | paletteChanged_ = tooltipTextCol_.isValid() && color != tooltipTextCol_; 178 | 179 | if(paletteChanged_) 180 | { 181 | delete LXQtPalette_; 182 | // This sets all colors appropriately but valid custom colors are set below. 183 | // If a custom color is not valid, Qt's calculated color will be used. 184 | LXQtPalette_ = new QPalette(winColor_); 185 | 186 | if (baseColor_.isValid()) 187 | { 188 | LXQtPalette_->setColor(QPalette::Base, baseColor_); 189 | // Qt makes the alternate base color (used by some item views) by mixing the button 190 | // color (= window color) and base color. That can result in unreadable texts when 191 | // the base and window colors have a high contrast with each other. 192 | color = baseColor_; 193 | int l = color.lightness(); 194 | if (l < 127) 195 | l += 10; 196 | else 197 | l -= 10; 198 | color.setHsl(color.hue(), color.saturation(), l); 199 | LXQtPalette_->setColor(QPalette::AlternateBase, color); 200 | } 201 | if (highlightColor_.isValid()) 202 | { 203 | LXQtPalette_->setColor(QPalette::Highlight, highlightColor_); 204 | int gray = qGray(highlightColor_.rgb()); 205 | color = QColor(gray, gray, gray); 206 | LXQtPalette_->setColor(QPalette::Disabled, QPalette::Highlight, color); 207 | } 208 | else 209 | { 210 | // Qt's default highlight color and that of Fusion may be different. This is a workaround: 211 | LXQtPalette_->setColor(QPalette::Highlight, QColor(60, 140, 230)); 212 | if (highlightedTextColor_.isValid()) 213 | LXQtPalette_->setColor(QPalette::HighlightedText, QColor(255, 255, 255)); 214 | } 215 | if (winTextColor_.isValid()) 216 | { 217 | LXQtPalette_->setColor(QPalette::WindowText, winTextColor_); 218 | LXQtPalette_->setColor(QPalette::ButtonText, winTextColor_); 219 | color = winTextColor_; 220 | color.setAlpha(130); 221 | LXQtPalette_->setColor(QPalette::Disabled, QPalette::WindowText, color); 222 | LXQtPalette_->setColor(QPalette::Disabled, QPalette::ButtonText, color); 223 | } 224 | if (textColor_.isValid()) 225 | { 226 | LXQtPalette_->setColor(QPalette::Text, textColor_); 227 | color = textColor_; 228 | color.setAlpha(130); 229 | LXQtPalette_->setColor(QPalette::Disabled, QPalette::Text, color); 230 | } 231 | if (highlightedTextColor_.isValid()) 232 | { 233 | LXQtPalette_->setColor(QPalette::HighlightedText, highlightedTextColor_); 234 | color = highlightedTextColor_; 235 | color.setAlpha(130); 236 | LXQtPalette_->setColor(QPalette::Disabled, QPalette::HighlightedText, color); 237 | } 238 | if (linkColor_.isValid()) 239 | LXQtPalette_->setColor(QPalette::Link, linkColor_); 240 | if (linkVisitedColor_.isValid()) 241 | LXQtPalette_->setColor(QPalette::LinkVisited, linkVisitedColor_); 242 | if (tooltipBaseCol_.isValid()) 243 | LXQtPalette_->setColor(QPalette::ToolTipBase, tooltipBaseCol_); 244 | if (tooltipTextCol_.isValid()) 245 | LXQtPalette_->setColor(QPalette::ToolTipText, tooltipTextCol_); 246 | } 247 | settings.endGroup(); 248 | 249 | // load Qt settings 250 | settings.beginGroup(QLatin1String("Qt")); 251 | 252 | // widget style 253 | style_ = settings.value(QLatin1String("style"), QLatin1String("fusion")).toString(); 254 | 255 | // SystemFont 256 | fontStr_ = settings.value(QLatin1String("font")).toString(); 257 | 258 | if(!fontStr_.isEmpty()) { 259 | if(font_.fromString(fontStr_)) { 260 | if(qobject_cast(QCoreApplication::instance())) { 261 | QApplication::setFont(font_); // it seems that we need to do this manually. 262 | } 263 | } 264 | } 265 | 266 | // FixedFont 267 | fixedFontStr_ = settings.value(QLatin1String("fixedFont")).toString(); 268 | if(!fixedFontStr_.isEmpty()) { 269 | fixedFont_.fromString(fixedFontStr_); 270 | } 271 | 272 | // mouse 273 | doubleClickInterval_ = settings.value(QLatin1String("doubleClickInterval")); 274 | wheelScrollLines_ = settings.value(QLatin1String("wheelScrollLines")); 275 | 276 | // keyboard 277 | cursorFlashTime_ = settings.value(QLatin1String("cursorFlashTime")); 278 | settings.endGroup(); 279 | 280 | // mouse cursor 281 | QSettings sessionSettings(QSettings::UserScope, QLatin1String("lxqt"), QLatin1String("session")); 282 | sessionSettings.beginGroup(QStringLiteral("Mouse")); 283 | mouseCursorTheme_ = sessionSettings.value(QLatin1String("cursor_theme")); 284 | int curSize = sessionSettings.value(QLatin1String("cursor_size"), 24).toInt(); 285 | mouseCursorSize_ = QSize(curSize, curSize); 286 | sessionSettings.endGroup(); 287 | } 288 | 289 | // this is called whenever the config file is changed. 290 | void LXQtPlatformTheme::onSettingsChanged() { 291 | // D*mn! yet another Qt 5.4 regression!!! 292 | // See the bug report: https://github.com/lxqt/lxqt/issues/441 293 | // Since Qt 5.4, QSettings uses QSaveFile to save the config files. 294 | // https://github.com/qtproject/qtbase/commit/8d15068911d7c0ba05732e2796aaa7a90e34a6a1#diff-e691c0405f02f3478f4f50a27bdaecde 295 | // QSaveFile will save the content to a new temp file, and replace the old file later. 296 | // Hence the existing config file is not changed. Instead, it's deleted and then replaced. 297 | // This new behaviour unfortunately breaks QFileSystemWatcher. 298 | // After file deletion, we can no longer receive any new change notifications. 299 | // The most ridiculous thing is, QFileSystemWatcher does not provide a 300 | // way for us to know if a file is deleted. WT*? 301 | // Luckily, I found a workaround: If the file path no longer exists 302 | // in the watcher's files(), this file is deleted. 303 | bool file_deleted = !settingsWatcher_->files().contains(settingsFile_); 304 | if(file_deleted) // if our config file is already deleted, reinstall a new watcher 305 | settingsWatcher_->addPath(settingsFile_); 306 | 307 | // NOTE: in Qt4, Qt monitors the change of _QT_SETTINGS_TIMESTAMP root property and 308 | // reload Trolltech.conf when the value is changed. Then, it automatically 309 | // applies the new settings. 310 | // Unfortunately, this is no longer the case in Qt5. Yes, yet another regression. 311 | // We're free to provide our own platform plugin, but Qt5 does not 312 | // update the settings and repaint the UI. We need to do it ourselves 313 | // through dirty hacks and private Qt internal APIs. 314 | QString oldStyle = style_; 315 | QString oldIconTheme = iconTheme_; 316 | QString oldFont = fontStr_; 317 | QString oldFixedFont = fixedFontStr_; 318 | 319 | loadSettings(); // reload the config file 320 | 321 | auto app = qobject_cast(QCoreApplication::instance()); 322 | 323 | if(app && (style_ != oldStyle || paletteChanged_)) // the widget style or palette is changed 324 | { 325 | // ask Qt to apply the new style 326 | QApplication::setStyle(style_); 327 | // Qt 5.15 needs this and it's safe otherwise 328 | if(LXQtPalette_ != nullptr) 329 | { 330 | QApplication::setPalette(*LXQtPalette_); 331 | // the app should be polished because the style may have an internal palette 332 | QApplication::style()->polish(app); 333 | } 334 | } 335 | 336 | if(iconTheme_ != oldIconTheme) { // the icon theme is changed 337 | XdgIconLoader::instance()->updateSystemTheme(); // this is a private internal API of Qt. 338 | } 339 | XdgIconLoader::instance()->setFollowColorScheme(iconFollowColorScheme_); 340 | 341 | // if font is changed 342 | if(oldFont != fontStr_ || oldFixedFont != fixedFontStr_){ 343 | // FIXME: to my knowledge there is no way to ask Qt to reload the fonts. 344 | // Should we call QApplication::setFont() to override the font? 345 | // This does not work with QSS, though. 346 | // 347 | // After reading the source code of Qt, I think the right method to call 348 | // here is QApplicationPrivate::setSystemFont(). However, this is an 349 | // internal API and should not be used. Let's call QApplication::setFont() 350 | // instead since it approximately does the same thing. 351 | // Internally, QApplication will emit QEvent::ApplicationFontChange so 352 | // all of the widgets will update their fonts. 353 | // FIXME: should we call the internal API: QApplicationPrivate::setFont() instead? 354 | // QGtkStyle does this internally. 355 | fixedFont_.fromString(fixedFontStr_); // FIXME: how to set this to the app? 356 | if(font_.fromString(fontStr_)) { 357 | if(app) { 358 | QApplication::setFont(font_); 359 | } 360 | } 361 | } 362 | 363 | if(app) { 364 | QApplication::setWheelScrollLines(wheelScrollLines_.toInt()); 365 | 366 | // emit a ThemeChange event to all widgets 367 | const auto widgets = QApplication::allWidgets(); 368 | for(QWidget* const widget : widgets) { 369 | // Qt5 added a QEvent::ThemeChange event. 370 | QEvent event(QEvent::ThemeChange); 371 | QApplication::sendEvent(widget, &event); 372 | } 373 | } 374 | } 375 | 376 | bool LXQtPlatformTheme::usePlatformNativeDialog(DialogType type) const { 377 | if(type == FileDialog 378 | && (qobject_cast(QCoreApplication::instance()) != nullptr)) { // QML may not have qApp 379 | // use our own file dialog 380 | return true; 381 | } 382 | return false; 383 | } 384 | 385 | 386 | QPlatformDialogHelper *LXQtPlatformTheme::createPlatformDialogHelper(DialogType type) const { 387 | if(type == FileDialog 388 | && (qobject_cast(QCoreApplication::instance()) != nullptr)) { // QML may not have qApp 389 | // use our own file dialog provided by libfm 390 | 391 | // When a process has this environment set, that means glib event loop integration is disabled. 392 | // In this case, libfm-qt just won't work. So let's disable the file dialog helper and return nullptr. 393 | if(QString::fromLocal8Bit(qgetenv("QT_NO_GLIB")) == QLatin1String("1")) { 394 | return nullptr; 395 | } 396 | 397 | // The createFileDialogHelper() method is dynamically loaded from libfm-qt on demand 398 | if(createFileDialogHelper == nullptr) { 399 | // try to dynamically load versioned libfm-qt.so 400 | QLibrary libfmQtLibrary{QLatin1String(LIB_FM_QT_SONAME)}; 401 | libfmQtLibrary.load(); 402 | if(!libfmQtLibrary.isLoaded()) { 403 | return nullptr; 404 | } 405 | 406 | // try to resolve the symbol to get the function pointer 407 | createFileDialogHelper = reinterpret_cast(libfmQtLibrary.resolve("createFileDialogHelper")); 408 | if(createFileDialogHelper == nullptr) { 409 | return nullptr; 410 | } 411 | } 412 | 413 | // create a new file dialog helper provided by libfm 414 | return createFileDialogHelper(); 415 | } 416 | return nullptr; 417 | } 418 | 419 | const QPalette *LXQtPlatformTheme::palette(Palette type) const { 420 | if(type == QPlatformTheme::SystemPalette) { 421 | if(LXQtPalette_ != nullptr) 422 | return LXQtPalette_; 423 | } 424 | return nullptr; 425 | } 426 | 427 | const QFont *LXQtPlatformTheme::font(Font type) const { 428 | if(type == SystemFont && !fontStr_.isEmpty()) { 429 | // NOTE: for some reason, this is not called by Qt when program startup. 430 | // So we do QApplication::setFont() manually. 431 | return &font_; 432 | } 433 | else if(type == FixedFont && !fixedFontStr_.isEmpty()) { 434 | return &fixedFont_; 435 | } 436 | return QPlatformTheme::font(type); 437 | } 438 | 439 | QVariant LXQtPlatformTheme::themeHint(ThemeHint hint) const { 440 | switch (hint) { 441 | case CursorFlashTime: 442 | return cursorFlashTime_; 443 | case KeyboardInputInterval: 444 | break; 445 | case MouseDoubleClickInterval: 446 | return doubleClickInterval_; 447 | case StartDragDistance: 448 | break; 449 | case StartDragTime: 450 | break; 451 | case KeyboardAutoRepeatRate: 452 | break; 453 | case PasswordMaskDelay: 454 | break; 455 | case StartDragVelocity: 456 | break; 457 | case TextCursorWidth: 458 | break; 459 | case DropShadow: 460 | return QVariant(true); 461 | case MaximumScrollBarDragDistance: 462 | break; 463 | case ToolButtonStyle: 464 | return QVariant(toolButtonStyle_); 465 | case ToolBarIconSize: 466 | return QVariant(toolBarIconSize_); 467 | case ItemViewActivateItemOnSingleClick: 468 | return QVariant(singleClickActivate_); 469 | case SystemIconThemeName: 470 | return iconTheme_; 471 | case SystemIconFallbackThemeName: 472 | return QLatin1String("hicolor"); 473 | case IconThemeSearchPaths: 474 | return xdgIconThemePaths(); 475 | case StyleNames: 476 | return QStringList() << style_; 477 | case WindowAutoPlacement: 478 | break; 479 | case DialogButtonBoxLayout: 480 | break; 481 | case DialogButtonBoxButtonsHaveIcons: 482 | return QVariant(true); 483 | case UseFullScreenForPopupMenu: 484 | break; 485 | case KeyboardScheme: 486 | return QVariant(X11KeyboardScheme); 487 | case UiEffects: 488 | return QVariant(static_cast(HoverEffect)); 489 | case SpellCheckUnderlineStyle: 490 | break; 491 | case IconPixmapSizes: 492 | break; 493 | case PasswordMaskCharacter: 494 | break; 495 | case DialogSnapToDefaultButton: 496 | break; 497 | case ContextMenuOnMouseRelease: 498 | break; 499 | case MousePressAndHoldInterval: 500 | break; 501 | case MouseDoubleClickDistance: 502 | break; 503 | case WheelScrollLines: 504 | return wheelScrollLines_; 505 | case QPlatformTheme::ShowShortcutsInContextMenus: 506 | return QVariant(true); 507 | case MouseCursorTheme: 508 | return mouseCursorTheme_; 509 | case MouseCursorSize: 510 | return QVariant(mouseCursorSize_); 511 | default: 512 | break; 513 | } 514 | return QPlatformTheme::themeHint(hint); 515 | } 516 | 517 | QIconEngine *LXQtPlatformTheme::createIconEngine(const QString &iconName) const 518 | { 519 | return new XdgIconLoaderEngine(iconName); 520 | } 521 | 522 | // Helper to return the icon theme paths from XDG. 523 | QStringList LXQtPlatformTheme::xdgIconThemePaths() const 524 | { 525 | QStringList paths; 526 | QStringList xdgDirs; 527 | 528 | // Add home directory first in search path 529 | const QFileInfo homeIconDir(QDir::homePath() + QStringLiteral("/.icons")); 530 | if (homeIconDir.isDir()) 531 | paths.prepend(homeIconDir.absoluteFilePath()); 532 | 533 | QString xdgDataHome = QFile::decodeName(qgetenv("XDG_DATA_HOME")); 534 | if (xdgDataHome.isEmpty()) 535 | xdgDataHome = QDir::homePath() + QLatin1String("/.local/share"); 536 | xdgDirs.append(xdgDataHome); 537 | 538 | QString xdgDataDirs = QFile::decodeName(qgetenv("XDG_DATA_DIRS")); 539 | if (xdgDataDirs.isEmpty()) 540 | xdgDataDirs = QLatin1String("/usr/local/share/:/usr/share/"); 541 | xdgDirs.append(xdgDataDirs); 542 | 543 | for (const auto &s: std::as_const(xdgDirs)) { 544 | const QStringList r = s.split(QLatin1Char(':'), Qt::SkipEmptyParts); 545 | for (const auto& xdgDir: r) { 546 | const QFileInfo xdgIconsDir(xdgDir + QStringLiteral("/icons")); 547 | if (xdgIconsDir.isDir()) 548 | paths.append(xdgIconsDir.absoluteFilePath()); 549 | } 550 | } 551 | paths.removeDuplicates(); 552 | return paths; 553 | } 554 | -------------------------------------------------------------------------------- /src/lxqtplatformtheme.h: -------------------------------------------------------------------------------- 1 | /* BEGIN_COMMON_COPYRIGHT_HEADER 2 | * (c)LGPL2+ 3 | * 4 | * LXQt - a lightweight, Qt based, desktop toolset 5 | * https://lxqt.org/ 6 | * 7 | * Copyright: 2014 LXQt team 8 | * Authors: 9 | * Hong Jen Yee (PCMan) 10 | * 11 | * This program or library is free software; you can redistribute it 12 | * and/or modify it under the terms of the GNU Lesser General Public 13 | * License as published by the Free Software Foundation; either 14 | * version 2.1 of the License, or (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | * Lesser General Public License for more details. 20 | 21 | * You should have received a copy of the GNU Lesser General 22 | * Public License along with this library; if not, write to the 23 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 | * Boston, MA 02110-1301 USA 25 | * 26 | * END_COMMON_COPYRIGHT_HEADER */ 27 | 28 | #ifndef LXQTPLATFORMTHEME_H 29 | #define LXQTPLATFORMTHEME_H 30 | 31 | #include "lxqtsystemtrayicon.h" 32 | 33 | #include // this private header is subject to changes 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | 41 | class Q_GUI_EXPORT LXQtPlatformTheme : public QObject, public QPlatformTheme { 42 | Q_OBJECT 43 | public: 44 | LXQtPlatformTheme(); 45 | ~LXQtPlatformTheme() override; 46 | 47 | // virtual QPlatformMenuItem* createPlatformMenuItem() const; 48 | // virtual QPlatformMenu* createPlatformMenu() const; 49 | // virtual QPlatformMenuBar* createPlatformMenuBar() const; 50 | 51 | bool usePlatformNativeDialog(DialogType type) const override; 52 | QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override; 53 | 54 | const QPalette *palette(Palette type = SystemPalette) const override; 55 | 56 | const QFont *font(Font type = SystemFont) const override; 57 | 58 | QVariant themeHint(ThemeHint hint) const override; 59 | 60 | QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override 61 | { 62 | auto trayIcon = new LXQtSystemTrayIcon; 63 | if (trayIcon->isSystemTrayAvailable()) 64 | return trayIcon; 65 | else 66 | { 67 | delete trayIcon; 68 | return nullptr; 69 | } 70 | } 71 | 72 | // virtual QPixmap standardPixmap(StandardPixmap sp, const QSizeF &size) const; 73 | // virtual QPixmap fileIconPixmap(const QFileInfo &fileInfo, const QSizeF &size, 74 | // QPlatformTheme::IconOptions iconOptions = 0) const; 75 | 76 | QIconEngine *createIconEngine(const QString &iconName) const override; 77 | 78 | // virtual QList keyBindings(QKeySequence::StandardKey key) const; 79 | 80 | // virtual QString standardButtonText(int button) const; 81 | 82 | public Q_SLOTS: 83 | void lazyInit(); 84 | 85 | private: 86 | void loadSettings(); 87 | 88 | private Q_SLOTS: 89 | void onSettingsChanged(); 90 | 91 | private: 92 | // LXQt settings 93 | QString iconTheme_; 94 | Qt::ToolButtonStyle toolButtonStyle_; 95 | int toolBarIconSize_; 96 | bool singleClickActivate_; 97 | bool iconFollowColorScheme_; 98 | 99 | // other Qt settings 100 | // widget 101 | QString style_; 102 | QColor winColor_, baseColor_, highlightColor_, 103 | winTextColor_, textColor_, highlightedTextColor_, 104 | linkColor_, linkVisitedColor_, 105 | tooltipBaseCol_, tooltipTextCol_; 106 | bool paletteChanged_; 107 | QString fontStr_; 108 | QFont font_; 109 | QString fixedFontStr_; 110 | QFont fixedFont_; 111 | // mouse 112 | QVariant doubleClickInterval_; 113 | QVariant wheelScrollLines_; 114 | // mouse cursor 115 | QVariant mouseCursorTheme_; 116 | QSize mouseCursorSize_; 117 | // keyboard 118 | QVariant cursorFlashTime_; 119 | 120 | QFileSystemWatcher *settingsWatcher_; 121 | QString settingsFile_; 122 | 123 | QPalette *LXQtPalette_; 124 | 125 | QStringList xdgIconThemePaths() const; 126 | }; 127 | 128 | #endif // LXQTPLATFORMTHEME_H 129 | -------------------------------------------------------------------------------- /src/lxqtplatformtheme.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys": [ "lxqt" ] 3 | } 4 | -------------------------------------------------------------------------------- /src/lxqtsystemtrayicon.cpp: -------------------------------------------------------------------------------- 1 | /* BEGIN_COMMON_COPYRIGHT_HEADER 2 | * (c)LGPL2+ 3 | * 4 | * LXQt - a lightweight, Qt based, desktop toolset 5 | * https://lxqt.org/ 6 | * 7 | * Copyright: 2015 LXQt team 8 | * Authors: 9 | * Paulo Lieuthier 10 | * 11 | * This program or library is free software; you can redistribute it 12 | * and/or modify it under the terms of the GNU Lesser General Public 13 | * License as published by the Free Software Foundation; either 14 | * version 2.1 of the License, or (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | * Lesser General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General 22 | * Public License along with this library; if not, write to the 23 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 | * Boston, MA 02110-1301 USA 25 | * 26 | * END_COMMON_COPYRIGHT_HEADER */ 27 | 28 | #include "lxqtsystemtrayicon.h" 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | SystemTrayMenu::SystemTrayMenu() 38 | : QPlatformMenu(), 39 | m_tag(0), 40 | m_menu(new QMenu()) 41 | { 42 | connect(m_menu.data(), &QMenu::aboutToShow, this, &QPlatformMenu::aboutToShow); 43 | connect(m_menu.data(), &QMenu::aboutToHide, this, &QPlatformMenu::aboutToHide); 44 | } 45 | 46 | SystemTrayMenu::~SystemTrayMenu() 47 | { 48 | if (m_menu != nullptr) 49 | m_menu->deleteLater(); 50 | } 51 | 52 | QPlatformMenuItem *SystemTrayMenu::createMenuItem() const 53 | { 54 | return new SystemTrayMenuItem(); 55 | } 56 | 57 | void SystemTrayMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) 58 | { 59 | if (SystemTrayMenuItem *ours = qobject_cast(menuItem)) 60 | { 61 | bool inserted = false; 62 | 63 | if (SystemTrayMenuItem *oursBefore = qobject_cast(before)) 64 | { 65 | for (auto it = m_items.cbegin(); it != m_items.cend(); ++it) 66 | { 67 | if (*it == oursBefore) 68 | { 69 | m_items.insert(it, ours); 70 | if (m_menu != nullptr) 71 | m_menu->insertAction(oursBefore->action(), ours->action()); 72 | 73 | inserted = true; 74 | break; 75 | } 76 | } 77 | } 78 | 79 | if (!inserted) 80 | { 81 | m_items.append(ours); 82 | if (m_menu != nullptr) 83 | m_menu->addAction(ours->action()); 84 | } 85 | } 86 | } 87 | 88 | QPlatformMenuItem *SystemTrayMenu::menuItemAt(int position) const 89 | { 90 | if (position < m_items.size()) 91 | return m_items.at(position); 92 | 93 | return nullptr; 94 | } 95 | 96 | QPlatformMenuItem *SystemTrayMenu::menuItemForTag(quintptr tag) const 97 | { 98 | auto it = std::find_if(m_items.constBegin(), m_items.constEnd(), [tag] (SystemTrayMenuItem *item) 99 | { 100 | return item->tag() == tag; 101 | }); 102 | 103 | if (it != m_items.constEnd()) 104 | return *it; 105 | 106 | return nullptr; 107 | } 108 | 109 | void SystemTrayMenu::removeMenuItem(QPlatformMenuItem *menuItem) 110 | { 111 | if (SystemTrayMenuItem *ours = qobject_cast(menuItem)) 112 | { 113 | m_items.removeOne(ours); 114 | if ((ours->action() != nullptr) && (m_menu != nullptr)) 115 | m_menu->removeAction(ours->action()); 116 | } 117 | } 118 | 119 | void SystemTrayMenu::setEnabled(bool enabled) 120 | { 121 | if (m_menu == nullptr) 122 | return; 123 | 124 | m_menu->setEnabled(enabled); 125 | } 126 | 127 | void SystemTrayMenu::setIcon(const QIcon &icon) 128 | { 129 | if (m_menu == nullptr) 130 | return; 131 | 132 | m_menu->setIcon(icon); 133 | } 134 | 135 | void SystemTrayMenu::setTag(quintptr tag) 136 | { 137 | m_tag = tag; 138 | } 139 | 140 | void SystemTrayMenu::setText(const QString &text) 141 | { 142 | if (m_menu == nullptr) 143 | return; 144 | 145 | m_menu->setTitle(text); 146 | } 147 | 148 | void SystemTrayMenu::setVisible(bool visible) 149 | { 150 | if (m_menu == nullptr) 151 | return; 152 | 153 | m_menu->setVisible(visible); 154 | } 155 | 156 | void SystemTrayMenu::syncMenuItem(QPlatformMenuItem *) 157 | { 158 | // Nothing to do 159 | } 160 | 161 | void SystemTrayMenu::syncSeparatorsCollapsible(bool enable) 162 | { 163 | if (m_menu == nullptr) 164 | return; 165 | 166 | m_menu->setSeparatorsCollapsible(enable); 167 | } 168 | 169 | quintptr SystemTrayMenu::tag() const 170 | { 171 | return m_tag; 172 | } 173 | 174 | QMenu *SystemTrayMenu::menu() const 175 | { 176 | return m_menu.data(); 177 | } 178 | 179 | SystemTrayMenuItem::SystemTrayMenuItem() 180 | : QPlatformMenuItem(), 181 | m_tag(0), 182 | m_action(new QAction(this)) 183 | { 184 | connect(m_action, &QAction::triggered, this, &QPlatformMenuItem::activated); 185 | connect(m_action, &QAction::hovered, this, &QPlatformMenuItem::hovered); 186 | } 187 | 188 | SystemTrayMenuItem::~SystemTrayMenuItem() 189 | { 190 | } 191 | 192 | void SystemTrayMenuItem::setCheckable(bool checkable) 193 | { 194 | m_action->setCheckable(checkable); 195 | } 196 | 197 | void SystemTrayMenuItem::setChecked(bool isChecked) 198 | { 199 | m_action->setChecked(isChecked); 200 | } 201 | 202 | void SystemTrayMenuItem::setEnabled(bool enabled) 203 | { 204 | m_action->setEnabled(enabled); 205 | } 206 | 207 | void SystemTrayMenuItem::setFont(const QFont &font) 208 | { 209 | m_action->setFont(font); 210 | } 211 | 212 | void SystemTrayMenuItem::setIcon(const QIcon &icon) 213 | { 214 | m_action->setIcon(icon); 215 | } 216 | 217 | void SystemTrayMenuItem::setIsSeparator(bool isSeparator) 218 | { 219 | m_action->setSeparator(isSeparator); 220 | } 221 | 222 | void SystemTrayMenuItem::setMenu(QPlatformMenu *menu) 223 | { 224 | if (SystemTrayMenu *ourMenu = qobject_cast(menu)) 225 | m_action->setMenu(ourMenu->menu()); 226 | } 227 | 228 | void SystemTrayMenuItem::setRole(QPlatformMenuItem::MenuRole) 229 | { 230 | } 231 | 232 | void SystemTrayMenuItem::setShortcut(const QKeySequence &shortcut) 233 | { 234 | m_action->setShortcut(shortcut); 235 | } 236 | 237 | void SystemTrayMenuItem::setTag(quintptr tag) 238 | { 239 | m_tag = tag; 240 | } 241 | 242 | void SystemTrayMenuItem::setText(const QString &text) 243 | { 244 | m_action->setText(text); 245 | } 246 | 247 | void SystemTrayMenuItem::setVisible(bool isVisible) 248 | { 249 | m_action->setVisible(isVisible); 250 | } 251 | 252 | void SystemTrayMenuItem::setIconSize(int) 253 | { 254 | } 255 | 256 | quintptr SystemTrayMenuItem::tag() const 257 | { 258 | return m_tag; 259 | } 260 | 261 | QAction *SystemTrayMenuItem::action() const 262 | { 263 | return m_action; 264 | } 265 | 266 | LXQtSystemTrayIcon::LXQtSystemTrayIcon() 267 | : QPlatformSystemTrayIcon(), 268 | mSni(nullptr) 269 | { 270 | // register types 271 | qDBusRegisterMetaType(); 272 | qDBusRegisterMetaType(); 273 | qDBusRegisterMetaType(); 274 | } 275 | 276 | LXQtSystemTrayIcon::~LXQtSystemTrayIcon() 277 | { 278 | } 279 | 280 | void LXQtSystemTrayIcon::init() 281 | { 282 | if (mSni == nullptr) 283 | { 284 | mSni = new StatusNotifierItem(QString::number(QCoreApplication::applicationPid()), this); 285 | mSni->setTitle(QApplication::applicationDisplayName()); 286 | 287 | // default menu 288 | QPlatformMenu *menu = createMenu(); 289 | menu->setParent(mSni); 290 | QPlatformMenuItem *menuItem = menu->createMenuItem(); 291 | menuItem->setParent(menu); 292 | menuItem->setText(tr("Quit")); 293 | menuItem->setIcon(QIcon::fromTheme(QLatin1String("application-exit"))); 294 | connect(menuItem, &QPlatformMenuItem::activated, qApp, &QApplication::quit); 295 | menu->insertMenuItem(menuItem, nullptr); 296 | updateMenu(menu); 297 | 298 | connect(mSni, &StatusNotifierItem::activateRequested, [this](const QPoint &) 299 | { 300 | Q_EMIT activated(QPlatformSystemTrayIcon::Trigger); 301 | }); 302 | 303 | connect(mSni, &StatusNotifierItem::secondaryActivateRequested, [this](const QPoint &) 304 | { 305 | Q_EMIT activated(QPlatformSystemTrayIcon::MiddleClick); 306 | }); 307 | } 308 | } 309 | 310 | void LXQtSystemTrayIcon::cleanup() 311 | { 312 | delete mSni; 313 | mSni = nullptr; 314 | } 315 | 316 | void LXQtSystemTrayIcon::updateIcon(const QIcon &icon) 317 | { 318 | if (mSni == nullptr) 319 | return; 320 | 321 | if (icon.name().isEmpty()) 322 | { 323 | mSni->setIconByPixmap(icon); 324 | mSni->setToolTipIconByPixmap(icon); 325 | } 326 | else 327 | { 328 | mSni->setIconByName(icon.name()); 329 | mSni->setToolTipIconByName(icon.name()); 330 | } 331 | } 332 | 333 | void LXQtSystemTrayIcon::updateToolTip(const QString &tooltip) 334 | { 335 | if (mSni == nullptr) 336 | return; 337 | 338 | mSni->setToolTipTitle(tooltip); 339 | } 340 | 341 | void LXQtSystemTrayIcon::updateMenu(QPlatformMenu *menu) 342 | { 343 | if (mSni == nullptr) 344 | return; 345 | 346 | if (SystemTrayMenu *ourMenu = qobject_cast(menu)) 347 | mSni->setContextMenu(ourMenu->menu()); 348 | } 349 | 350 | QPlatformMenu *LXQtSystemTrayIcon::createMenu() const 351 | { 352 | return new SystemTrayMenu(); 353 | } 354 | 355 | QRect LXQtSystemTrayIcon::geometry() const 356 | { 357 | // StatusNotifierItem doesn't provide the geometry 358 | return {}; 359 | } 360 | 361 | void LXQtSystemTrayIcon::showMessage(const QString &title, const QString &msg, 362 | const QIcon &icon, MessageIcon, int secs) 363 | { 364 | if (mSni == nullptr) 365 | return; 366 | 367 | mSni->showMessage(title, msg, icon.name(), secs); 368 | } 369 | 370 | bool LXQtSystemTrayIcon::isSystemTrayAvailable() const 371 | { 372 | QDBusInterface systrayHost(QLatin1String("org.kde.StatusNotifierWatcher"), 373 | QLatin1String("/StatusNotifierWatcher"), 374 | QLatin1String("org.kde.StatusNotifierWatcher")); 375 | 376 | return systrayHost.isValid() && systrayHost.property("IsStatusNotifierHostRegistered").toBool(); 377 | } 378 | 379 | bool LXQtSystemTrayIcon::supportsMessages() const 380 | { 381 | return true; 382 | } 383 | -------------------------------------------------------------------------------- /src/lxqtsystemtrayicon.h: -------------------------------------------------------------------------------- 1 | /* BEGIN_COMMON_COPYRIGHT_HEADER 2 | * (c)LGPL2+ 3 | * 4 | * LXQt - a lightweight, Qt based, desktop toolset 5 | * https://lxqt.org/ 6 | * 7 | * Copyright: 2015 LXQt team 8 | * Authors: 9 | * Paulo Lieuthier 10 | * 11 | * This program or library is free software; you can redistribute it 12 | * and/or modify it under the terms of the GNU Lesser General Public 13 | * License as published by the Free Software Foundation; either 14 | * version 2.1 of the License, or (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | * Lesser General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General 22 | * Public License along with this library; if not, write to the 23 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 | * Boston, MA 02110-1301 USA 25 | * 26 | * END_COMMON_COPYRIGHT_HEADER */ 27 | 28 | #ifndef LXQTSYSTEMTRAYICON_H 29 | #define LXQTSYSTEMTRAYICON_H 30 | 31 | #include 32 | #include 33 | 34 | #include "statusnotifieritem/statusnotifieritem.h" 35 | 36 | class SystemTrayMenuItem; 37 | class QAction; 38 | class QMenu; 39 | 40 | class SystemTrayMenu : public QPlatformMenu 41 | { 42 | Q_OBJECT 43 | public: 44 | SystemTrayMenu(); 45 | ~SystemTrayMenu() override; 46 | void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) override; 47 | QPlatformMenuItem *menuItemAt(int position) const override; 48 | QPlatformMenuItem *menuItemForTag(quintptr tag) const override; 49 | void removeMenuItem(QPlatformMenuItem *menuItem) override; 50 | void setEnabled(bool enabled) override; 51 | void setIcon(const QIcon &icon) override; 52 | void setTag(quintptr tag) override; 53 | void setText(const QString &text) override; 54 | void setVisible(bool visible) override; 55 | void syncMenuItem(QPlatformMenuItem *menuItem) override; 56 | void syncSeparatorsCollapsible(bool enable) override; 57 | quintptr tag() const override; 58 | QPlatformMenuItem *createMenuItem() const override; 59 | 60 | QMenu *menu() const; 61 | 62 | private: 63 | quintptr m_tag; 64 | QPointer m_menu; 65 | QList m_items; 66 | }; 67 | 68 | class SystemTrayMenuItem : public QPlatformMenuItem 69 | { 70 | Q_OBJECT 71 | public: 72 | SystemTrayMenuItem(); 73 | ~SystemTrayMenuItem() override; 74 | void setCheckable(bool checkable) override; 75 | void setChecked(bool isChecked) override; 76 | void setEnabled(bool enabled) override; 77 | void setFont(const QFont &font) override; 78 | void setIcon(const QIcon &icon) override; 79 | void setIsSeparator(bool isSeparator) override; 80 | void setMenu(QPlatformMenu *menu) override; 81 | void setRole(MenuRole role) override; 82 | void setShortcut(const QKeySequence &shortcut) override; 83 | void setTag(quintptr tag) override; 84 | void setText(const QString &text) override; 85 | void setVisible(bool isVisible) override; 86 | quintptr tag() const override; 87 | void setIconSize(int size) override; 88 | 89 | QAction *action() const; 90 | 91 | private: 92 | quintptr m_tag; 93 | QAction *m_action; 94 | }; 95 | 96 | class LXQtSystemTrayIcon : public QPlatformSystemTrayIcon 97 | { 98 | public: 99 | LXQtSystemTrayIcon(); 100 | ~LXQtSystemTrayIcon() override; 101 | 102 | void init() override; 103 | void cleanup() override; 104 | void updateIcon(const QIcon &icon) override; 105 | void updateToolTip(const QString &tooltip) override; 106 | void updateMenu(QPlatformMenu *menu) override; 107 | QRect geometry() const override; 108 | void showMessage(const QString &title, const QString &msg, 109 | const QIcon &icon, MessageIcon iconType, int secs) override; 110 | 111 | bool isSystemTrayAvailable() const override; 112 | bool supportsMessages() const override; 113 | 114 | QPlatformMenu *createMenu() const override; 115 | 116 | private: 117 | StatusNotifierItem *mSni; 118 | }; 119 | 120 | #endif 121 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* BEGIN_COMMON_COPYRIGHT_HEADER 2 | * (c)LGPL2+ 3 | * 4 | * LXQt - a lightweight, Qt based, desktop toolset 5 | * https://lxqt.org/ 6 | * 7 | * Copyright: 2014 LXQt team 8 | * Authors: 9 | * Hong Jen Yee (PCMan) 10 | * 11 | * This program or library is free software; you can redistribute it 12 | * and/or modify it under the terms of the GNU Lesser General Public 13 | * License as published by the Free Software Foundation; either 14 | * version 2.1 of the License, or (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | * Lesser General Public License for more details. 20 | 21 | * You should have received a copy of the GNU Lesser General 22 | * Public License along with this library; if not, write to the 23 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 | * Boston, MA 02110-1301 USA 25 | * 26 | * END_COMMON_COPYRIGHT_HEADER */ 27 | 28 | // NOTE: To load the plugin, we can set environment variable QT_QPA_PLATFORMTHEME=lxqt 29 | 30 | #include 31 | #include "lxqtplatformtheme.h" 32 | 33 | QT_BEGIN_NAMESPACE 34 | 35 | class LXQtPlatformThemePlugin: public QPlatformThemePlugin { 36 | Q_OBJECT 37 | Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformThemeFactoryInterface.5.1" FILE "lxqtplatformtheme.json") 38 | public: 39 | QPlatformTheme *create(const QString &key, const QStringList ¶ms) override; 40 | }; 41 | 42 | QPlatformTheme *LXQtPlatformThemePlugin::create(const QString &key, const QStringList &/*params*/) { 43 | if (key.compare(QLatin1String("lxqt"), Qt::CaseInsensitive) == 0) 44 | return new LXQtPlatformTheme(); 45 | return nullptr; 46 | } 47 | 48 | QT_END_NAMESPACE 49 | 50 | #include "main.moc" 51 | -------------------------------------------------------------------------------- /src/statusnotifieritem/dbustypes.cpp: -------------------------------------------------------------------------------- 1 | /* BEGIN_COMMON_COPYRIGHT_HEADER 2 | * (c)LGPL2+ 3 | * 4 | * LXQt - a lightweight, Qt based, desktop toolset 5 | * https://lxqt.org 6 | * 7 | * Copyright: 2015 LXQt team 8 | * Authors: 9 | * Balázs Béla 10 | * Paulo Lieuthier 11 | * 12 | * This program or library is free software; you can redistribute it 13 | * and/or modify it under the terms of the GNU Lesser General Public 14 | * License as published by the Free Software Foundation; either 15 | * version 2.1 of the License, or (at your option) any later version. 16 | * 17 | * This library is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | * Lesser General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Lesser General 23 | * Public License along with this library; if not, write to the 24 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 | * Boston, MA 02110-1301 USA 26 | * 27 | * END_COMMON_COPYRIGHT_HEADER */ 28 | 29 | #include "dbustypes.h" 30 | 31 | // Marshall the IconPixmap data into a D-Bus argument 32 | QDBusArgument &operator<<(QDBusArgument &argument, const IconPixmap &icon) 33 | { 34 | argument.beginStructure(); 35 | argument << icon.width; 36 | argument << icon.height; 37 | argument << icon.bytes; 38 | argument.endStructure(); 39 | return argument; 40 | } 41 | 42 | // Retrieve the ImageStruct data from the D-Bus argument 43 | const QDBusArgument &operator>>(const QDBusArgument &argument, IconPixmap &icon) 44 | { 45 | argument.beginStructure(); 46 | argument >> icon.width; 47 | argument >> icon.height; 48 | argument >> icon.bytes; 49 | argument.endStructure(); 50 | return argument; 51 | } 52 | 53 | // Marshall the ToolTip data into a D-Bus argument 54 | QDBusArgument &operator<<(QDBusArgument &argument, const ToolTip &toolTip) 55 | { 56 | argument.beginStructure(); 57 | argument << toolTip.iconName; 58 | argument << toolTip.iconPixmap; 59 | argument << toolTip.title; 60 | argument << toolTip.description; 61 | argument.endStructure(); 62 | return argument; 63 | } 64 | 65 | // Retrieve the ToolTip data from the D-Bus argument 66 | const QDBusArgument &operator>>(const QDBusArgument &argument, ToolTip &toolTip) 67 | { 68 | argument.beginStructure(); 69 | argument >> toolTip.iconName; 70 | argument >> toolTip.iconPixmap; 71 | argument >> toolTip.title; 72 | argument >> toolTip.description; 73 | argument.endStructure(); 74 | return argument; 75 | } 76 | -------------------------------------------------------------------------------- /src/statusnotifieritem/dbustypes.h: -------------------------------------------------------------------------------- 1 | /* BEGIN_COMMON_COPYRIGHT_HEADER 2 | * (c)LGPL2+ 3 | * 4 | * LXQt - a lightweight, Qt based, desktop toolset 5 | * https://lxqt.org 6 | * 7 | * Copyright: 2015 LXQt team 8 | * Authors: 9 | * Balázs Béla 10 | * Paulo Lieuthier 11 | * 12 | * This program or library is free software; you can redistribute it 13 | * and/or modify it under the terms of the GNU Lesser General Public 14 | * License as published by the Free Software Foundation; either 15 | * version 2.1 of the License, or (at your option) any later version. 16 | * 17 | * This library is distributed in the hope that it will be useful, 18 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 20 | * Lesser General Public License for more details. 21 | * 22 | * You should have received a copy of the GNU Lesser General 23 | * Public License along with this library; if not, write to the 24 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 25 | * Boston, MA 02110-1301 USA 26 | * 27 | * END_COMMON_COPYRIGHT_HEADER */ 28 | 29 | #include 30 | 31 | #ifndef DBUSTYPES_H 32 | #define DBUSTYPES_H 33 | 34 | struct IconPixmap { 35 | int width; 36 | int height; 37 | QByteArray bytes; 38 | }; 39 | 40 | typedef QList IconPixmapList; 41 | 42 | Q_DECLARE_METATYPE(IconPixmap) 43 | Q_DECLARE_METATYPE(IconPixmapList) 44 | 45 | struct ToolTip { 46 | QString iconName; 47 | QList iconPixmap; 48 | QString title; 49 | QString description; 50 | }; 51 | 52 | Q_DECLARE_METATYPE(ToolTip) 53 | 54 | QDBusArgument &operator<<(QDBusArgument &argument, const IconPixmap &icon); 55 | const QDBusArgument &operator>>(const QDBusArgument &argument, IconPixmap &icon); 56 | 57 | QDBusArgument &operator<<(QDBusArgument &argument, const ToolTip &toolTip); 58 | const QDBusArgument &operator>>(const QDBusArgument &argument, ToolTip &toolTip); 59 | 60 | #endif // DBUSTYPES_H 61 | -------------------------------------------------------------------------------- /src/statusnotifieritem/org.kde.StatusNotifierItem.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/statusnotifieritem/statusnotifieritem.cpp: -------------------------------------------------------------------------------- 1 | /* BEGIN_COMMON_COPYRIGHT_HEADER 2 | * (c)LGPL2+ 3 | * 4 | * LXQt - a lightweight, Qt based, desktop toolset 5 | * https://lxqt.org/ 6 | * 7 | * Copyright: 2015 LXQt team 8 | * Authors: 9 | * Paulo Lieuthier 10 | * 11 | * This program or library is free software; you can redistribute it 12 | * and/or modify it under the terms of the GNU Lesser General Public 13 | * License as published by the Free Software Foundation; either 14 | * version 2.1 of the License, or (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | * Lesser General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General 22 | * Public License along with this library; if not, write to the 23 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 | * Boston, MA 02110-1301 USA 25 | * 26 | * END_COMMON_COPYRIGHT_HEADER */ 27 | 28 | #include "statusnotifieritem.h" 29 | #include "statusnotifieritemadaptor.h" 30 | #include 31 | #include 32 | #include 33 | #include 34 | 35 | int StatusNotifierItem::mServiceCounter = 0; 36 | 37 | StatusNotifierItem::StatusNotifierItem(QString id, QObject *parent) 38 | : QObject(parent), 39 | mAdaptor(new StatusNotifierItemAdaptor(this)), 40 | mService(QString::fromLatin1("org.freedesktop.StatusNotifierItem-%1-%2") 41 | .arg(QCoreApplication::applicationPid()) 42 | .arg(++mServiceCounter)), 43 | mId(std::move(id)), 44 | mTitle(QLatin1String("Test")), 45 | mStatus(QLatin1String("Active")), 46 | mCategory(QLatin1String("ApplicationStatus")), 47 | mMenu(nullptr), 48 | mMenuPath(QLatin1String("/NO_DBUSMENU")), 49 | mMenuExporter(nullptr), 50 | mSessionBus(QDBusConnection::connectToBus(QDBusConnection::SessionBus, mService)) 51 | { 52 | // Separate DBus connection to the session bus is created, because QDbus does not provide 53 | // a way to register different objects for different services with the same paths. 54 | // For status notifiers we need different /StatusNotifierItem for each service. 55 | 56 | // register service 57 | 58 | mSessionBus.registerObject(QLatin1String("/StatusNotifierItem"), this); 59 | 60 | registerToHost(); 61 | 62 | // monitor the watcher service in case the host restarts 63 | QDBusServiceWatcher *watcher = new QDBusServiceWatcher(QLatin1String("org.kde.StatusNotifierWatcher"), 64 | mSessionBus, 65 | QDBusServiceWatcher::WatchForOwnerChange, 66 | this); 67 | connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged, 68 | this, &StatusNotifierItem::onServiceOwnerChanged); 69 | } 70 | 71 | StatusNotifierItem::~StatusNotifierItem() 72 | { 73 | mSessionBus.unregisterObject(QLatin1String("/StatusNotifierItem")); 74 | QDBusConnection::disconnectFromBus(mService); 75 | } 76 | 77 | void StatusNotifierItem::registerToHost() 78 | { 79 | QDBusInterface interface(QLatin1String("org.kde.StatusNotifierWatcher"), 80 | QLatin1String("/StatusNotifierWatcher"), 81 | QLatin1String("org.kde.StatusNotifierWatcher"), 82 | mSessionBus); 83 | interface.asyncCall(QLatin1String("RegisterStatusNotifierItem"), mSessionBus.baseService()); 84 | } 85 | 86 | void StatusNotifierItem::onServiceOwnerChanged(const QString& service, const QString& oldOwner, 87 | const QString& newOwner) 88 | { 89 | Q_UNUSED(service); 90 | Q_UNUSED(oldOwner); 91 | 92 | if (!newOwner.isEmpty()) 93 | registerToHost(); 94 | } 95 | 96 | void StatusNotifierItem::onMenuDestroyed() 97 | { 98 | mMenu = nullptr; 99 | setMenuPath(QLatin1String("/NO_DBUSMENU")); 100 | mMenuExporter = nullptr; //mMenu is a QObject parent of the mMenuExporter 101 | } 102 | 103 | void StatusNotifierItem::setTitle(const QString &title) 104 | { 105 | if (mTitle == title) 106 | return; 107 | 108 | mTitle = title; 109 | Q_EMIT mAdaptor->NewTitle(); 110 | } 111 | 112 | void StatusNotifierItem::setStatus(const QString &status) 113 | { 114 | if (mStatus == status) 115 | return; 116 | 117 | mStatus = status; 118 | Q_EMIT mAdaptor->NewStatus(mStatus); 119 | } 120 | 121 | void StatusNotifierItem::setCategory(const QString &category) 122 | { 123 | if (mCategory == category) 124 | return; 125 | 126 | mCategory = category; 127 | } 128 | 129 | void StatusNotifierItem::setMenuPath(const QString& path) 130 | { 131 | mMenuPath.setPath(path); 132 | } 133 | 134 | void StatusNotifierItem::setIconByName(const QString &name) 135 | { 136 | if (mIconName == name) 137 | return; 138 | 139 | mIconName = name; 140 | Q_EMIT mAdaptor->NewIcon(); 141 | } 142 | 143 | void StatusNotifierItem::setIconByPixmap(const QIcon &icon) 144 | { 145 | if (mIconCacheKey == icon.cacheKey()) 146 | return; 147 | 148 | mIconCacheKey = icon.cacheKey(); 149 | mIcon = iconToPixmapList(icon); 150 | mIconName.clear(); 151 | Q_EMIT mAdaptor->NewIcon(); 152 | } 153 | 154 | void StatusNotifierItem::setOverlayIconByName(const QString &name) 155 | { 156 | if (mOverlayIconName == name) 157 | return; 158 | 159 | mOverlayIconName = name; 160 | Q_EMIT mAdaptor->NewOverlayIcon(); 161 | } 162 | 163 | void StatusNotifierItem::setOverlayIconByPixmap(const QIcon &icon) 164 | { 165 | if (mOverlayIconCacheKey == icon.cacheKey()) 166 | return; 167 | 168 | mOverlayIconCacheKey = icon.cacheKey(); 169 | mOverlayIcon = iconToPixmapList(icon); 170 | mOverlayIconName.clear(); 171 | Q_EMIT mAdaptor->NewOverlayIcon(); 172 | } 173 | 174 | void StatusNotifierItem::setAttentionIconByName(const QString &name) 175 | { 176 | if (mAttentionIconName == name) 177 | return; 178 | 179 | mAttentionIconName = name; 180 | Q_EMIT mAdaptor->NewAttentionIcon(); 181 | } 182 | 183 | void StatusNotifierItem::setAttentionIconByPixmap(const QIcon &icon) 184 | { 185 | if (mAttentionIconCacheKey == icon.cacheKey()) 186 | return; 187 | 188 | mAttentionIconCacheKey = icon.cacheKey(); 189 | mAttentionIcon = iconToPixmapList(icon); 190 | mAttentionIconName.clear(); 191 | Q_EMIT mAdaptor->NewAttentionIcon(); 192 | } 193 | 194 | void StatusNotifierItem::setToolTipTitle(const QString &title) 195 | { 196 | if (mTooltipTitle == title) 197 | return; 198 | 199 | mTooltipTitle = title; 200 | Q_EMIT mAdaptor->NewToolTip(); 201 | } 202 | 203 | void StatusNotifierItem::setToolTipSubTitle(const QString &subTitle) 204 | { 205 | if (mTooltipSubtitle == subTitle) 206 | return; 207 | 208 | mTooltipSubtitle = subTitle; 209 | Q_EMIT mAdaptor->NewToolTip(); 210 | } 211 | 212 | void StatusNotifierItem::setToolTipIconByName(const QString &name) 213 | { 214 | if (mTooltipIconName == name) 215 | return; 216 | 217 | mTooltipIconName = name; 218 | Q_EMIT mAdaptor->NewToolTip(); 219 | } 220 | 221 | void StatusNotifierItem::setToolTipIconByPixmap(const QIcon &icon) 222 | { 223 | if (mTooltipIconCacheKey == icon.cacheKey()) 224 | return; 225 | 226 | mTooltipIconCacheKey = icon.cacheKey(); 227 | mTooltipIcon = iconToPixmapList(icon); 228 | mTooltipIconName.clear(); 229 | Q_EMIT mAdaptor->NewToolTip(); 230 | } 231 | 232 | void StatusNotifierItem::setContextMenu(QMenu* menu) 233 | { 234 | if (mMenu == menu) 235 | return; 236 | 237 | if (nullptr != mMenu) 238 | { 239 | disconnect(mMenu, &QObject::destroyed, this, &StatusNotifierItem::onMenuDestroyed); 240 | } 241 | mMenu = menu; 242 | 243 | if (nullptr != mMenu) 244 | setMenuPath(QLatin1String("/MenuBar")); 245 | else 246 | setMenuPath(QLatin1String("/NO_DBUSMENU")); 247 | 248 | //Note: we need to destroy menu exporter before creating new one -> to free the DBus object path for new menu 249 | delete mMenuExporter; 250 | if (nullptr != mMenu) 251 | { 252 | connect(mMenu, &QObject::destroyed, this, &StatusNotifierItem::onMenuDestroyed); 253 | mMenuExporter = new DBusMenuExporter{this->menu().path(), mMenu, mSessionBus}; 254 | } 255 | } 256 | 257 | void StatusNotifierItem::Activate(int x, int y) 258 | { 259 | if (mStatus == QLatin1String("NeedsAttention")) 260 | mStatus = QLatin1String("Active"); 261 | 262 | Q_EMIT activateRequested(QPoint(x, y)); 263 | } 264 | 265 | void StatusNotifierItem::SecondaryActivate(int x, int y) 266 | { 267 | if (mStatus == QLatin1String("NeedsAttention")) 268 | mStatus = QLatin1String("Active"); 269 | 270 | Q_EMIT secondaryActivateRequested(QPoint(x, y)); 271 | } 272 | 273 | void StatusNotifierItem::ContextMenu(int x, int y) 274 | { 275 | if (mMenu != nullptr) 276 | { 277 | if (mMenu->isVisible()) 278 | mMenu->popup(QPoint(x, y)); 279 | else 280 | mMenu->hide(); 281 | } 282 | } 283 | 284 | void StatusNotifierItem::Scroll(int delta, const QString &orientation) 285 | { 286 | Qt::Orientation orient = Qt::Vertical; 287 | if (orientation.toLower() == QLatin1String("horizontal")) 288 | orient = Qt::Horizontal; 289 | 290 | Q_EMIT scrollRequested(delta, orient); 291 | } 292 | 293 | void StatusNotifierItem::showMessage(const QString& title, const QString& msg, 294 | const QString& iconName, int secs) 295 | { 296 | QDBusInterface interface(QLatin1String("org.freedesktop.Notifications"), QLatin1String("/org/freedesktop/Notifications"), 297 | QLatin1String("org.freedesktop.Notifications"), mSessionBus); 298 | interface.call(QLatin1String("Notify"), mTitle, (uint) 0, iconName, title, 299 | msg, QStringList(), QVariantMap(), secs); 300 | } 301 | 302 | IconPixmapList StatusNotifierItem::iconToPixmapList(const QIcon& icon) 303 | { 304 | IconPixmapList pixmapList; 305 | 306 | // long live KDE! 307 | const QList sizes = icon.availableSizes(); 308 | for (const QSize &size : sizes) 309 | { 310 | QImage image = icon.pixmap(size).toImage(); 311 | 312 | IconPixmap pix; 313 | pix.height = image.height(); 314 | pix.width = image.width(); 315 | 316 | if (image.format() != QImage::Format_ARGB32) 317 | image = image.convertToFormat(QImage::Format_ARGB32); 318 | 319 | pix.bytes = QByteArray((char *) image.bits(), 320 | image.sizeInBytes()); 321 | 322 | // swap to network byte order if we are little endian 323 | if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) 324 | { 325 | quint32 *uintBuf = (quint32 *) pix.bytes.data(); 326 | for (uint i = 0; i < pix.bytes.size() / sizeof(quint32); ++i) 327 | { 328 | *uintBuf = qToBigEndian(*uintBuf); 329 | ++uintBuf; 330 | } 331 | } 332 | 333 | pixmapList.append(pix); 334 | } 335 | 336 | return pixmapList; 337 | } 338 | -------------------------------------------------------------------------------- /src/statusnotifieritem/statusnotifieritem.h: -------------------------------------------------------------------------------- 1 | /* BEGIN_COMMON_COPYRIGHT_HEADER 2 | * (c)LGPL2+ 3 | * 4 | * LXQt - a lightweight, Qt based, desktop toolset 5 | * https://lxqt.org/ 6 | * 7 | * Copyright: 2015 LXQt team 8 | * Authors: 9 | * Paulo Lieuthier 10 | * 11 | * This program or library is free software; you can redistribute it 12 | * and/or modify it under the terms of the GNU Lesser General Public 13 | * License as published by the Free Software Foundation; either 14 | * version 2.1 of the License, or (at your option) any later version. 15 | * 16 | * This library is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 19 | * Lesser General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General 22 | * Public License along with this library; if not, write to the 23 | * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 | * Boston, MA 02110-1301 USA 25 | * 26 | * END_COMMON_COPYRIGHT_HEADER */ 27 | 28 | 29 | #ifndef STATUS_NOTIFIER_ITEM_H 30 | #define STATUS_NOTIFIER_ITEM_H 31 | 32 | #include 33 | #include 34 | #include 35 | #include 36 | 37 | #include "dbustypes.h" 38 | 39 | class StatusNotifierItemAdaptor; 40 | class DBusMenuExporter; 41 | 42 | class StatusNotifierItem : public QObject 43 | { 44 | Q_OBJECT 45 | 46 | Q_PROPERTY(QString Category READ category) 47 | Q_PROPERTY(QString Title READ title) 48 | Q_PROPERTY(QString Id READ id) 49 | Q_PROPERTY(QString Status READ status) 50 | Q_PROPERTY(QDBusObjectPath Menu READ menu) 51 | 52 | Q_PROPERTY(QString IconName READ iconName) 53 | Q_PROPERTY(IconPixmapList IconPixmap READ iconPixmap) 54 | 55 | Q_PROPERTY(QString OverlayIconName READ overlayIconName) 56 | Q_PROPERTY(IconPixmapList OverlayIconPixmap READ overlayIconPixmap) 57 | 58 | Q_PROPERTY(QString AttentionIconName READ attentionIconName) 59 | Q_PROPERTY(IconPixmapList AttentionIconPixmap READ attentionIconPixmap) 60 | 61 | Q_PROPERTY(ToolTip ToolTip READ toolTip) 62 | 63 | public: 64 | StatusNotifierItem(QString id, QObject *parent = nullptr); 65 | ~StatusNotifierItem() override; 66 | 67 | QString id() const 68 | { return mId; } 69 | 70 | QString title() const 71 | { return mTitle; } 72 | void setTitle(const QString &title); 73 | 74 | QString status() const 75 | { return mStatus; } 76 | void setStatus(const QString &status); 77 | 78 | QString category() const 79 | { return mCategory; } 80 | void setCategory(const QString &category); 81 | 82 | QDBusObjectPath menu() const 83 | { return mMenuPath; } 84 | void setMenuPath(const QString &path); 85 | 86 | QString iconName() const 87 | { return mIconName; } 88 | void setIconByName(const QString &name); 89 | 90 | IconPixmapList iconPixmap() const 91 | { return mIcon; } 92 | void setIconByPixmap(const QIcon &icon); 93 | 94 | QString overlayIconName() const 95 | { return mOverlayIconName; } 96 | void setOverlayIconByName(const QString &name); 97 | 98 | IconPixmapList overlayIconPixmap() const 99 | { return mOverlayIcon; } 100 | void setOverlayIconByPixmap(const QIcon &icon); 101 | 102 | QString attentionIconName() const 103 | { return mAttentionIconName; } 104 | void setAttentionIconByName(const QString &name); 105 | 106 | IconPixmapList attentionIconPixmap() const 107 | { return mAttentionIcon; } 108 | void setAttentionIconByPixmap(const QIcon &icon); 109 | 110 | QString toolTipTitle() const 111 | { return mTooltipTitle; } 112 | void setToolTipTitle(const QString &title); 113 | 114 | QString toolTipSubTitle() const 115 | { return mTooltipSubtitle; } 116 | void setToolTipSubTitle(const QString &subTitle); 117 | 118 | QString toolTipIconName() const 119 | { return mTooltipIconName; } 120 | void setToolTipIconByName(const QString &name); 121 | 122 | IconPixmapList toolTipIconPixmap() const 123 | { return mTooltipIcon; } 124 | void setToolTipIconByPixmap(const QIcon &icon); 125 | 126 | ToolTip toolTip() const 127 | { 128 | ToolTip tt; 129 | tt.title = mTooltipTitle; 130 | tt.description = mTooltipSubtitle; 131 | tt.iconName = mTooltipIconName; 132 | tt.iconPixmap = mTooltipIcon; 133 | return tt; 134 | } 135 | 136 | /*! 137 | * \Note: we don't take ownership for the \param menu 138 | */ 139 | void setContextMenu(QMenu *menu); 140 | 141 | public Q_SLOTS: 142 | void Activate(int x, int y); 143 | void SecondaryActivate(int x, int y); 144 | void ContextMenu(int x, int y); 145 | void Scroll(int delta, const QString &orientation); 146 | 147 | void showMessage(const QString &title, const QString &msg, const QString &iconName, int secs); 148 | 149 | private: 150 | void registerToHost(); 151 | IconPixmapList iconToPixmapList(const QIcon &icon); 152 | 153 | private Q_SLOTS: 154 | void onServiceOwnerChanged(const QString &service, const QString &oldOwner, 155 | const QString &newOwner); 156 | void onMenuDestroyed(); 157 | 158 | Q_SIGNALS: 159 | void activateRequested(const QPoint &pos); 160 | void secondaryActivateRequested(const QPoint &pos); 161 | void scrollRequested(int delta, Qt::Orientation orientation); 162 | 163 | private: 164 | StatusNotifierItemAdaptor *mAdaptor; 165 | 166 | QString mService; 167 | QString mId; 168 | QString mTitle; 169 | QString mStatus; 170 | QString mCategory; 171 | 172 | // icons 173 | QString mIconName, mOverlayIconName, mAttentionIconName; 174 | IconPixmapList mIcon, mOverlayIcon, mAttentionIcon; 175 | qint64 mIconCacheKey, mOverlayIconCacheKey, mAttentionIconCacheKey; 176 | 177 | // tooltip 178 | QString mTooltipTitle, mTooltipSubtitle, mTooltipIconName; 179 | IconPixmapList mTooltipIcon; 180 | qint64 mTooltipIconCacheKey; 181 | 182 | // menu 183 | QMenu *mMenu; 184 | QDBusObjectPath mMenuPath; 185 | DBusMenuExporter *mMenuExporter; 186 | QDBusConnection mSessionBus; 187 | 188 | static int mServiceCounter; 189 | }; 190 | 191 | #endif 192 | --------------------------------------------------------------------------------