Merge lp:~mitya57/appmenu-qt5/menus-and-systemtrayicons into lp:appmenu-qt5
- menus-and-systemtrayicons
- Merge into trunk
Status: | Merged | ||||
---|---|---|---|---|---|
Approved by: | Łukasz Zemczak | ||||
Approved revision: | 28 | ||||
Merged at revision: | 29 | ||||
Proposed branch: | lp:~mitya57/appmenu-qt5/menus-and-systemtrayicons | ||||
Merge into: | lp:appmenu-qt5 | ||||
Diff against target: |
1241 lines (+1139/-5) 13 files modified
src/appmenuplatformmenu.cpp (+129/-0) src/appmenuplatformmenu.h (+59/-0) src/appmenuplatformmenubar.cpp (+11/-2) src/appmenuplatformmenuitem.cpp (+124/-0) src/appmenuplatformmenuitem.h (+54/-0) src/appmenuplatformsystemtrayicon.cpp (+235/-0) src/appmenuplatformsystemtrayicon.h (+101/-0) src/dbusstructures.cpp (+64/-0) src/dbusstructures.h (+58/-0) src/iconcache.cpp (+140/-0) src/iconcache.h (+54/-0) src/org.kde.StatusNotifierItem.xml (+96/-0) src/src.pro (+14/-3) |
||||
To merge this branch: | bzr merge lp:~mitya57/appmenu-qt5/menus-and-systemtrayicons | ||||
Related bugs: |
|
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Łukasz Zemczak | Approve | ||
Indicator Applet Developers | Pending | ||
Review via email: mp+244545@code.launchpad.net |
Commit message
Add implementations of QPlatformMenuItem, QPlatformMenu and QPlatformSystem
Description of the change
This adds new AppMenuPlatform
Please read the QLTBA (questions likely to be asked) in the commit message.
How to test it: you need either Qt 5.4 from ppa:canonical-
- 27. By Dmitry Shachnev
-
Add AppMenuPlatform
MenuItem and AppMenuPlatformMenu classes Having these classes will allow us to implement missing functionality
for AppMenuPlatformMenuBar class, and also add a new class for system
tray icons (AppMenuPlatformSystemTrayIcon ). - 28. By Dmitry Shachnev
-
Add AppMenuPlatform
SystemTrayIcon implementation Q: What’s this for?
A: Currently Qt 5 desktop applications which try to display a system
tray icon will fail to do so in Unity desktop, because we abandoned
the old XEmbed-based tray. This branch will make it work again, and
in a nicer way. As a side-effect, notifications will now work too.Q: We already have sni-qt, why is this needed?
A: sni-qt was only working with Qt 4, it does not work with Qt 5.
Actually, a big part of code in this commit is copied from sni-qt.Q: Why not just port sni-qt to Qt 5, why this code should be here?
A: In Qt 5, features such as platform tray icon should be implemented
using Qt Platform Abstraction (QPA), which was not the case in Qt 4.
More important, things like platform menu bar and platform tray icon
should be in the same plugin, called “platform theme”. There should be
only one platform theme for a platform, in Ubuntu it is appmenu-qt5.Q: So is it just a copy of sni-qt code?
A: Most of it is based on sni-qt code, but it was changed a lot to work
with Qt 5 and QPA interface. I also cleaned it up a bit and deleted
some stuff that I think is not relevant.Q: Does it add any additional dependencies to appmenu-qt5?
A: No. The only third-party library it needs is libdbusmenu-qt5, which
is already used by appmenu-qt5.Q: How does it work?
A: There are two things we should export — the “status notifier item”
(which contains icon, tooltip, etc) and the menu. While the status
notifier item will work with any server-side implementation, the menu
will only work with ours (indicator-application) . When
indicator-application sees that an item has a menu, it will convert
it into an indicator, and any indicator renderer (such as Unity, or
indicator-applet) will show it. Q: Which desktop environments will it work with?
A: It is designed to work with anything that supports indicators, like
Unity or GNOME Flashback.Q: Why the interface has KDE in name?
A: The StatusNotifierItem was originally developed by KDE project. While
it is very possible that this standard will be adopted by FreeDesktop,
indicator-application currently only supports the KDE name. Q: Can KDE reuse this implementation?
A: They already have their own implementation, in Framework Integration.
I could not reuse their code because they have not signed the
Canonical contributor license agreement.
Dmitry Shachnev (mitya57) wrote : | # |
Łukasz Zemczak (sil2100) wrote : | # |
Looks really good so far. I would like to do a few additional tests tomorrow morning still. Come to think of it, maybe soon we might need to think of changing the name from appmenu-qt5 to something more 'ubuntu-generic'.
Dmitry Shachnev (mitya57) wrote : | # |
Yes, I want to get bug 1378935 fixed (when I have time for it), and after that it will become a full-featured platform theme. Maybe ubuntu-
Dmitry Shachnev (mitya57) wrote : | # |
I have just been pointed to https:/
Łukasz Zemczak (sil2100) wrote : | # |
Ok, the code looks good. I didn't know too much of the old sni-qt code, but it seems everything we need is now included here as well. And let's not worry about future plans for Qt5, we can deprecate this when the need arises. For this cycle we're targeting 5.4 and I'm not sure if we'd jump to an even higher revision before release still.
Dmitry Shachnev (mitya57) wrote : | # |
Hi Łukasz, are you going to land my branches, or should I take care of that? (Or do you have other pending changes?)
Preview Diff
1 | === added file 'src/appmenuplatformmenu.cpp' |
2 | --- src/appmenuplatformmenu.cpp 1970-01-01 00:00:00 +0000 |
3 | +++ src/appmenuplatformmenu.cpp 2014-12-13 15:39:31 +0000 |
4 | @@ -0,0 +1,129 @@ |
5 | +/* |
6 | + * Copyright 2014 Dmitry Shachnev <mitya57@ubuntu.com> |
7 | + * |
8 | + * This program is free software: you can redistribute it and/or modify |
9 | + * it under the terms of the GNU Lesser General Public License as published by |
10 | + * the Free Software Foundation, version 3 of the License. |
11 | + * |
12 | + * This program is distributed in the hope that it will be useful, |
13 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | + * GNU Lesser General Public License for more details. |
16 | + * |
17 | + * You should have received a copy of the GNU Lesser General Public License |
18 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | + */ |
20 | + |
21 | +#include "appmenuplatformmenuitem.h" |
22 | +#include "appmenuplatformmenu.h" |
23 | + |
24 | +AppMenuPlatformMenu::AppMenuPlatformMenu(): |
25 | + m_menu(new QMenu()), |
26 | + m_tag(0) |
27 | +{ |
28 | + connect(m_menu, &QMenu::aboutToShow, this, &QPlatformMenu::aboutToShow); |
29 | + connect(m_menu, &QMenu::aboutToHide, this, &QPlatformMenu::aboutToHide); |
30 | +} |
31 | + |
32 | +AppMenuPlatformMenu::~AppMenuPlatformMenu() |
33 | +{ |
34 | + delete m_menu; |
35 | +} |
36 | + |
37 | +void AppMenuPlatformMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) |
38 | +{ |
39 | + AppMenuPlatformMenuItem *myMenuItem = qobject_cast<AppMenuPlatformMenuItem *>(menuItem); |
40 | + AppMenuPlatformMenuItem *myBefore = qobject_cast<AppMenuPlatformMenuItem *>(before); |
41 | + |
42 | + QAction *beforeAction = myBefore ? myBefore->m_action : Q_NULLPTR; |
43 | + m_menu->insertAction(beforeAction, myMenuItem->m_action); |
44 | + |
45 | + if (!myBefore) { |
46 | + m_menuitems.append(myMenuItem); |
47 | + return; |
48 | + } |
49 | + |
50 | + for (int i = 0; i < m_menuitems.size(); ++i) { |
51 | + if (m_menuitems.at(i)->m_action == beforeAction) { |
52 | + m_menuitems.insert(i, myMenuItem); |
53 | + return; |
54 | + } |
55 | + } |
56 | +} |
57 | + |
58 | +void AppMenuPlatformMenu::removeMenuItem(QPlatformMenuItem *menuItem) |
59 | +{ |
60 | + AppMenuPlatformMenuItem *myMenuItem = qobject_cast<AppMenuPlatformMenuItem *>(menuItem); |
61 | + m_menuitems.removeOne(myMenuItem); |
62 | + m_menu->removeAction(myMenuItem->m_action); |
63 | +} |
64 | + |
65 | +void AppMenuPlatformMenu::syncMenuItem(QPlatformMenuItem *menuItem) |
66 | +{ |
67 | + Q_UNUSED(menuItem); |
68 | +} |
69 | + |
70 | +void AppMenuPlatformMenu::syncSeparatorsCollapsible(bool enable) |
71 | +{ |
72 | + m_menu->setSeparatorsCollapsible(enable); |
73 | +} |
74 | + |
75 | +void AppMenuPlatformMenu::setTag(quintptr tag) |
76 | +{ |
77 | + m_tag = tag; |
78 | +} |
79 | + |
80 | +quintptr AppMenuPlatformMenu::tag() const |
81 | +{ |
82 | + return m_tag; |
83 | +} |
84 | + |
85 | +void AppMenuPlatformMenu::setText(const QString &text) |
86 | +{ |
87 | + m_menu->setTitle(text); |
88 | +} |
89 | + |
90 | +void AppMenuPlatformMenu::setIcon(const QIcon &icon) |
91 | +{ |
92 | + m_menu->setIcon(icon); |
93 | +} |
94 | + |
95 | +void AppMenuPlatformMenu::setEnabled(bool enabled) |
96 | +{ |
97 | + m_menu->setEnabled(enabled); |
98 | +} |
99 | + |
100 | +void AppMenuPlatformMenu::setVisible(bool visible) |
101 | +{ |
102 | + m_menu->setVisible(visible); |
103 | +} |
104 | + |
105 | +void AppMenuPlatformMenu::setMinimumWidth(int width) |
106 | +{ |
107 | + m_menu->setMinimumWidth(width); |
108 | +} |
109 | + |
110 | +void AppMenuPlatformMenu::setFont(const QFont &font) |
111 | +{ |
112 | + m_menu->setFont(font); |
113 | +} |
114 | + |
115 | +QPlatformMenuItem *AppMenuPlatformMenu::menuItemAt(int position) const |
116 | +{ |
117 | + return m_menuitems.at(position); |
118 | +} |
119 | + |
120 | +QPlatformMenuItem *AppMenuPlatformMenu::menuItemForTag(quintptr tag) const |
121 | +{ |
122 | + foreach (AppMenuPlatformMenuItem *item, m_menuitems) { |
123 | + if (item->m_tag == tag) { |
124 | + return item; |
125 | + } |
126 | + } |
127 | + return Q_NULLPTR; |
128 | +} |
129 | + |
130 | +QPlatformMenuItem *AppMenuPlatformMenu::createMenuItem() const |
131 | +{ |
132 | + return new AppMenuPlatformMenuItem(); |
133 | +} |
134 | |
135 | === added file 'src/appmenuplatformmenu.h' |
136 | --- src/appmenuplatformmenu.h 1970-01-01 00:00:00 +0000 |
137 | +++ src/appmenuplatformmenu.h 2014-12-13 15:39:31 +0000 |
138 | @@ -0,0 +1,59 @@ |
139 | +/* |
140 | + * Copyright 2014 Dmitry Shachnev <mitya57@ubuntu.com> |
141 | + * |
142 | + * This program is free software: you can redistribute it and/or modify |
143 | + * it under the terms of the GNU Lesser General Public License as published by |
144 | + * the Free Software Foundation, version 3 of the License. |
145 | + * |
146 | + * This program is distributed in the hope that it will be useful, |
147 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
148 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
149 | + * GNU Lesser General Public License for more details. |
150 | + * |
151 | + * You should have received a copy of the GNU Lesser General Public License |
152 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
153 | + */ |
154 | + |
155 | +#ifndef APPMENUPLATFORMMENU_H |
156 | +#define APPMENUPLATFORMMENU_H |
157 | + |
158 | +#include <QtCore/qlist.h> |
159 | +#include <QtWidgets/qmenu.h> |
160 | +#include <qpa/qplatformmenu.h> |
161 | +#include "appmenuplatformmenuitem.h" |
162 | + |
163 | +class AppMenuPlatformMenu : public QPlatformMenu |
164 | +{ |
165 | + Q_OBJECT |
166 | + friend class AppMenuPlatformMenuItem; |
167 | + friend class AppMenuPlatformSystemTrayIcon; |
168 | + |
169 | +public: |
170 | + AppMenuPlatformMenu(); |
171 | + virtual ~AppMenuPlatformMenu(); |
172 | + |
173 | + virtual void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before); |
174 | + virtual void removeMenuItem(QPlatformMenuItem *menuItem); |
175 | + virtual void syncMenuItem(QPlatformMenuItem *menuItem); |
176 | + virtual void syncSeparatorsCollapsible(bool enable); |
177 | + virtual void setTag(quintptr tag); |
178 | + virtual quintptr tag() const; |
179 | + virtual void setText(const QString &text); |
180 | + virtual void setIcon(const QIcon &icon); |
181 | + virtual void setEnabled(bool enabled); |
182 | + virtual void setVisible(bool visible); |
183 | + virtual void setMinimumWidth(int width); |
184 | + virtual void setFont(const QFont &font); |
185 | + virtual QPlatformMenuItem *menuItemAt(int position) const; |
186 | + virtual QPlatformMenuItem *menuItemForTag(quintptr tag) const; |
187 | + virtual QPlatformMenuItem *createMenuItem() const; |
188 | + |
189 | +private: |
190 | + Q_DISABLE_COPY(AppMenuPlatformMenu) |
191 | + |
192 | + QMenu *m_menu; |
193 | + QList<AppMenuPlatformMenuItem *> m_menuitems; |
194 | + quintptr m_tag; |
195 | +}; |
196 | + |
197 | +#endif /* APPMENUPLATFORMMENU_H */ |
198 | |
199 | === modified file 'src/appmenuplatformmenubar.cpp' |
200 | --- src/appmenuplatformmenubar.cpp 2014-11-24 15:17:32 +0000 |
201 | +++ src/appmenuplatformmenubar.cpp 2014-12-13 15:39:31 +0000 |
202 | @@ -17,6 +17,8 @@ |
203 | */ |
204 | |
205 | #include "appmenuplatformmenubar.h" |
206 | +#include "appmenuplatformmenuitem.h" |
207 | +#include "appmenuplatformsystemtrayicon.h" |
208 | #include "registrar_interface.h" |
209 | |
210 | // Ugly, but sadly we need to use private headers for desktop-theme related classes |
211 | @@ -243,9 +245,14 @@ |
212 | { |
213 | public: |
214 | GnomeAppMenuPlatformTheme(); |
215 | - virtual QPlatformMenuItem* createPlatformMenuItem() const { return 0; } |
216 | + virtual QPlatformMenuItem* createPlatformMenuItem() const { |
217 | + return new AppMenuPlatformMenuItem(); |
218 | + } |
219 | virtual QPlatformMenu* createPlatformMenu() const { return 0; } |
220 | virtual QPlatformMenuBar* createPlatformMenuBar() const; |
221 | + virtual QPlatformSystemTrayIcon* createPlatformSystemTrayIcon() const { |
222 | + return new AppMenuPlatformSystemTrayIcon(); |
223 | + } |
224 | |
225 | virtual QVariant themeHint(QPlatformTheme::ThemeHint hint) const; |
226 | }; |
227 | @@ -304,7 +311,9 @@ |
228 | { |
229 | } |
230 | #endif |
231 | - virtual QPlatformMenuItem* createPlatformMenuItem() const { return 0; } |
232 | + virtual QPlatformMenuItem* createPlatformMenuItem() const { |
233 | + return new AppMenuPlatformMenuItem(); |
234 | + } |
235 | virtual QPlatformMenu* createPlatformMenu() const { return 0; } |
236 | virtual QPlatformMenuBar* createPlatformMenuBar() const; |
237 | }; |
238 | |
239 | === added file 'src/appmenuplatformmenuitem.cpp' |
240 | --- src/appmenuplatformmenuitem.cpp 1970-01-01 00:00:00 +0000 |
241 | +++ src/appmenuplatformmenuitem.cpp 2014-12-13 15:39:31 +0000 |
242 | @@ -0,0 +1,124 @@ |
243 | +/* |
244 | + * Copyright 2014 Dmitry Shachnev <mitya57@ubuntu.com> |
245 | + * |
246 | + * This program is free software: you can redistribute it and/or modify |
247 | + * it under the terms of the GNU Lesser General Public License as published by |
248 | + * the Free Software Foundation, version 3 of the License. |
249 | + * |
250 | + * This program is distributed in the hope that it will be useful, |
251 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
252 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
253 | + * GNU Lesser General Public License for more details. |
254 | + * |
255 | + * You should have received a copy of the GNU Lesser General Public License |
256 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
257 | + */ |
258 | + |
259 | +#include "appmenuplatformmenuitem.h" |
260 | +#include "appmenuplatformmenu.h" |
261 | + |
262 | +AppMenuPlatformMenuItem::AppMenuPlatformMenuItem(): |
263 | + m_action(new QAction(this)), |
264 | + m_tag(0) |
265 | +{ |
266 | + connect(m_action, &QAction::triggered, this, &QPlatformMenuItem::activated); |
267 | + connect(m_action, &QAction::hovered, this, &QPlatformMenuItem::hovered); |
268 | +} |
269 | + |
270 | +AppMenuPlatformMenuItem::~AppMenuPlatformMenuItem() |
271 | +{ |
272 | + delete m_action; |
273 | +} |
274 | + |
275 | +void AppMenuPlatformMenuItem::setTag(quintptr tag) |
276 | +{ |
277 | + m_tag = tag; |
278 | +} |
279 | + |
280 | +quintptr AppMenuPlatformMenuItem::tag() const |
281 | +{ |
282 | + return m_tag; |
283 | +} |
284 | + |
285 | +void AppMenuPlatformMenuItem::setText(const QString &text) |
286 | +{ |
287 | + m_action->setText(text); |
288 | +} |
289 | + |
290 | +void AppMenuPlatformMenuItem::setIcon(const QIcon &icon) |
291 | +{ |
292 | + m_action->setIcon(icon); |
293 | +} |
294 | + |
295 | +void AppMenuPlatformMenuItem::setMenu(QPlatformMenu *menu) |
296 | +{ |
297 | + if (menu) { |
298 | + AppMenuPlatformMenu *myMenu = qobject_cast<AppMenuPlatformMenu *>(menu); |
299 | + m_action->setMenu(myMenu->m_menu); |
300 | + } |
301 | +} |
302 | + |
303 | +void AppMenuPlatformMenuItem::setVisible(bool isVisible) |
304 | +{ |
305 | + m_action->setVisible(isVisible); |
306 | +} |
307 | + |
308 | +void AppMenuPlatformMenuItem::setIsSeparator(bool isSeparator) |
309 | +{ |
310 | + m_action->setSeparator(isSeparator); |
311 | +} |
312 | + |
313 | +void AppMenuPlatformMenuItem::setFont(const QFont &font) |
314 | +{ |
315 | + m_action->setFont(font); |
316 | +} |
317 | + |
318 | +void AppMenuPlatformMenuItem::setRole(MenuRole role) |
319 | +{ |
320 | + QAction::MenuRole newRole; |
321 | + switch (role) { |
322 | + case TextHeuristicRole: |
323 | + newRole = QAction::TextHeuristicRole; |
324 | + break; |
325 | + case ApplicationSpecificRole: |
326 | + newRole = QAction::ApplicationSpecificRole; |
327 | + break; |
328 | + case AboutRole: |
329 | + newRole = QAction::AboutRole; |
330 | + break; |
331 | + case PreferencesRole: |
332 | + newRole = QAction::PreferencesRole; |
333 | + break; |
334 | + case QuitRole: |
335 | + newRole = QAction::QuitRole; |
336 | + break; |
337 | + default: |
338 | + newRole = QAction::NoRole; |
339 | + } |
340 | + m_action->setMenuRole(newRole); |
341 | +} |
342 | + |
343 | +void AppMenuPlatformMenuItem::setCheckable(bool checkable) |
344 | +{ |
345 | + m_action->setCheckable(checkable); |
346 | +} |
347 | + |
348 | +void AppMenuPlatformMenuItem::setChecked(bool isChecked) |
349 | +{ |
350 | + m_action->setChecked(isChecked); |
351 | +} |
352 | + |
353 | +void AppMenuPlatformMenuItem::setShortcut(const QKeySequence& shortcut) |
354 | +{ |
355 | + m_action->setShortcut(shortcut); |
356 | +} |
357 | + |
358 | +void AppMenuPlatformMenuItem::setEnabled(bool enabled) |
359 | +{ |
360 | + m_action->setEnabled(enabled); |
361 | +} |
362 | + |
363 | +void AppMenuPlatformMenuItem::setIconSize(int size) |
364 | +{ |
365 | + Q_UNUSED(size); |
366 | +} |
367 | |
368 | === added file 'src/appmenuplatformmenuitem.h' |
369 | --- src/appmenuplatformmenuitem.h 1970-01-01 00:00:00 +0000 |
370 | +++ src/appmenuplatformmenuitem.h 2014-12-13 15:39:31 +0000 |
371 | @@ -0,0 +1,54 @@ |
372 | +/* |
373 | + * Copyright 2014 Dmitry Shachnev <mitya57@ubuntu.com> |
374 | + * |
375 | + * This program is free software: you can redistribute it and/or modify |
376 | + * it under the terms of the GNU Lesser General Public License as published by |
377 | + * the Free Software Foundation, version 3 of the License. |
378 | + * |
379 | + * This program is distributed in the hope that it will be useful, |
380 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
381 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
382 | + * GNU Lesser General Public License for more details. |
383 | + * |
384 | + * You should have received a copy of the GNU Lesser General Public License |
385 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
386 | + */ |
387 | + |
388 | +#ifndef APPMENUPLATFORMMENUITEM_H |
389 | +#define APPMENUPLATFORMMENUITEM_H |
390 | + |
391 | +#include <QtWidgets/qaction.h> |
392 | +#include <qpa/qplatformmenu.h> |
393 | + |
394 | +class AppMenuPlatformMenuItem : public QPlatformMenuItem |
395 | +{ |
396 | + Q_OBJECT |
397 | + friend class AppMenuPlatformMenu; |
398 | + |
399 | +public: |
400 | + AppMenuPlatformMenuItem(); |
401 | + virtual ~AppMenuPlatformMenuItem(); |
402 | + |
403 | + virtual void setTag(quintptr tag); |
404 | + virtual quintptr tag() const; |
405 | + virtual void setText(const QString &text); |
406 | + virtual void setIcon(const QIcon &icon); |
407 | + virtual void setMenu(QPlatformMenu *menu); |
408 | + virtual void setVisible(bool isVisible); |
409 | + virtual void setIsSeparator(bool isSeparator); |
410 | + virtual void setFont(const QFont &font); |
411 | + virtual void setRole(MenuRole role); |
412 | + virtual void setCheckable(bool checkable); |
413 | + virtual void setChecked(bool isChecked); |
414 | + virtual void setShortcut(const QKeySequence& shortcut); |
415 | + virtual void setEnabled(bool enabled); |
416 | + virtual void setIconSize(int size); |
417 | + |
418 | +private: |
419 | + Q_DISABLE_COPY(AppMenuPlatformMenuItem) |
420 | + |
421 | + QAction *m_action; |
422 | + quintptr m_tag; |
423 | +}; |
424 | + |
425 | +#endif /* APPMENUPLATFORMMENUITEM_H */ |
426 | |
427 | === added file 'src/appmenuplatformsystemtrayicon.cpp' |
428 | --- src/appmenuplatformsystemtrayicon.cpp 1970-01-01 00:00:00 +0000 |
429 | +++ src/appmenuplatformsystemtrayicon.cpp 2014-12-13 15:39:31 +0000 |
430 | @@ -0,0 +1,235 @@ |
431 | +/* |
432 | + * Copyright 2011 Canonical Ltd. |
433 | + * Copyright 2014 Dmitry Shachnev <mitya57@ubuntu.com> |
434 | + * |
435 | + * This program is free software: you can redistribute it and/or modify |
436 | + * it under the terms of the GNU Lesser General Public License as published by |
437 | + * the Free Software Foundation, version 3 of the License. |
438 | + * |
439 | + * This program is distributed in the hope that it will be useful, |
440 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
441 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
442 | + * GNU Lesser General Public License for more details. |
443 | + * |
444 | + * You should have received a copy of the GNU Lesser General Public License |
445 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
446 | + * |
447 | + * Based on sni-qt code by Aurelien Gateau <aurelien.gateau@canonical.com>. |
448 | + */ |
449 | + |
450 | +#include <QtCore/qcoreapplication.h> |
451 | +#include <QtDBus/qdbusconnection.h> |
452 | +#include <QtDBus/qdbusconnectioninterface.h> |
453 | +#include <QtDBus/qdbusinterface.h> |
454 | +#include "appmenuplatformmenu.h" |
455 | +#include "appmenuplatformsystemtrayicon.h" |
456 | +#include "iconcache.h" |
457 | + |
458 | +static const char *SNW_INTERFACE = "org.kde.StatusNotifierWatcher"; |
459 | +static const char *SNW_SERVICE = "org.kde.StatusNotifierWatcher"; |
460 | +static const char *SNW_PATH = "/StatusNotifierWatcher"; |
461 | + |
462 | +static const char *NOTIFICATION_INTERFACE = "org.freedesktop.Notifications"; |
463 | +static const char *NOTIFICATION_SERVICE = "org.freedesktop.Notifications"; |
464 | +static const char *NOTIFICATION_PATH = "/org/freedesktop/Notifications"; |
465 | + |
466 | +static IconCache iconCache; |
467 | + |
468 | +AppMenuPlatformSystemTrayIcon::AppMenuPlatformSystemTrayIcon(): |
469 | + m_sniAdaptor(new StatusNotifierItemAdaptor(this)), |
470 | + m_dbusMenuExporter(Q_NULLPTR) |
471 | +{ |
472 | + static int id = 1; |
473 | + m_objectPath = QString("/org/kde/statusnotifieritem/%1").arg(id++); |
474 | + |
475 | + registerMetaTypes(); |
476 | + QDBusConnection bus = QDBusConnection::sessionBus(); |
477 | + bus.registerObject(m_objectPath, this, QDBusConnection::ExportAdaptors); |
478 | + QDBusInterface snw(SNW_SERVICE, SNW_PATH, SNW_INTERFACE); |
479 | + snw.asyncCall("RegisterStatusNotifierItem", m_objectPath); |
480 | +} |
481 | + |
482 | +AppMenuPlatformSystemTrayIcon::~AppMenuPlatformSystemTrayIcon() |
483 | +{ |
484 | + delete m_sniAdaptor; |
485 | +} |
486 | + |
487 | +// QPlatformSystemTrayIcon implementation |
488 | + |
489 | +void AppMenuPlatformSystemTrayIcon::init() |
490 | +{ |
491 | + m_status = QStringLiteral("Active"); |
492 | + emit NewStatus(m_status); |
493 | +} |
494 | + |
495 | +void AppMenuPlatformSystemTrayIcon::cleanup() |
496 | +{ |
497 | + m_status = QStringLiteral("Passive"); |
498 | + emit NewStatus(m_status); |
499 | +} |
500 | + |
501 | +void AppMenuPlatformSystemTrayIcon::updateIcon(const QIcon &icon) |
502 | +{ |
503 | + m_icon = icon; |
504 | + emit NewIcon(); |
505 | + emit NewToolTip(); // ToolTip contains the icon |
506 | +} |
507 | + |
508 | +void AppMenuPlatformSystemTrayIcon::updateToolTip(const QString &tooltip) |
509 | +{ |
510 | + m_tooltip = tooltip; |
511 | + emit NewToolTip(); |
512 | +} |
513 | + |
514 | +void AppMenuPlatformSystemTrayIcon::updateMenu(QPlatformMenu *menu) |
515 | +{ |
516 | + QMenu *qMenu = qobject_cast<AppMenuPlatformMenu *>(menu)->m_menu; |
517 | + QString menuObjectPath = m_objectPath + QStringLiteral("/menu"); |
518 | + m_dbusMenuExporter = new DBusMenuExporter(menuObjectPath, qMenu); |
519 | +} |
520 | + |
521 | +QRect AppMenuPlatformSystemTrayIcon::geometry() const |
522 | +{ |
523 | + return QRect(); |
524 | +} |
525 | + |
526 | +void AppMenuPlatformSystemTrayIcon::showMessage(const QString &title, const QString &msg, |
527 | + const QIcon &icon, MessageIcon iconType, int msecs) |
528 | +{ |
529 | + QString iconString = icon.name(); |
530 | + if (iconString.isEmpty()) { |
531 | + switch (iconType) { |
532 | + case NoIcon: |
533 | + break; |
534 | + case Information: |
535 | + iconString = "dialog-information"; |
536 | + break; |
537 | + case Warning: |
538 | + iconString = "dialog-warning"; |
539 | + break; |
540 | + case Critical: |
541 | + iconString = "dialog-error"; |
542 | + break; |
543 | + } |
544 | + } |
545 | + |
546 | + QDBusInterface iface(NOTIFICATION_SERVICE, NOTIFICATION_PATH, NOTIFICATION_INTERFACE); |
547 | + iface.asyncCall("Notify", |
548 | + id(), |
549 | + quint32(0), // replaces_id |
550 | + iconString, |
551 | + title, |
552 | + msg, |
553 | + QStringList(), // actions |
554 | + QVariantMap(), // hints |
555 | + msecs); // timeout |
556 | +} |
557 | + |
558 | +bool AppMenuPlatformSystemTrayIcon::isSystemTrayAvailable() const |
559 | +{ |
560 | + const QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface(); |
561 | + if (!interface->isServiceRegistered(SNW_SERVICE).value()) { |
562 | + return false; |
563 | + } |
564 | + QDBusInterface snw(SNW_SERVICE, SNW_PATH, SNW_INTERFACE); |
565 | + QVariant value = snw.property("IsStatusNotifierHostRegistered"); |
566 | + if (!value.canConvert<bool>()) { |
567 | + return false; |
568 | + } |
569 | + return value.toBool(); |
570 | +} |
571 | + |
572 | +bool AppMenuPlatformSystemTrayIcon::supportsMessages() const |
573 | +{ |
574 | + const QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface(); |
575 | + return interface->isServiceRegistered(NOTIFICATION_SERVICE).value(); |
576 | +} |
577 | + |
578 | +QPlatformMenu *AppMenuPlatformSystemTrayIcon::createMenu() const |
579 | +{ |
580 | + return new AppMenuPlatformMenu(); |
581 | +} |
582 | + |
583 | +// Properties |
584 | + |
585 | +QString AppMenuPlatformSystemTrayIcon::category() const |
586 | +{ |
587 | + return QStringLiteral("ApplicationStatus"); |
588 | +} |
589 | + |
590 | +QString AppMenuPlatformSystemTrayIcon::id() const |
591 | +{ |
592 | + return QCoreApplication::applicationFilePath().section('/', -1); |
593 | +} |
594 | + |
595 | +QString AppMenuPlatformSystemTrayIcon::title() const |
596 | +{ |
597 | + QString title = QCoreApplication::applicationName(); |
598 | + return title.isEmpty() ? id() : title; |
599 | +} |
600 | + |
601 | +QString AppMenuPlatformSystemTrayIcon::status() const |
602 | +{ |
603 | + return m_status; |
604 | +} |
605 | + |
606 | +QString AppMenuPlatformSystemTrayIcon::iconThemePath() const |
607 | +{ |
608 | + return iconCache.themePath(); |
609 | +} |
610 | + |
611 | +QString AppMenuPlatformSystemTrayIcon::iconName() const |
612 | +{ |
613 | + if (m_icon.isNull()) { |
614 | + return QString(); |
615 | + } |
616 | + QString name = m_icon.name(); |
617 | + if (!name.isEmpty()) { |
618 | + return name; |
619 | + } |
620 | + |
621 | + return iconCache.nameForIcon(m_icon); |
622 | +} |
623 | + |
624 | +DBusToolTip AppMenuPlatformSystemTrayIcon::toolTip() const |
625 | +{ |
626 | + DBusToolTip tip; |
627 | + tip.iconName = iconName(); |
628 | + tip.title = m_tooltip; |
629 | + return tip; |
630 | +} |
631 | + |
632 | +QDBusObjectPath AppMenuPlatformSystemTrayIcon::menu() const |
633 | +{ |
634 | + return m_dbusMenuExporter |
635 | + ? QDBusObjectPath(m_objectPath + QStringLiteral("/menu")) |
636 | + : QDBusObjectPath("/invalid"); |
637 | +} |
638 | + |
639 | +// StatusNotifierItem implementation |
640 | + |
641 | +void AppMenuPlatformSystemTrayIcon::ContextMenu(int x, int y) |
642 | +{ |
643 | + Q_UNUSED(x); |
644 | + Q_UNUSED(y); |
645 | +} |
646 | + |
647 | +void AppMenuPlatformSystemTrayIcon::Activate(int x, int y) |
648 | +{ |
649 | + Q_UNUSED(x); |
650 | + Q_UNUSED(y); |
651 | + emit activated(Trigger); |
652 | +} |
653 | + |
654 | +void AppMenuPlatformSystemTrayIcon::SecondaryActivate(int x, int y) |
655 | +{ |
656 | + Q_UNUSED(x); |
657 | + Q_UNUSED(y); |
658 | + emit activated(MiddleClick); |
659 | +} |
660 | + |
661 | +void AppMenuPlatformSystemTrayIcon::Scroll(int delta, const QString& orientation) |
662 | +{ |
663 | + Q_UNUSED(delta); |
664 | + Q_UNUSED(orientation); |
665 | +} |
666 | |
667 | === added file 'src/appmenuplatformsystemtrayicon.h' |
668 | --- src/appmenuplatformsystemtrayicon.h 1970-01-01 00:00:00 +0000 |
669 | +++ src/appmenuplatformsystemtrayicon.h 2014-12-13 15:39:31 +0000 |
670 | @@ -0,0 +1,101 @@ |
671 | +/* |
672 | + * Copyright 2014 Dmitry Shachnev <mitya57@ubuntu.com> |
673 | + * |
674 | + * This program is free software: you can redistribute it and/or modify |
675 | + * it under the terms of the GNU Lesser General Public License as published by |
676 | + * the Free Software Foundation, version 3 of the License. |
677 | + * |
678 | + * This program is distributed in the hope that it will be useful, |
679 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
680 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
681 | + * GNU Lesser General Public License for more details. |
682 | + * |
683 | + * You should have received a copy of the GNU Lesser General Public License |
684 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
685 | + */ |
686 | + |
687 | +#ifndef APPMENUPLATFORMSYSTEMTRAYICON_H |
688 | +#define APPMENUPLATFORMSYSTEMTRAYICON_H |
689 | + |
690 | +#include <QtCore/qrect.h> |
691 | +#include <QtCore/qstring.h> |
692 | +#include <QtGui/qicon.h> |
693 | +#include <qpa/qplatformsystemtrayicon.h> |
694 | +#include <dbusmenuexporter.h> |
695 | +#include "statusnotifieritem_adaptor.h" |
696 | + |
697 | +class AppMenuPlatformSystemTrayIcon : public QPlatformSystemTrayIcon |
698 | +{ |
699 | + Q_OBJECT |
700 | + Q_PROPERTY(QString Category READ category) |
701 | + Q_PROPERTY(QString Id READ id) |
702 | + Q_PROPERTY(QString Title READ title) |
703 | + Q_PROPERTY(QString Status READ status) |
704 | + Q_PROPERTY(quint32 WindowId READ windowId) |
705 | + Q_PROPERTY(QString IconThemePath READ iconThemePath) |
706 | + Q_PROPERTY(QString IconName READ iconName) |
707 | + Q_PROPERTY(DBusImageList IconPixmap READ iconPixmap) |
708 | + Q_PROPERTY(QString OverlayIconName READ overlayIconName) |
709 | + Q_PROPERTY(DBusImageList OverlayIconPixmap READ overlayIconPixmap) |
710 | + Q_PROPERTY(QString AttentionIconName READ attentionIconName) |
711 | + Q_PROPERTY(DBusImageList AttentionIconPixmap READ attentionIconPixmap) |
712 | + Q_PROPERTY(QString AttentionMovieName READ attentionMovieName) |
713 | + Q_PROPERTY(DBusToolTip ToolTip READ toolTip) |
714 | + Q_PROPERTY(QDBusObjectPath Menu READ menu) |
715 | + |
716 | +public: |
717 | + AppMenuPlatformSystemTrayIcon(); |
718 | + virtual ~AppMenuPlatformSystemTrayIcon(); |
719 | + |
720 | + // QPlatformSystemTrayIcon |
721 | + virtual void init(); |
722 | + virtual void cleanup(); |
723 | + virtual void updateIcon(const QIcon &icon); |
724 | + virtual void updateToolTip(const QString &tooltip); |
725 | + virtual void updateMenu(QPlatformMenu *menu); |
726 | + virtual QRect geometry() const; |
727 | + virtual void showMessage(const QString &title, const QString &msg, |
728 | + const QIcon &icon, MessageIcon iconType, int msecs); |
729 | + virtual bool isSystemTrayAvailable() const; |
730 | + virtual bool supportsMessages() const; |
731 | + virtual QPlatformMenu *createMenu() const; |
732 | + |
733 | + // Properties |
734 | + QString category() const; |
735 | + QString id() const; |
736 | + QString title() const; |
737 | + QString status() const; |
738 | + quint32 windowId() const { return 0; } |
739 | + QString iconThemePath() const; |
740 | + QString iconName() const; |
741 | + DBusImageList iconPixmap() const { return DBusImageList(); } |
742 | + QString overlayIconName() const { return QString(); } |
743 | + DBusImageList overlayIconPixmap() const { return DBusImageList(); } |
744 | + QString attentionIconName() const { return QString(); } |
745 | + DBusImageList attentionIconPixmap() const { return DBusImageList(); } |
746 | + QString attentionMovieName() const { return QString(); } |
747 | + DBusToolTip toolTip() const; |
748 | + QDBusObjectPath menu() const; |
749 | + |
750 | + // StatusNotifierItem |
751 | + void ContextMenu(int x, int y); |
752 | + void Activate(int x, int y); |
753 | + void SecondaryActivate(int x, int y); |
754 | + void Scroll(int delta, const QString& orientation); |
755 | + |
756 | +Q_SIGNALS: |
757 | + void NewStatus(const QString &status); |
758 | + void NewIcon(); |
759 | + void NewToolTip(); |
760 | + |
761 | +private: |
762 | + QString m_objectPath; |
763 | + QString m_status; |
764 | + QIcon m_icon; |
765 | + QString m_tooltip; |
766 | + |
767 | + StatusNotifierItemAdaptor *m_sniAdaptor; |
768 | + DBusMenuExporter *m_dbusMenuExporter; |
769 | +}; |
770 | + |
771 | +#endif /* APPMENUPLATFORMSYSTEMTRAYICON_H */ |
772 | |
773 | === added file 'src/dbusstructures.cpp' |
774 | --- src/dbusstructures.cpp 1970-01-01 00:00:00 +0000 |
775 | +++ src/dbusstructures.cpp 2014-12-13 15:39:31 +0000 |
776 | @@ -0,0 +1,64 @@ |
777 | +/* |
778 | + * Copyright 2011 Canonical Ltd. |
779 | + * Copyright 2014 Dmitry Shachnev <mitya57@ubuntu.com> |
780 | + * |
781 | + * This program is free software: you can redistribute it and/or modify |
782 | + * it under the terms of the GNU Lesser General Public License as published by |
783 | + * the Free Software Foundation, version 3 of the License. |
784 | + * |
785 | + * This program is distributed in the hope that it will be useful, |
786 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
787 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
788 | + * GNU Lesser General Public License for more details. |
789 | + * |
790 | + * You should have received a copy of the GNU Lesser General Public License |
791 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
792 | + * |
793 | + * Based on sni-qt code by Aurelien Gateau <aurelien.gateau@canonical.com>. |
794 | + */ |
795 | + |
796 | +#include <QtDBus/qdbusmetatype.h> |
797 | +#include "dbusstructures.h" |
798 | + |
799 | +QDBusArgument &operator<<(QDBusArgument &argument, const DBusImage &image) |
800 | +{ |
801 | + argument.beginStructure(); |
802 | + argument << image.width << image.height << image.pixels; |
803 | + argument.endStructure(); |
804 | + return argument; |
805 | +} |
806 | + |
807 | +const QDBusArgument &operator>>(const QDBusArgument &argument, DBusImage &image) |
808 | +{ |
809 | + argument.beginStructure(); |
810 | + argument >> image.width >> image.height >> image.pixels; |
811 | + argument.endStructure(); |
812 | + return argument; |
813 | +} |
814 | + |
815 | +QDBusArgument &operator<<(QDBusArgument &argument, const DBusToolTip &tip) |
816 | +{ |
817 | + argument.beginStructure(); |
818 | + argument << tip.iconName << tip.iconPixmap << tip.title << tip.description; |
819 | + argument.endStructure(); |
820 | + return argument; |
821 | +} |
822 | + |
823 | +const QDBusArgument &operator>>(const QDBusArgument &argument, DBusToolTip &tip) |
824 | +{ |
825 | + argument.beginStructure(); |
826 | + argument >> tip.iconName >> tip.iconPixmap >> tip.title >> tip.description; |
827 | + argument.endStructure(); |
828 | + return argument; |
829 | +} |
830 | + |
831 | +void registerMetaTypes() |
832 | +{ |
833 | + static bool registered = false; |
834 | + if (registered) { |
835 | + return; |
836 | + } |
837 | + qDBusRegisterMetaType<DBusImage>(); |
838 | + qDBusRegisterMetaType<DBusImageList>(); |
839 | + qDBusRegisterMetaType<DBusToolTip>(); |
840 | +} |
841 | |
842 | === added file 'src/dbusstructures.h' |
843 | --- src/dbusstructures.h 1970-01-01 00:00:00 +0000 |
844 | +++ src/dbusstructures.h 2014-12-13 15:39:31 +0000 |
845 | @@ -0,0 +1,58 @@ |
846 | +/* |
847 | + * Copyright 2011 Canonical Ltd. |
848 | + * Copyright 2014 Dmitry Shachnev <mitya57@ubuntu.com> |
849 | + * |
850 | + * This program is free software: you can redistribute it and/or modify |
851 | + * it under the terms of the GNU Lesser General Public License as published by |
852 | + * the Free Software Foundation, version 3 of the License. |
853 | + * |
854 | + * This program is distributed in the hope that it will be useful, |
855 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
856 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
857 | + * GNU Lesser General Public License for more details. |
858 | + * |
859 | + * You should have received a copy of the GNU Lesser General Public License |
860 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
861 | + * |
862 | + * Based on sni-qt code by Aurelien Gateau <aurelien.gateau@canonical.com>. |
863 | + */ |
864 | + |
865 | +#ifndef DBUSSTRUCTURES_H |
866 | +#define DBUSSTRUCTURES_H |
867 | + |
868 | +#include <QtCore/qbytearray.h> |
869 | +#include <QtCore/qstring.h> |
870 | +#include <QtCore/qlist.h> |
871 | +#include <QtGui/qicon.h> |
872 | +#include <QtGui/qpixmap.h> |
873 | +#include <QtDBus/qdbusargument.h> |
874 | + |
875 | +struct DBusImage |
876 | +{ |
877 | + int width; |
878 | + int height; |
879 | + QByteArray pixels; |
880 | +}; |
881 | +Q_DECLARE_METATYPE(DBusImage) |
882 | + |
883 | +typedef QList<DBusImage> DBusImageList; |
884 | +Q_DECLARE_METATYPE(DBusImageList) |
885 | + |
886 | +struct DBusToolTip |
887 | +{ |
888 | + QString iconName; |
889 | + DBusImageList iconPixmap; |
890 | + QString title; |
891 | + QString description; |
892 | +}; |
893 | +Q_DECLARE_METATYPE(DBusToolTip) |
894 | + |
895 | +QDBusArgument &operator<<(QDBusArgument&, const DBusImage&); |
896 | +const QDBusArgument &operator>>(const QDBusArgument&, DBusImage&); |
897 | + |
898 | +QDBusArgument &operator<<(QDBusArgument&, const DBusToolTip&); |
899 | +const QDBusArgument &operator>>(const QDBusArgument&, DBusToolTip&); |
900 | + |
901 | +void registerMetaTypes(); |
902 | + |
903 | +#endif /* DBUSSTRUCTURES_H */ |
904 | |
905 | === added file 'src/iconcache.cpp' |
906 | --- src/iconcache.cpp 1970-01-01 00:00:00 +0000 |
907 | +++ src/iconcache.cpp 2014-12-13 15:39:31 +0000 |
908 | @@ -0,0 +1,140 @@ |
909 | +/* |
910 | + * Copyright 2011 Canonical Ltd. |
911 | + * Copyright 2014 Dmitry Shachnev <mitya57@ubuntu.com> |
912 | + * |
913 | + * This program is free software: you can redistribute it and/or modify |
914 | + * it under the terms of the GNU Lesser General Public License as published by |
915 | + * the Free Software Foundation, version 3 of the License. |
916 | + * |
917 | + * This program is distributed in the hope that it will be useful, |
918 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
919 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
920 | + * GNU Lesser General Public License for more details. |
921 | + * |
922 | + * You should have received a copy of the GNU Lesser General Public License |
923 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
924 | + * |
925 | + * Based on sni-qt code by Aurelien Gateau <aurelien.gateau@canonical.com>. |
926 | + */ |
927 | + |
928 | +#include <sys/types.h> |
929 | +#include <utime.h> |
930 | +#include <QtCore/qcoreapplication.h> |
931 | +#include <QtCore/qdatetime.h> |
932 | +#include <QtCore/qdebug.h> |
933 | +#include <QtCore/qdir.h> |
934 | +#include "iconcache.h" |
935 | + |
936 | +const int IconCache::MaxIconCount = 20; |
937 | + |
938 | +IconCache::IconCache(QObject *parent): |
939 | + QObject(parent), |
940 | + m_temporaryDir(Q_NULLPTR), |
941 | + m_initialized(false) |
942 | +{ |
943 | +} |
944 | + |
945 | +IconCache::~IconCache() |
946 | +{ |
947 | + if (m_temporaryDir) { |
948 | + delete m_temporaryDir; |
949 | + } |
950 | +} |
951 | + |
952 | +QString IconCache::themePath() |
953 | +{ |
954 | + if (!m_initialized) { |
955 | + QString path = QDir::tempPath() + QStringLiteral("/iconcache-XXXXXX"); |
956 | + m_temporaryDir = new QTemporaryDir(path); |
957 | + m_initialized = true; |
958 | + } |
959 | + return m_temporaryDir->path(); |
960 | +} |
961 | + |
962 | +QString IconCache::nameForIcon(const QIcon &icon) |
963 | +{ |
964 | + if (icon.isNull()) { |
965 | + return QString(); |
966 | + } |
967 | + |
968 | + qint64 key = icon.cacheKey(); |
969 | + QList<qint64>::iterator it = qFind(m_cacheKeys.begin(), m_cacheKeys.end(), key); |
970 | + if (it == m_cacheKeys.end()) { |
971 | + cacheIcon(key, icon); |
972 | + trimCache(); |
973 | + } else { |
974 | + // Place key at the end of list as it is the most recently accessed |
975 | + m_cacheKeys.erase(it); |
976 | + m_cacheKeys.append(key); |
977 | + } |
978 | + |
979 | + return QString::number(key); |
980 | +} |
981 | + |
982 | +void IconCache::trimCache() |
983 | +{ |
984 | + QDir dir(themePath() + "/hicolor"); |
985 | + dir.setFilter(QDir::Dirs); |
986 | + |
987 | + while (m_cacheKeys.count() > MaxIconCount) { |
988 | + qint64 cacheKey = m_cacheKeys.takeFirst(); |
989 | + |
990 | + Q_FOREACH(const QString &sizeDir, dir.entryList()) { |
991 | + QString iconSubPath = QString("%1/apps/%2.png").arg(sizeDir).arg(cacheKey); |
992 | + if (dir.exists(iconSubPath)) { |
993 | + dir.remove(iconSubPath); |
994 | + } |
995 | + } |
996 | + } |
997 | +} |
998 | + |
999 | +void touch(const QString &_name, const QDateTime &time) |
1000 | +{ |
1001 | + QByteArray name = QFile::encodeName(_name); |
1002 | + struct utimbuf buf; |
1003 | + buf.actime = time.toTime_t(); |
1004 | + buf.modtime = buf.actime; |
1005 | + int ret = utime(name.data(), &buf); |
1006 | + if (ret) { |
1007 | + qCritical("Failed to touch %s: %s", name.data(), strerror(errno)); |
1008 | + } |
1009 | +} |
1010 | + |
1011 | +void IconCache::cacheIcon(qint64 key, const QIcon &icon) |
1012 | +{ |
1013 | + QList<QSize> sizes; //= icon.availableSizes(); |
1014 | + if (sizes.isEmpty()) { |
1015 | + // sizes can be empty if icon is an SVG. In this case generate images for a few sizes |
1016 | + sizes << QSize(16, 16) << QSize(22, 22) << QSize(32, 32) << QSize(48, 48); |
1017 | + } |
1018 | + |
1019 | + QDir dir(themePath()); |
1020 | + Q_FOREACH(const QSize &size, sizes) { |
1021 | + QPixmap pix = icon.pixmap(size); |
1022 | + QString dirName = QString("hicolor/%1x%1/apps").arg(size.width()); |
1023 | + if (!dir.exists(dirName)) { |
1024 | + if (!dir.mkpath(dirName)) { |
1025 | + qWarning("Could not create '%s' dir in '%s'", |
1026 | + qPrintable(themePath()), qPrintable(dirName)); |
1027 | + continue; |
1028 | + } |
1029 | + } |
1030 | + QString pixPath = QString("%1/%2/%3.png") |
1031 | + .arg(themePath()).arg(dirName).arg(key); |
1032 | + if (!pix.save(pixPath, "png")) { |
1033 | + qWarning("Could not save icon as '%s'", qPrintable(pixPath)); |
1034 | + } |
1035 | + } |
1036 | + |
1037 | + m_cacheKeys << key; |
1038 | + |
1039 | + // Touch the theme path: GTK icon loading system checks the mtime of the |
1040 | + // dir to decide whether it should look for new icons in the theme dir. |
1041 | + // |
1042 | + // Note: We do not use QDateTime::currentDateTime() as the new time because |
1043 | + // if the icon is updated in less than one second, GTK won't notice it. |
1044 | + // See https://bugs.launchpad.net/sni-qt/+bug/812884 |
1045 | + QFileInfo info(themePath()); |
1046 | + QDateTime mtime = info.lastModified(); |
1047 | + touch(themePath(), mtime.addSecs(1)); |
1048 | +} |
1049 | |
1050 | === added file 'src/iconcache.h' |
1051 | --- src/iconcache.h 1970-01-01 00:00:00 +0000 |
1052 | +++ src/iconcache.h 2014-12-13 15:39:31 +0000 |
1053 | @@ -0,0 +1,54 @@ |
1054 | +/* |
1055 | + * Copyright 2011 Canonical Ltd. |
1056 | + * Copyright 2014 Dmitry Shachnev <mitya57@ubuntu.com> |
1057 | + * |
1058 | + * This program is free software: you can redistribute it and/or modify |
1059 | + * it under the terms of the GNU Lesser General Public License as published by |
1060 | + * the Free Software Foundation, version 3 of the License. |
1061 | + * |
1062 | + * This program is distributed in the hope that it will be useful, |
1063 | + * but WITHOUT ANY WARRANTY; without even the implied warranty of |
1064 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
1065 | + * GNU Lesser General Public License for more details. |
1066 | + * |
1067 | + * You should have received a copy of the GNU Lesser General Public License |
1068 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1069 | + * |
1070 | + * Based on sni-qt code by Aurelien Gateau <aurelien.gateau@canonical.com>. |
1071 | + */ |
1072 | + |
1073 | +#ifndef ICONCACHE_H |
1074 | +#define ICONCACHE_H |
1075 | + |
1076 | +#include <QtCore/qlist.h> |
1077 | +#include <QtCore/qobject.h> |
1078 | +#include <QtCore/qstring.h> |
1079 | +#include <QtCore/qtemporarydir.h> |
1080 | +#include <QtGui/qicon.h> |
1081 | + |
1082 | +/** |
1083 | + * This class will save pixmaps from icons in a temporary dir on the disk, |
1084 | + * making it possible to pass filenames for icons without names. |
1085 | + */ |
1086 | +class IconCache : public QObject |
1087 | +{ |
1088 | + Q_OBJECT |
1089 | +public: |
1090 | + IconCache(QObject* parent=0); |
1091 | + ~IconCache(); |
1092 | + |
1093 | + static const int MaxIconCount; |
1094 | + |
1095 | + QString themePath(); |
1096 | + QString nameForIcon(const QIcon &icon); |
1097 | + |
1098 | +private: |
1099 | + QTemporaryDir *m_temporaryDir; |
1100 | + mutable QList<qint64> m_cacheKeys; |
1101 | + bool m_initialized; |
1102 | + |
1103 | + void cacheIcon(qint64 key, const QIcon &); |
1104 | + void trimCache(); |
1105 | +}; |
1106 | + |
1107 | +#endif /* ICONCACHE_H */ |
1108 | |
1109 | === added file 'src/org.kde.StatusNotifierItem.xml' |
1110 | --- src/org.kde.StatusNotifierItem.xml 1970-01-01 00:00:00 +0000 |
1111 | +++ src/org.kde.StatusNotifierItem.xml 2014-12-13 15:39:31 +0000 |
1112 | @@ -0,0 +1,96 @@ |
1113 | +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> |
1114 | +<node> |
1115 | + <interface name="org.kde.StatusNotifierItem"> |
1116 | + |
1117 | + <property name="Category" type="s" access="read"/> |
1118 | + <property name="Id" type="s" access="read"/> |
1119 | + <property name="Title" type="s" access="read"/> |
1120 | + <property name="Status" type="s" access="read"/> |
1121 | + <property name="WindowId" type="i" access="read"/> |
1122 | + |
1123 | + <!-- An additional path to add to the theme search path to find the icons specified above. --> |
1124 | + <property name="IconThemePath" type="s" access="read"/> |
1125 | + <property name="Menu" type="o" access="read"/> |
1126 | + <property name="ItemIsMenu" type="b" access="read"/> |
1127 | + |
1128 | + |
1129 | + <!-- main icon --> |
1130 | + <!-- names are preferred over pixmaps --> |
1131 | + <property name="IconName" type="s" access="read"/> |
1132 | + |
1133 | + <!--struct containing width, height and image data--> |
1134 | + <property name="IconPixmap" type="(iiay)" access="read"> |
1135 | + <annotation name="org.qtproject.QtDBus.QtTypeName" value="DBusImageList"/> |
1136 | + </property> |
1137 | + |
1138 | + <property name="OverlayIconName" type="s" access="read"/> |
1139 | + |
1140 | + <property name="OverlayIconPixmap" type="(iiay)" access="read"> |
1141 | + <annotation name="org.qtproject.QtDBus.QtTypeName" value="DBusImageList"/> |
1142 | + </property> |
1143 | + |
1144 | + |
1145 | + <!-- Requesting attention icon --> |
1146 | + <property name="AttentionIconName" type="s" access="read"/> |
1147 | + |
1148 | + <!--same definition as image--> |
1149 | + <property name="AttentionIconPixmap" type="(iiay)" access="read"> |
1150 | + <annotation name="org.qtproject.QtDBus.QtTypeName" value="DBusImageList"/> |
1151 | + </property> |
1152 | + |
1153 | + <property name="AttentionMovieName" type="s" access="read"/> |
1154 | + |
1155 | + |
1156 | + |
1157 | + <!-- tooltip data --> |
1158 | + |
1159 | + <!--(iiay) is an image--> |
1160 | + <property name="ToolTip" type="(s(iiay)ss)" access="read"> |
1161 | + <annotation name="org.qtproject.QtDBus.QtTypeName" value="DBusToolTip"/> |
1162 | + </property> |
1163 | + |
1164 | + |
1165 | + <!-- interaction: the systemtray wants the application to do something --> |
1166 | + <method name="ContextMenu"> |
1167 | + <!-- we're passing the coordinates of the icon, so the app knows where to put the popup window --> |
1168 | + <arg name="x" type="i" direction="in"/> |
1169 | + <arg name="y" type="i" direction="in"/> |
1170 | + </method> |
1171 | + |
1172 | + <method name="Activate"> |
1173 | + <arg name="x" type="i" direction="in"/> |
1174 | + <arg name="y" type="i" direction="in"/> |
1175 | + </method> |
1176 | + |
1177 | + <method name="SecondaryActivate"> |
1178 | + <arg name="x" type="i" direction="in"/> |
1179 | + <arg name="y" type="i" direction="in"/> |
1180 | + </method> |
1181 | + |
1182 | + <method name="Scroll"> |
1183 | + <arg name="delta" type="i" direction="in"/> |
1184 | + <arg name="orientation" type="s" direction="in"/> |
1185 | + </method> |
1186 | + |
1187 | + <!-- Signals: the client wants to change something in the status--> |
1188 | + <signal name="NewTitle"> |
1189 | + </signal> |
1190 | + |
1191 | + <signal name="NewIcon"> |
1192 | + </signal> |
1193 | + |
1194 | + <signal name="NewAttentionIcon"> |
1195 | + </signal> |
1196 | + |
1197 | + <signal name="NewOverlayIcon"> |
1198 | + </signal> |
1199 | + |
1200 | + <signal name="NewToolTip"> |
1201 | + </signal> |
1202 | + |
1203 | + <signal name="NewStatus"> |
1204 | + <arg name="status" type="s"/> |
1205 | + </signal> |
1206 | + |
1207 | + </interface> |
1208 | +</node> |
1209 | |
1210 | === modified file 'src/src.pro' |
1211 | --- src/src.pro 2014-04-14 15:04:02 +0000 |
1212 | +++ src/src.pro 2014-12-13 15:39:31 +0000 |
1213 | @@ -6,14 +6,25 @@ |
1214 | |
1215 | QT += core-private gui-private platformsupport-private dbus widgets |
1216 | DBUS_INTERFACES += com.canonical.AppMenu.Registrar.xml |
1217 | +DBUS_ADAPTORS += org.kde.StatusNotifierItem.xml |
1218 | +QDBUSXML2CPP_ADAPTOR_HEADER_FLAGS += -i dbusstructures.h |
1219 | |
1220 | CONFIG += X11 link_pkgconfig debug |
1221 | PKGCONFIG += dbusmenu-qt5 gtk+-2.0 |
1222 | DESTDIR = ./ |
1223 | |
1224 | HEADERS += \ |
1225 | - appmenuplatformmenubar.h |
1226 | + appmenuplatformmenuitem.h \ |
1227 | + appmenuplatformmenu.h \ |
1228 | + appmenuplatformmenubar.h \ |
1229 | + appmenuplatformsystemtrayicon.h \ |
1230 | + dbusstructures.h \ |
1231 | + iconcache.h |
1232 | |
1233 | SOURCES += \ |
1234 | - appmenuplatformmenubar.cpp |
1235 | - |
1236 | + appmenuplatformmenuitem.cpp \ |
1237 | + appmenuplatformmenu.cpp \ |
1238 | + appmenuplatformmenubar.cpp \ |
1239 | + appmenuplatformsystemtrayicon.cpp \ |
1240 | + dbusstructures.cpp \ |
1241 | + iconcache.cpp |
This is ready for review now.
Note that this is disabled for KDE, as it is not going to work properly there. I think we should drop the KDE support altogether, as KDE now has its own platform theme and these two will conflict.