Merge lp:~nick-dedekind/qtubuntu/menuTheme into lp:qtubuntu
- menuTheme
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Gerry Boland |
Approved revision: | 319 |
Merged at revision: | 362 |
Proposed branch: | lp:~nick-dedekind/qtubuntu/menuTheme |
Merge into: | lp:qtubuntu |
Diff against target: |
2206 lines (+1850/-36) 28 files modified
README (+3/-2) debian/control (+12/-0) debian/gles-patches/convert-to-gles.patch (+23/-10) debian/qtubuntu-appmenutheme.install (+1/-0) debian/rules (+1/-0) src/src.pro (+1/-1) src/ubuntuappmenu/com.ubuntu.MenuRegistrar.xml (+83/-0) src/ubuntuappmenu/gmenumodelexporter.cpp (+350/-0) src/ubuntuappmenu/gmenumodelexporter.h (+83/-0) src/ubuntuappmenu/gmenumodelplatformmenu.cpp (+526/-0) src/ubuntuappmenu/gmenumodelplatformmenu.h (+181/-0) src/ubuntuappmenu/logging.h (+27/-0) src/ubuntuappmenu/menuregistrar.cpp (+137/-0) src/ubuntuappmenu/menuregistrar.h (+59/-0) src/ubuntuappmenu/registry.cpp (+97/-0) src/ubuntuappmenu/registry.h (+56/-0) src/ubuntuappmenu/theme.cpp (+46/-13) src/ubuntuappmenu/theme.h (+9/-4) src/ubuntuappmenu/themeplugin.cpp (+36/-0) src/ubuntuappmenu/themeplugin.h (+34/-0) src/ubuntuappmenu/ubuntuappmenu.json (+3/-0) src/ubuntuappmenu/ubuntuappmenu.pro (+41/-0) src/ubuntumirclient/integration.cpp (+24/-3) src/ubuntumirclient/nativeinterface.cpp (+3/-0) src/ubuntumirclient/plugin.cpp (+3/-0) src/ubuntumirclient/ubuntumirclient.pro (+4/-3) src/ubuntumirclient/window.cpp (+5/-0) src/ubuntumirclient/window.h (+2/-0) |
To merge this branch: | bzr merge lp:~nick-dedekind/qtubuntu/menuTheme |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gerry Boland (community) | Approve | ||
Unity8 CI Bot | continuous-integration | Approve | |
Timo Jyrinki | Approve | ||
Review via email: mp+296997@code.launchpad.net |
Commit message
Added a QPlatformTheme for exporting gmenumodel for qt menus.
Description of the change
Added a QPlatformTheme for exporting a gmenumodel for qt menus.
Theme packaged in qtubuntu-
Theme is loadable through QT_QPA_
Changed categories logging to ubuntu.mirclient, ubuntu.appmenu
Requires registry implementation to function:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:285
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:287
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:288
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:292
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:293
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:295
https:/
Executed test runs:
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:296
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:297
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
FAILURE: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:298
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Gerry Boland (gerboland) wrote : | # |
Hey,
this was bigger than I was expecting!
Questions:
1. how do I test this?
2. is the theme plugin totally independent from the QPA plugin? I see you moved the QPA's theme code into the Integration code. Why? Just to indicate the majority of the theming is elsewhere?
~/dev/projects/
src.pro ubuntuappmenu/ ubuntumirclient/
~/dev/projects/
ubuntuappmenu/ ubuntumirclient/
/me hating how we prefix everything with "ubuntu".
I'm just looking at the QPA for now
- you add persistentSurfaceId - can it be a QByteArray instead of QString?
+ // queue the windowPropertyC
+ QMetaObject:
+ Q_ARG(QPlatform
+ Q_ARG(QString, "persistentSurf
How does a persistent Surface ID ever change?
+ return QStringList(
QStringLiteral please.
-Q_LOGGING_
+Q_LOGGING_
Why? This breaks the standard elsewhere in the code. Check the README too.
Gerry Boland (gerboland) wrote : | # |
=== modified file 'debian/control'
What dependency will pull in this new qtubuntu-
+Build-Depends: ${misc:Depends},
+ ${shlibs:Depends},
bad indent
Nick Dedekind (nick-dedekind) wrote : | # |
> === modified file 'debian/control'
> What dependency will pull in this new qtubuntu-
I would probably say it's whatever pulls in unity8/qtubuntu. unity8-
>
> +Build-Depends: ${misc:Depends},
> + ${shlibs:Depends},
> bad indent
Fixed.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:300
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Nick Dedekind (nick-dedekind) wrote : | # |
>
> I'm just looking at the QPA for now
> - you add persistentSurfaceId - can it be a QByteArray instead of QString?
Done.
>
> + // queue the windowPropertyC
> platformWindow will not yet be set for the window.
> + QMetaObject:
> Qt::QueuedConne
> + Q_ARG(QPlatform
> + Q_ARG(QString, "persistentSurf
> How does a persistent Surface ID ever change?
It doesn't change but the platform window is created after the QWindow, and therefore could be created after an attached menubar; so when the exporter initially asks for the surface id, it wont be set yet, therefore we need to be notified when it is.
>
> + return QStringList(
> QStringLiteral please.
Done
>
> -Q_LOGGING_
> QtWarningMsg)
> +Q_LOGGING_
> QtWarningMsg)
> Why? This breaks the standard elsewhere in the code. Check the README too.
I've changed it everywhere in the code now. Since it's kind of like a url i think it should be this way for better "domain" control.
We also have "ubuntu.appmenu" so to get all the logging from qtubuntu, we can do "QT_LOGGING_
* I think it should really be "qtubuntu.
Nick Dedekind (nick-dedekind) wrote : | # |
If you'd rather just leave the log categories as ubuntumirclient
Nick Dedekind (nick-dedekind) wrote : | # |
> Hey,
> this was bigger than I was expecting!
>
> Questions:
> 1. how do I test this?
I'll sort out something to test with.
> 2. is the theme plugin totally independent from the QPA plugin? I see you
> moved the QPA's theme code into the Integration code. Why? Just to indicate
> the majority of the theming is elsewhere?
They're both independent of each other, but ubuntuappmenu relies on a persistent surface id to be set to work correctly, so there is a "soft" dependence their.
The theme in the integration is just a fallback theme in case ubuntuappmenu isn't installed. I didn't think it really needed it's own files since it isn't instantiable outside the integration (No QT_QPA_
The reason for separating the theme into a different package was so that it is very easy to revert or replace. Just uninstall the package.
>
>
> ~/dev/projects/
> src.pro ubuntuappmenu/ ubuntumirclient/
> ~/dev/projects/
> ubuntuappmenu/ ubuntumirclient/
> /me hating how we prefix everything with "ubuntu".
>
yes...
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:302
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Nick Dedekind (nick-dedekind) wrote : | # |
Righty'O
I've added a testing file to https:/
You'll need to build this because it has the server side of the menu registry, but otherwise the test file sits in isolation to any thing else.
The test file is in "qml/Applicatio
So:
1) Get and build deps for unity8 branch.
lp:~unity-team/unity-api/persistent_surface_id
2) Get and build lp:~nick-dedekind/unity8/menus.
3) Start test with: QML2_IMPORT_
4) new term
5) mir_demo_server
6) new term
7) create a qml example with menus. (MenuAPI.qml example graciously provided by dednick http://
8) Build and install this qtubuntu branch
9) QT_LOGGING_
Tadaa. hopefully. you should see a bunch of log output from ubuntu.appmenu, and from ubuntu.
The Test.qml should also contain a pretty printed output of the exported menus it's picked up from dbus.
Gerry Boland (gerboland) wrote : | # |
=== modified file 'README'
+ * ubuntu.mirclient - For all other messages frm the ubuntumirclient QPA.
+ * ubuntu.appmenu - For all messages frm the ubuntuappmenu QPA theme.
typo: "from"
=== modified file 'debian/control'
+ appmenutheme provides you with an integrated application menu in your global
+ menu bar. It functions as a QPA platformtheme plugin.
I just want to edit this to remove the subject from the sentence:
Appmenutheme enables the export of application menus to a global menu bar. It is implemented in a QPA platform theme plugin.
=== added file 'src/ubuntuappm
+ main interface is docuemented here: @ref com::ubuntu:
typo: "documented"
+ ensure this method is called with the same connection that implmenets the object.
and one more "implements"
+ Associates a gmenumodel with a application
typo: "an application"
+ <arg name="menuObjec
+ <dox:d>The object on the dbus interface implementing the gmenumodel interface</dox:d>
+ </arg>
+ <arg name="actionObj
+ <dox:d>The object on the dbus interface implementing the gmenumodel interface</dox:d>
Documentation text could be slightly better (in both RegisterAppMenu and RegisterSurface
+ A method to allow removing a application menu from the database.
same typo: "an application"
+ ensure this method is called with the same connection that implmenets the object.
"implements" again
Gerry Boland (gerboland) wrote : | # |
+++ src/ubuntuappme
+class GMenuModelExporter : public QObject
I was curious if this really needed to be a QObject, but you do use that fact to ensure lifetimes of the objects in signal/lambda connections are correct. So it's ok.
+ QList<QMetaObje
I understand why you do this, because QObject::disconnect must have a sender (i.e. first argument) set.
But GMenuModelPlatf
+++ src/ubuntuappme
+inline QString getActionString
opening brace on newline - just for consistency.
+ for(auto iter = parts.begin(); iter != parts.end(); ++iter) {
space after the "for" - could you not use Q_FOREACH(const auto &part, parts) and save messing with iterators?
I'd also appreciate an explanatory comment of what "getActionString" is doing.
+ auto item = (GMenuModelPlat
Use C++ static_cast<> please
+#define MENU_OBJECT_PATH QString(
QStringLiteral would work. But odd to define more than a character constant here,
#define MENU_OBJECT_PATH "/com/ubuntu/
would be more typical
+ GMenuModelBarEx
+ auto iter = bar->menus(
+ for (; iter != bar->menus().end(); ++iter) {
Q_FOREACH nicer, no? You use these style loops everywhere, any reason?
Also, a quick comment explaining the purpose of these classes and each method would help other readers. I'm especially obsessed with documenting methods that return GThing* pointers, to clearly indicate if or how the returned GThings* should be cleaned up by the consumer.
+ GError *error = NULL;
I see lots of NULL here, can we not use nullptr, or is G stuff incompatible with it? /me obsessed with C++11 goodies
+ bus = g_bus_get_sync (G_BUS_
leak, is never unref-ed.
+ m_exportedModel = g_dbus_
g_dbus_
+ error = NULL;
Leak, you should be doing g_error_free (err)
+ GMenuModelPlatf
I see this a bunch of places, it can be simpler:
auto gplatformMenu = static_
+void GMenuModelExpor
There's a lot going on here, quick comment explaining the intention would be good. I'm just looking at the code, it appears fine. Q_FOREACH not suitable, but you can use static_cast instead of qobject_cast here.
+ bool checkable(
I prefer = instead of () assignment for simple types - same line could be misinterpreted as a function declaration
same for "checked" inside the conditional.
+ std::function<
perf...
Gerry Boland (gerboland) wrote : | # |
QList<QMetaObje
Please use QVector instead of QList here. Ref:
http://
as I suspect (not 100%) that the type is bigger than a pointer
Gerry Boland (gerboland) wrote : | # |
=== added file 'src/ubuntuappm
+ GMenuModelExporter* m_exporter;
+ MenuRegistrar* m_registrar;
If you used QScopedPointer, you would not need to worry about deleting them in a destructor. And then can have a trivial destructor
+class Q_DECL_EXPORT GMenuModelPlatf
+ GMenuModelExporter* m_exporter;
+ MenuRegistrar* m_registrar;
same as above, but you'll need to set them with reset(). Note you're leaking these currently.
=== added file 'src/ubuntuappm
+int logRecusion = 0;
polluting the global namespace, please stuck in an anonymous NS.
+ connect(menu, SIGNAL(
new style connect statement gives us compile-time checking of this, please convert. I see this 2 times
for (auto iter = m_menus.begin(); iter != m_menus.end(); ++iter) {
I think there's the odd loop you could convert to Q_FOREACH. menuForTag & menuItemForTag anyway
+QDebug GMenuModelPlatf
+ auto myMenu = qobject_
static_cast works
+void GMenuModelPlatf
underscore, yuk! :) Just "ready" please
=== added file 'src/ubuntuappm
+bool isMirClient() {
+ return qgetenv(
+}
move to anonymous NS please, and qGuiApp-
+ : m_registeredPro
Putting my security hat on for a second, it is not impossible for an application to have a pid ~0, and also pid 0 (I believe pids can wrap around). Highly unlikely, but an attack vector. Consequences here aren't dire, so I'm ok with it.
std::optional is nice to avoid such munging of set/unset with value.
+ bus = g_bus_get_sync (G_BUS_
leaked again here. NULLs!
+ if (!bus) {
+ qCWarning(
error leaked here
+void MenuRegistrar:
+{
+ if (!UbuntuMenuReg
+ m_registeredPro
+ return;
+ }
+ UbuntuMenuRegis
+ m_registeredPro
+}
m_registeredPro
Gerry Boland (gerboland) wrote : | # |
=== added file 'src/ubuntuappm
+ #include <QObject>
space, and please have a newline between it and the class declarations
+ QDBusServiceWat
+ ComUbuntuMenuRe
QScopedPointer to ease lifetime (even though they'll probably leak anyway since it is a singleton) and simplify destructor
=== added file 'src/ubuntuappm
+ Q_UNUSED(oldOwner);
+ if (serviceName != REGISTRAR_SERVICE) return;
+
+ if (oldOwner != newOwner) {
this is why I don't like Q_UNUSED. oldOwner is actually used.
I prefer commenting out the variable name in the method if it really is unused.
+++ src/ubuntuappme
+bool useLocalMenu() {
anonymous NS please
- return QVariant(
+ return QVariant(
How is this related to this MP? And who decided to change the icon theme?
+++ src/ubuntuappme
learning our lesson about Q_UNUSED, please comment out the variable names instead
Gerry Boland (gerboland) wrote : | # |
+ // queue the windowPropertyC
+ QMetaObject:
+ Q_ARG(QPlatform
+ Q_ARG(QString, "persistentSurf
Why pass the PlatformWindow, why not the QWindow? I see you only using the QWindow in the appmenu stuff:
+ connect(
+ if (property != QStringLiteral(
+ return;
+ }
+ if (window->window() == m_window) {
+ registerMenuFor
+ }
Gerry Boland (gerboland) wrote : | # |
I think for name collision safety all of ubuntuappmenu should be either within its own namespace, or all the class names prefixed with "Ubuntu"
This is something the mirclient QPA needs to do too
Gerry Boland (gerboland) wrote : | # |
> If you'd rather just leave the log categories as
> ubuntumirclient
Can you just put the ubuntumirclient ones back? This MP is big enough as it is.
I do agree with your thinking that the category names should be better namespaced.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:303
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:306
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Nick Dedekind (nick-dedekind) wrote : | # |
> + // queue the windowPropertyC
> platformWindow will not yet be set for the window.
> + QMetaObject:
> Qt::QueuedConne
> + Q_ARG(QPlatform
> + Q_ARG(QString, "persistentSurf
>
> Why pass the PlatformWindow, why not the QWindow? I see you only using the
> QWindow in the appmenu stuff:
>
> + connect(
> &QPlatformNativ
> [this](
> + if (property != QStringLiteral(
> + return;
> + }
> + if (window->window() == m_window) {
> + registerMenuFor
> + }
Because the QPlatformNative
Nick Dedekind (nick-dedekind) wrote : | # |
> +++ src/ubuntuappme
> +class GMenuModelExporter : public QObject
> I was curious if this really needed to be a QObject, but you do use that fact
> to ensure lifetimes of the objects in signal/lambda connections are correct.
> So it's ok.
>
> + QList<QMetaObje
> I understand why you do this, because QObject::disconnect must have a sender
> (i.e. first argument) set.
>
> But GMenuModelPlatf
> their connections. Is that not enough? Do they clean up too late?
When the structure of the menu changes (for additions/removals from menu), we re-process all the menus so need to re-connect. Just because we remove a menu doesn't mean it's necessarily been deleted.
>
>
> +++ src/ubuntuappme
> +inline QString getActionString
> opening brace on newline - just for consistency.
>
> + for(auto iter = parts.begin(); iter != parts.end(); ++iter) {
> space after the "for" - could you not use Q_FOREACH(const auto &part, parts)
> and save messing with iterators?
>
> I'd also appreciate an explanatory comment of what "getActionString" is doing.
>
>
> + auto item = (GMenuModelPlat
> Use C++ static_cast<> please
>
> +#define MENU_OBJECT_PATH QString(
> QStringLiteral would work. But odd to define more than a character constant
> here,
> #define MENU_OBJECT_PATH "/com/ubuntu/
> would be more typical
>
>
> + GMenuModelBarEx
> bar)
> + auto iter = bar->menus(
> + for (; iter != bar->menus().end(); ++iter) {
> Q_FOREACH nicer, no? You use these style loops everywhere, any reason?
>
> Also, a quick comment explaining the purpose of these classes and each method
> would help other readers. I'm especially obsessed with documenting methods
> that return GThing* pointers, to clearly indicate if or how the returned
> GThings* should be cleaned up by the consumer.
>
>
> + GError *error = NULL;
> I see lots of NULL here, can we not use nullptr, or is G stuff incompatible
> with it? /me obsessed with C++11 goodies
>
> + bus = g_bus_get_sync (G_BUS_
> leak, is never unref-ed.
>
> + m_exportedModel = g_dbus_
> menuPath.
> g_dbus_
> m_exportedModel is an int. Implicit lossy conversion here, definitely safe?
>
>
> + error = NULL;
> Leak, you should be doing g_error_free (err)
>
>
> + GMenuModelPlatf
> qobject_
> I see this a bunch of places, it can be simpler:
> auto gplatformMenu = static_
>
>
> +void GMenuModelExpor
> gplatformMenu, GMenu* menu)
> There's a lot going on here, quick comment explaining the intention would be
> good. I'm just looking at the code, it appears fine. Q_FOREACH not suitable,
> but you can use static_cast instead of qobje...
Nick Dedekind (nick-dedekind) wrote : | # |
> === added file 'src/ubuntuappm
>
> + GMenuModelExporter* m_exporter;
> + MenuRegistrar* m_registrar;
> If you used QScopedPointer, you would not need to worry about deleting them in
> a destructor. And then can have a trivial destructor
>
> +class Q_DECL_EXPORT GMenuModelPlatf
> + GMenuModelExporter* m_exporter;
> + MenuRegistrar* m_registrar;
> same as above, but you'll need to set them with reset(). Note you're leaking
> these currently.
>
>
>
> === added file 'src/ubuntuappm
> +int logRecusion = 0;
> polluting the global namespace, please stuck in an anonymous NS.
>
> + connect(menu, SIGNAL(
> new style connect statement gives us compile-time checking of this, please
> convert. I see this 2 times
>
>
> for (auto iter = m_menus.begin(); iter != m_menus.end(); ++iter) {
> I think there's the odd loop you could convert to Q_FOREACH. menuForTag &
> menuItemForTag anyway
>
> +QDebug GMenuModelPlatf
> + auto myMenu = qobject_
> static_cast works
>
> +void GMenuModelPlatf
> underscore, yuk! :) Just "ready" please
>
>
> === added file 'src/ubuntuappm
> +bool isMirClient() {
> + return qgetenv(
> +}
> move to anonymous NS please, and qGuiApp-
> thing to check.
>
>
> + : m_registeredPro
> Putting my security hat on for a second, it is not impossible for an
> application to have a pid ~0, and also pid 0 (I believe pids can wrap around).
> Highly unlikely, but an attack vector. Consequences here aren't dire, so I'm
> ok with it.
>
> std::optional is nice to avoid such munging of set/unset with value.
>
C++17 only.
>
> + bus = g_bus_get_sync (G_BUS_
> leaked again here. NULLs!
>
> + if (!bus) {
> + qCWarning(
> error ? error->message : "unknown error");
> error leaked here
>
> +void MenuRegistrar:
> +{
> + if (!UbuntuMenuReg
> + m_registeredPro
> + return;
> + }
> + UbuntuMenuRegis
> cessId, m_path);
> + m_registeredPro
> +}
>
> m_registeredPro
> duplicate line.
Done.
Nick Dedekind (nick-dedekind) wrote : | # |
Think I got everything.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:309
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:310
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Nick Dedekind (nick-dedekind) wrote : | # |
I'll sort out the namespacing in another MP I; along with changes to the logging categories. For now i've just left them in the current format.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:311
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:312
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Gerry Boland (gerboland) wrote : | # |
> > std::optional is nice to avoid such munging of set/unset with value.
> >
>
> C++17 only.
Damnit yes.
> I'll sort out the namespacing in another MP I; along with changes to
> the logging categories. For now i've just left them in the current format.
Ok.
Gerry Boland (gerboland) wrote : | # |
Only 1 thing missed:
=== added file 'src/ubuntuappm
+bool isMirClient() {
+ return qgetenv(
+}
qGuiApp-
You've addressed everything else
Nick Dedekind (nick-dedekind) wrote : | # |
> Only 1 thing missed:
>
> === added file 'src/ubuntuappm
> +bool isMirClient() {
> + return qgetenv(
> +}
> qGuiApp-
>
> You've addressed everything else
Fixed.
Nick Dedekind (nick-dedekind) wrote : | # |
You will also need lp:~nick-dedekind/qmenumodel/desktop_menus to test.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:313
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:314
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:315
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:316
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Timo Jyrinki (timo-jyrinki) wrote : | # |
The gles version of qtubuntu-
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:317
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Gerry Boland (gerboland) wrote : | # |
Overall this looks good, can approve
Timo Jyrinki (timo-jyrinki) wrote : | # |
Looks fine to me too.
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
FAILED: Continuous integration, rev:319
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild:
https:/
Unity8 CI Bot (unity8-ci-bot) wrote : | # |
PASSED: Continuous integration, rev:319
https:/
Executed test runs:
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
SUCCESS: https:/
deb: https:/
Click here to trigger a rebuild:
https:/
Gerry Boland (gerboland) : | # |
Preview Diff
1 | === modified file 'README' |
2 | --- README 2016-06-22 17:16:33 +0000 |
3 | +++ README 2016-12-09 17:04:44 +0000 |
4 | @@ -48,7 +48,9 @@ |
5 | * ubuntumirclient.input - Messages related to input and other Mir events. |
6 | * ubuntumirclient.graphics - Messages related to graphics, GL and EGL. |
7 | * ubuntumirclient.swapBuffers - Messages related to surface buffer swapping. |
8 | - * ubuntumirclient - For all other messages. |
9 | + * ubuntumirclient - For all other messages form the ubuntumirclient QPA. |
10 | + * ubuntuappmenu.registrar - Messages related to application menu registration. |
11 | + * ubuntuappmenu - For all other messages form the ubuntuappmenu QPA theme. |
12 | |
13 | The QT_QPA_EGLFS_DEBUG environment variable prints a little more information |
14 | from Qt's internals. |
15 | @@ -99,4 +101,3 @@ |
16 | |
17 | [1] http://doc-snapshot.qt-project.org/5.0/qabstractnativeeventfilter.html |
18 | [2] http://doc-snapshot.qt-project.org/5.0/qcoreapplication.html#installNativeEventFilter |
19 | - |
20 | |
21 | === modified file 'debian/control' |
22 | --- debian/control 2016-09-22 19:45:30 +0000 |
23 | +++ debian/control 2016-12-09 17:04:44 +0000 |
24 | @@ -66,3 +66,15 @@ |
25 | plugins. |
26 | . |
27 | This variant of the package is for GNU-based desktops. |
28 | + |
29 | +Package: qtubuntu-appmenutheme |
30 | +Architecture: any |
31 | +Multi-Arch: same |
32 | +Depends: ${misc:Depends}, |
33 | + ${shlibs:Depends}, |
34 | +Description: Qt platform theme for exported application menus |
35 | + Appmenutheme enables the export of application menus to a global menu bar. |
36 | + It is implemented in a QPA platform theme plugin. |
37 | + . |
38 | + This package will work for applications designed for Qt5, as well as QML |
39 | + applications |
40 | |
41 | === modified file 'debian/gles-patches/convert-to-gles.patch' |
42 | --- debian/gles-patches/convert-to-gles.patch 2016-06-03 10:36:39 +0000 |
43 | +++ debian/gles-patches/convert-to-gles.patch 2016-12-09 17:04:44 +0000 |
44 | @@ -1,14 +1,14 @@ |
45 | -Index: cross-build-support/debian/control |
46 | +Index: qtubuntu/debian/control |
47 | =================================================================== |
48 | ---- cross-build-support.orig/debian/control |
49 | -+++ cross-build-support/debian/control |
50 | +--- qtubuntu.orig/debian/control |
51 | ++++ qtubuntu/debian/control |
52 | @@ -1,4 +1,4 @@ |
53 | -Source: qtubuntu |
54 | +Source: qtubuntu-gles |
55 | Priority: optional |
56 | Maintainer: Ubuntu Developers <ubuntu-devel-discuss@lists.ubuntu.com> |
57 | Build-Depends: debhelper (>= 9), |
58 | -@@ -11,11 +11,20 @@ Build-Depends: debhelper (>= 9), |
59 | +@@ -12,11 +12,20 @@ Build-Depends: debhelper (>= 9), |
60 | libinput-dev, |
61 | libmirclient-dev (>= 0.13.0), |
62 | libmtdev-dev, |
63 | @@ -29,7 +29,7 @@ |
64 | quilt, |
65 | # if you don't have have commit access to this branch but would like to upload |
66 | # directly to Ubuntu, don't worry: your changes will be merged back into the |
67 | -@@ -25,7 +34,7 @@ Standards-Version: 3.9.6 |
68 | +@@ -26,7 +35,7 @@ Standards-Version: 3.9.6 |
69 | Section: libs |
70 | |
71 | Package: qtubuntu-android |
72 | @@ -38,7 +38,7 @@ |
73 | Multi-Arch: same |
74 | Conflicts: qtubuntu-desktop, |
75 | Replaces: qtubuntu (<< 0.52), |
76 | -@@ -36,31 +45,12 @@ Provides: qtubuntu, |
77 | +@@ -37,43 +46,12 @@ Provides: qtubuntu, |
78 | Depends: ubuntu-application-api3-touch, |
79 | ${misc:Depends}, |
80 | ${shlibs:Depends}, |
81 | @@ -71,13 +71,25 @@ |
82 | - plugins. |
83 | - . |
84 | - This variant of the package is for GNU-based desktops. |
85 | +- |
86 | +-Package: qtubuntu-appmenutheme |
87 | +-Architecture: any |
88 | +-Multi-Arch: same |
89 | +-Depends: ${misc:Depends}, |
90 | +- ${shlibs:Depends}, |
91 | +-Description: Qt platform theme for exported application menus |
92 | +- Appmenutheme enables the export of application menus to a global menu bar. |
93 | +- It is implemented in a QPA platform theme plugin. |
94 | +- . |
95 | +- This package will work for applications designed for Qt5, as well as QML |
96 | +- applications |
97 | + This variant of the package is for Android-based phones and tablets (built |
98 | + against the OpenGLES variant of qtbase). |
99 | -Index: cross-build-support/debian/rules |
100 | +Index: qtubuntu/debian/rules |
101 | =================================================================== |
102 | ---- cross-build-support.orig/debian/rules |
103 | -+++ cross-build-support/debian/rules |
104 | -@@ -5,60 +5,35 @@ export DPKG_GENSYMBOLS_CHECK_LEVEL=4 |
105 | +--- qtubuntu.orig/debian/rules |
106 | ++++ qtubuntu/debian/rules |
107 | +@@ -5,61 +5,35 @@ export DPKG_GENSYMBOLS_CHECK_LEVEL=4 |
108 | export QT_SELECT=5 |
109 | |
110 | ANDROID_DIR = build-android |
111 | @@ -139,3 +151,4 @@ |
112 | dh_install --sourcedir=$(TMP1_DIR) -pqtubuntu-android |
113 | -endif |
114 | - dh_install --sourcedir=$(TMP2_DIR) -pqtubuntu-desktop |
115 | +- dh_install --sourcedir=$(TMP2_DIR) -pqtubuntu-appmenutheme |
116 | |
117 | === added file 'debian/qtubuntu-appmenutheme.install' |
118 | --- debian/qtubuntu-appmenutheme.install 1970-01-01 00:00:00 +0000 |
119 | +++ debian/qtubuntu-appmenutheme.install 2016-12-09 17:04:44 +0000 |
120 | @@ -0,0 +1,1 @@ |
121 | +usr/lib/*/qt5/plugins/platformthemes/* |
122 | |
123 | === modified file 'debian/rules' |
124 | --- debian/rules 2016-06-03 09:38:55 +0000 |
125 | +++ debian/rules 2016-12-09 17:04:44 +0000 |
126 | @@ -62,3 +62,4 @@ |
127 | dh_install --sourcedir=$(TMP1_DIR) -pqtubuntu-android |
128 | endif |
129 | dh_install --sourcedir=$(TMP2_DIR) -pqtubuntu-desktop |
130 | + dh_install --sourcedir=$(TMP2_DIR) -pqtubuntu-appmenutheme |
131 | |
132 | === modified file 'src/src.pro' |
133 | --- src/src.pro 2014-06-18 23:10:00 +0000 |
134 | +++ src/src.pro 2016-12-09 17:04:44 +0000 |
135 | @@ -1,3 +1,3 @@ |
136 | TEMPLATE = subdirs |
137 | |
138 | -SUBDIRS += ubuntumirclient |
139 | +SUBDIRS += ubuntumirclient ubuntuappmenu |
140 | |
141 | === added directory 'src/ubuntuappmenu' |
142 | === added file 'src/ubuntuappmenu/com.ubuntu.MenuRegistrar.xml' |
143 | --- src/ubuntuappmenu/com.ubuntu.MenuRegistrar.xml 1970-01-01 00:00:00 +0000 |
144 | +++ src/ubuntuappmenu/com.ubuntu.MenuRegistrar.xml 2016-12-09 17:04:44 +0000 |
145 | @@ -0,0 +1,83 @@ |
146 | +<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd"> |
147 | +<node xmlns:dox="http://www.ayatana.org/dbus/dox.dtd"> |
148 | + <dox:d><![CDATA[ |
149 | + @mainpage |
150 | + |
151 | + An interface to register menus that are associated with a window in an application. The |
152 | + main interface is documented here: @ref com::ubuntu::MenuRegistrar. |
153 | + |
154 | + The actual menus are transported using the gmenumodel protocol |
155 | + ]]></dox:d> |
156 | + <interface name="com.ubuntu.MenuRegistrar" xmlns:dox="http://www.ayatana.org/dbus/dox.dtd"> |
157 | + <dox:d> |
158 | + An interface to register a menu from an application to be displayed in another |
159 | + window. This manages that association between processes and/or Mir surface IDs and the dbus |
160 | + address and object that provides the menus using the org.gtk.Menus interface. |
161 | + </dox:d> |
162 | + <method name="RegisterAppMenu"> |
163 | + <dox:d><![CDATA[ |
164 | + Associates a gmenumodel with an application |
165 | + |
166 | + /note this method assumes that the connection from the caller is the DBus connection |
167 | + to use for the object. Applications that use multiple DBus connections will need to |
168 | + ensure this method is called with the same connection that implements the object. |
169 | + ]]></dox:d> |
170 | + <arg name="pid" type="u" direction="in"> |
171 | + <dox:d>The process ID of the application for which the menu is associated</dox:d> |
172 | + </arg> |
173 | + <arg name="menuObjectPath" type="o" direction="in"> |
174 | + <dox:d>The dbus path where the gmenumodel interface for the application menu has been exported</dox:d> |
175 | + </arg> |
176 | + <arg name="actionObjectPath" type="o" direction="in"> |
177 | + <dox:d>The dbus path where the gactionmenu interface for the application menu actions has been exported</dox:d> |
178 | + </arg> |
179 | + <arg name="service" type="s" direction="in"> |
180 | + <dox:d>The dbus conection name of the client application to be registered (e.g. :1.23 or org.example.service)</dox:d> |
181 | + </arg> |
182 | + </method> |
183 | + <method name="UnregisterAppMenu"> |
184 | + <dox:d> |
185 | + A method to allow removing an application menu from the database. |
186 | + </dox:d> |
187 | + <arg name="pid" type="u" direction="in"> |
188 | + <dox:d>The process id of the application</dox:d> |
189 | + </arg> |
190 | + <arg name="menuObjectPath" type="o" direction="in"> |
191 | + <dox:d>The dbus path for the registered application menu to be unregistered</dox:d> |
192 | + </arg> |
193 | + </method> |
194 | + |
195 | + <method name="RegisterSurfaceMenu"> |
196 | + <dox:d><![CDATA[ |
197 | + Associates a gmenumodel with a surface |
198 | + |
199 | + /note this method assumes that the connection from the caller is the DBus connection |
200 | + to use for the object. Applications that use multiple DBus connections will need to |
201 | + ensure this method is called with the same connection that implements the object. |
202 | + ]]></dox:d> |
203 | + <arg name="surface" type="s" direction="in"> |
204 | + <dox:d>The surface ID of the surface</dox:d> |
205 | + </arg> |
206 | + <arg name="menuObjectPath" type="o" direction="in"> |
207 | + <dox:d>The dbus path where the gmenumodel interface for the surface menu has been exported</dox:d> |
208 | + </arg> |
209 | + <arg name="actionObjectPath" type="o" direction="in"> |
210 | + <dox:d>The dbus path where the gactionmenu interface for the surface menu actions has been exported</dox:d> |
211 | + </arg> |
212 | + <arg name="service" type="s" direction="in"> |
213 | + <dox:d>The dbus conection name of the client application to be registered (e.g. :1.23 or org.example.service)</dox:d> |
214 | + </arg> |
215 | + </method> |
216 | + <method name="UnregisterSurfaceMenu"> |
217 | + <dox:d> |
218 | + A method to allow removing a surface menu from the database. |
219 | + </dox:d> |
220 | + <arg name="surfaceId" type="s" direction="in"> |
221 | + <dox:d>The surface id of the surface</dox:d> |
222 | + </arg> |
223 | + <arg name="menuObjectPath" type="o" direction="in"> |
224 | + <dox:d>The dbus path for the registered surface menu to be unregistered</dox:d> |
225 | + </arg> |
226 | + </method> |
227 | + </interface> |
228 | +</node> |
229 | |
230 | === added file 'src/ubuntuappmenu/gmenumodelexporter.cpp' |
231 | --- src/ubuntuappmenu/gmenumodelexporter.cpp 1970-01-01 00:00:00 +0000 |
232 | +++ src/ubuntuappmenu/gmenumodelexporter.cpp 2016-12-09 17:04:44 +0000 |
233 | @@ -0,0 +1,350 @@ |
234 | +/* |
235 | + * Copyright (C) 2016 Canonical, Ltd. |
236 | + * |
237 | + * This program is free software: you can redistribute it and/or modify it under |
238 | + * the terms of the GNU Lesser General Public License version 3, as published by |
239 | + * the Free Software Foundation. |
240 | + * |
241 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
242 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
243 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
244 | + * Lesser General Public License for more details. |
245 | + * |
246 | + * You should have received a copy of the GNU Lesser General Public License |
247 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
248 | + */ |
249 | + |
250 | +// Local |
251 | +#include "gmenumodelexporter.h" |
252 | +#include "registry.h" |
253 | +#include "logging.h" |
254 | + |
255 | +#include <QDebug> |
256 | + |
257 | +#include <functional> |
258 | + |
259 | +namespace { |
260 | + |
261 | +// Derive an action name from the label by removing spaces and Capitilizing the words. |
262 | +// Also remove mnemonics from the label. |
263 | +inline QString getActionString(QString label) |
264 | +{ |
265 | + QRegExp re("\\W"); |
266 | + label = label.replace(QRegExp("(&|_)"), ""); |
267 | + QStringList parts = label.split(re, QString::SkipEmptyParts); |
268 | + |
269 | + QString result; |
270 | + Q_FOREACH(const QString& part, parts) { |
271 | + result += part[0].toUpper(); |
272 | + result += part.right(part.length()-1); |
273 | + } |
274 | + return result; |
275 | +} |
276 | + |
277 | +static void activate_cb(GSimpleAction *action, GVariant *, gpointer user_data) |
278 | +{ |
279 | + qCDebug(ubuntuappmenu, "Activate menu action '%s'", g_action_get_name(G_ACTION(action))); |
280 | + auto item = static_cast<UbuntuPlatformMenuItem*>(user_data); |
281 | + item->activated(); |
282 | +} |
283 | + |
284 | +static uint s_menuId = 0; |
285 | + |
286 | +#define MENU_OBJECT_PATH "/com/ubuntu/Menu/%1" |
287 | + |
288 | +} // namespace |
289 | + |
290 | + |
291 | +UbuntuMenuBarExporter::UbuntuMenuBarExporter(UbuntuPlatformMenuBar * bar) |
292 | + : UbuntuGMenuModelExporter(bar) |
293 | +{ |
294 | + qCDebug(ubuntuappmenu, "UbuntuMenuBarExporter::UbuntuMenuBarExporter"); |
295 | + |
296 | + connect(bar, &UbuntuPlatformMenuBar::structureChanged, this, [this]() { |
297 | + m_structureTimer.start(); |
298 | + }); |
299 | + connect(&m_structureTimer, &QTimer::timeout, this, [this, bar]() { |
300 | + clear(); |
301 | + Q_FOREACH(QPlatformMenu *platformMenu, bar->menus()) { |
302 | + GMenuItem* item = createSubmenu(platformMenu, nullptr); |
303 | + if (item) { |
304 | + g_menu_append_item(m_gmainMenu, item); |
305 | + g_object_unref(item); |
306 | + } |
307 | + } |
308 | + }); |
309 | + |
310 | + connect(bar, &UbuntuPlatformMenuBar::ready, this, [this]() { |
311 | + exportModels(); |
312 | + }); |
313 | +} |
314 | + |
315 | +UbuntuMenuBarExporter::~UbuntuMenuBarExporter() |
316 | +{ |
317 | + qCDebug(ubuntuappmenu, "UbuntuMenuBarExporter::~UbuntuMenuBarExporter"); |
318 | +} |
319 | + |
320 | +UbuntuMenuExporter::UbuntuMenuExporter(UbuntuPlatformMenu *menu) |
321 | + : UbuntuGMenuModelExporter(menu) |
322 | +{ |
323 | + qCDebug(ubuntuappmenu, "UbuntuMenuExporter::UbuntuMenuExporter"); |
324 | + |
325 | + connect(menu, &UbuntuPlatformMenu::structureChanged, this, [this]() { |
326 | + m_structureTimer.start(); |
327 | + }); |
328 | + connect(&m_structureTimer, &QTimer::timeout, this, [this, menu]() { |
329 | + clear(); |
330 | + addSubmenuItems(menu, m_gmainMenu); |
331 | + }); |
332 | + addSubmenuItems(menu, m_gmainMenu); |
333 | +} |
334 | + |
335 | +UbuntuMenuExporter::~UbuntuMenuExporter() |
336 | +{ |
337 | + qCDebug(ubuntuappmenu, "UbuntuMenuExporter::~UbuntuMenuExporter"); |
338 | +} |
339 | + |
340 | +UbuntuGMenuModelExporter::UbuntuGMenuModelExporter(QObject *parent) |
341 | + : QObject(parent) |
342 | + , m_connection(nullptr) |
343 | + , m_gmainMenu(g_menu_new()) |
344 | + , m_gactionGroup(g_simple_action_group_new()) |
345 | + , m_exportedModel(0) |
346 | + , m_exportedActions(0) |
347 | + , m_menuPath(QStringLiteral(MENU_OBJECT_PATH).arg(s_menuId++)) |
348 | +{ |
349 | + m_structureTimer.setSingleShot(true); |
350 | + m_structureTimer.setInterval(0); |
351 | +} |
352 | + |
353 | +UbuntuGMenuModelExporter::~UbuntuGMenuModelExporter() |
354 | +{ |
355 | + unexportModels(); |
356 | + clear(); |
357 | + |
358 | + g_object_unref(m_gmainMenu); |
359 | + g_object_unref(m_gactionGroup); |
360 | +} |
361 | + |
362 | +// Clear the menu and actions that have been created. |
363 | +void UbuntuGMenuModelExporter::clear() |
364 | +{ |
365 | + Q_FOREACH(const QMetaObject::Connection& connection, m_propertyConnections) { |
366 | + QObject::disconnect(connection); |
367 | + } |
368 | + |
369 | + g_menu_remove_all(m_gmainMenu); |
370 | + |
371 | + Q_FOREACH(const QByteArray& action, m_actions) { |
372 | + g_action_map_remove_action(G_ACTION_MAP(m_gactionGroup), action.constData()); |
373 | + } |
374 | + m_actions.clear(); |
375 | +} |
376 | + |
377 | +// Export the model on dbus |
378 | +void UbuntuGMenuModelExporter::exportModels() |
379 | +{ |
380 | + GError *error = nullptr; |
381 | + m_connection = g_bus_get_sync (G_BUS_TYPE_SESSION, nullptr, &error); |
382 | + if (!m_connection) { |
383 | + qCWarning(ubuntuappmenu, "Failed to retreive session bus - %s", error ? error->message : "unknown error"); |
384 | + g_error_free (error); |
385 | + return; |
386 | + } |
387 | + |
388 | + QByteArray menuPath(m_menuPath.toUtf8()); |
389 | + |
390 | + if (m_exportedModel == 0) { |
391 | + m_exportedModel = g_dbus_connection_export_menu_model(m_connection, menuPath.constData(), G_MENU_MODEL(m_gmainMenu), &error); |
392 | + if (m_exportedModel == 0) { |
393 | + qCWarning(ubuntuappmenu, "Failed to export menu - %s", error ? error->message : "unknown error"); |
394 | + g_error_free (error); |
395 | + error = nullptr; |
396 | + } else { |
397 | + qCDebug(ubuntuappmenu, "Exported menu on %s", g_dbus_connection_get_unique_name(m_connection)); |
398 | + } |
399 | + } |
400 | + |
401 | + if (m_exportedActions == 0) { |
402 | + m_exportedActions = g_dbus_connection_export_action_group(m_connection, menuPath.constData(), G_ACTION_GROUP(m_gactionGroup), &error); |
403 | + if (m_exportedActions == 0) { |
404 | + qCWarning(ubuntuappmenu, "Failed to export actions - %s", error ? error->message : "unknown error"); |
405 | + g_error_free (error); |
406 | + error = nullptr; |
407 | + } else { |
408 | + qCDebug(ubuntuappmenu, "Exported actions on %s", g_dbus_connection_get_unique_name(m_connection)); |
409 | + } |
410 | + } |
411 | +} |
412 | + |
413 | +// Unexport the model |
414 | +void UbuntuGMenuModelExporter::unexportModels() |
415 | +{ |
416 | + GError *error = nullptr; |
417 | + if (!m_connection) { |
418 | + qCWarning(ubuntuappmenu, "Failed to retreive session bus - %s", error ? error->message : "unknown error"); |
419 | + return; |
420 | + } |
421 | + |
422 | + if (m_exportedModel != 0) { |
423 | + g_dbus_connection_unexport_menu_model(m_connection, m_exportedModel); |
424 | + m_exportedModel = 0; |
425 | + } |
426 | + if (m_exportedActions != 0) { |
427 | + g_dbus_connection_unexport_action_group(m_connection, m_exportedActions); |
428 | + m_exportedActions = 0; |
429 | + } |
430 | + g_object_unref(m_connection); |
431 | + m_connection = nullptr; |
432 | +} |
433 | + |
434 | +// Create a submenu for the given platform menu. |
435 | +// Returns a gmenuitem entry for the menu, which must be cleaned up using g_object_unref. |
436 | +// If forItem is suplied, use it's label. |
437 | +GMenuItem *UbuntuGMenuModelExporter::createSubmenu(QPlatformMenu *platformMenu, UbuntuPlatformMenuItem *forItem) |
438 | +{ |
439 | + UbuntuPlatformMenu* gplatformMenu = static_cast<UbuntuPlatformMenu*>(platformMenu); |
440 | + if (!gplatformMenu) return nullptr; |
441 | + GMenu* menu = g_menu_new(); |
442 | + |
443 | + QByteArray label; |
444 | + if (forItem) { |
445 | + label = UbuntuPlatformMenuItem::get_text(forItem).toUtf8(); |
446 | + } else { |
447 | + label = UbuntuPlatformMenu::get_text(gplatformMenu).toUtf8(); |
448 | + } |
449 | + |
450 | + addSubmenuItems(gplatformMenu, menu); |
451 | + |
452 | + GMenuItem* gmenuItem = g_menu_item_new_submenu(label.constData(), G_MENU_MODEL(menu)); |
453 | + g_object_unref(menu); |
454 | + return gmenuItem; |
455 | +} |
456 | + |
457 | +// Add a platform menu's items to the given gmenu. |
458 | +// The items are inserted into menus sections, split by the menu separators. |
459 | +void UbuntuGMenuModelExporter::addSubmenuItems(UbuntuPlatformMenu* gplatformMenu, GMenu* menu) |
460 | +{ |
461 | + auto iter = gplatformMenu->menuItems().begin(); |
462 | + auto lastSectionStart = iter; |
463 | + // Iterate through all the menu items adding sections when a separator is found. |
464 | + for (; iter != gplatformMenu->menuItems().end(); ++iter) { |
465 | + UbuntuPlatformMenuItem* gplatformMenuItem = static_cast<UbuntuPlatformMenuItem*>(*iter); |
466 | + if (!gplatformMenuItem) continue; |
467 | + |
468 | + // don't add a section until we have separator |
469 | + if (UbuntuPlatformMenuItem::get_separator(gplatformMenuItem)) { |
470 | + if (lastSectionStart != gplatformMenu->menuItems().begin()) { |
471 | + GMenuItem* section = createSection(lastSectionStart, iter); |
472 | + g_menu_append_item(menu, section); |
473 | + g_object_unref(section); |
474 | + } |
475 | + lastSectionStart = iter + 1; |
476 | + } else if (lastSectionStart == gplatformMenu->menuItems().begin()) { |
477 | + processItemForGMenu(gplatformMenuItem, menu); |
478 | + } |
479 | + } |
480 | + |
481 | + // Add the last section |
482 | + if (lastSectionStart != gplatformMenu->menuItems().begin() && |
483 | + lastSectionStart != gplatformMenu->menuItems().end()) { |
484 | + GMenuItem* gsectionItem = createSection(lastSectionStart, gplatformMenu->menuItems().end()); |
485 | + g_menu_append_item(menu, gsectionItem); |
486 | + g_object_unref(gsectionItem); |
487 | + } |
488 | +} |
489 | + |
490 | +// Create and return a gmenu item for the given platform menu item. |
491 | +// Returned GMenuItem must be cleaned up using g_object_unref |
492 | +GMenuItem *UbuntuGMenuModelExporter::createMenuItem(QPlatformMenuItem *platformMenuItem) |
493 | +{ |
494 | + UbuntuPlatformMenuItem* gplatformMenuItem = static_cast<UbuntuPlatformMenuItem*>(platformMenuItem); |
495 | + if (!gplatformMenuItem) return nullptr; |
496 | + |
497 | + QByteArray label(UbuntuPlatformMenuItem::get_text(gplatformMenuItem).toUtf8()); |
498 | + QByteArray actionLabel(getActionString(UbuntuPlatformMenuItem::get_text(gplatformMenuItem)).toUtf8()); |
499 | + QByteArray shortcut(UbuntuPlatformMenuItem::get_shortcut(gplatformMenuItem).toString(QKeySequence::NativeText).toUtf8()); |
500 | + |
501 | + GMenuItem* gmenuItem = g_menu_item_new(label.constData(), nullptr); |
502 | + g_menu_item_set_attribute(gmenuItem, "accel", "s", shortcut.constData()); |
503 | + g_menu_item_set_detailed_action(gmenuItem, ("unity." + actionLabel).constData()); |
504 | + |
505 | + addAction(actionLabel, gplatformMenuItem); |
506 | + return gmenuItem; |
507 | +} |
508 | + |
509 | +// Create a menu section for a section of separated menu items. |
510 | +// Returned GMenuItem must be cleaned up using g_object_unref |
511 | +GMenuItem *UbuntuGMenuModelExporter::createSection(QList<QPlatformMenuItem *>::const_iterator iter, QList<QPlatformMenuItem *>::const_iterator end) |
512 | +{ |
513 | + GMenu* gsectionMenu = g_menu_new(); |
514 | + for (; iter != end; ++iter) { |
515 | + processItemForGMenu(*iter, gsectionMenu); |
516 | + } |
517 | + GMenuItem* gsectionItem = g_menu_item_new_section("", G_MENU_MODEL(gsectionMenu)); |
518 | + g_object_unref(gsectionMenu); |
519 | + return gsectionItem; |
520 | +} |
521 | + |
522 | +// Add the given platform menu item to the menu. |
523 | +// If it has an attached submenu, then create and add the submenu. |
524 | +void UbuntuGMenuModelExporter::processItemForGMenu(QPlatformMenuItem *platformMenuItem, GMenu *gmenu) |
525 | +{ |
526 | + UbuntuPlatformMenuItem* gplatformMenuItem = static_cast<UbuntuPlatformMenuItem*>(platformMenuItem); |
527 | + if (!gplatformMenuItem) return; |
528 | + |
529 | + GMenuItem* gmenuItem = gplatformMenuItem->menu() ? createSubmenu(gplatformMenuItem->menu(), gplatformMenuItem) : |
530 | + createMenuItem(gplatformMenuItem); |
531 | + if (gmenuItem) { |
532 | + g_menu_append_item(gmenu, gmenuItem); |
533 | + g_object_unref(gmenuItem); |
534 | + } |
535 | +} |
536 | + |
537 | +// Create and add an action for a menu item. |
538 | +void UbuntuGMenuModelExporter::addAction(const QByteArray &name, UbuntuPlatformMenuItem *gplatformMenuItem) |
539 | +{ |
540 | + disconnect(gplatformMenuItem, &UbuntuPlatformMenuItem::checkedChanged, this, 0); |
541 | + disconnect(gplatformMenuItem, &UbuntuPlatformMenuItem::enabledChanged, this, 0); |
542 | + |
543 | + if (m_actions.contains(name)) { |
544 | + g_action_map_remove_action(G_ACTION_MAP(m_gactionGroup), name.constData()); |
545 | + m_actions.remove(name); |
546 | + } |
547 | + |
548 | + bool checkable = UbuntuPlatformMenuItem::get_checkable(gplatformMenuItem); |
549 | + |
550 | + GSimpleAction* action = nullptr; |
551 | + if (checkable) { |
552 | + bool checked = UbuntuPlatformMenuItem::get_checked(gplatformMenuItem); |
553 | + action = g_simple_action_new_stateful(name.constData(), nullptr, g_variant_new_boolean(checked)); |
554 | + |
555 | + std::function<void(bool)> updateChecked = [gplatformMenuItem, action](bool checked) { |
556 | + auto type = g_action_get_state_type(G_ACTION(action)); |
557 | + if (type && g_variant_type_equal(type, G_VARIANT_TYPE_BOOLEAN)) { |
558 | + g_simple_action_set_state(action, g_variant_new_boolean(checked ? TRUE : FALSE)); |
559 | + } |
560 | + }; |
561 | + // save the connection to disconnect in UbuntuGMenuModelExporter::clear() |
562 | + m_propertyConnections << connect(gplatformMenuItem, &UbuntuPlatformMenuItem::checkedChanged, this, updateChecked); |
563 | + } else { |
564 | + action = g_simple_action_new(name.constData(), nullptr); |
565 | + } |
566 | + |
567 | + // Enabled update |
568 | + std::function<void(bool)> updateEnabled = [gplatformMenuItem, action](bool enabled) { |
569 | + GValue value = G_VALUE_INIT; |
570 | + g_value_init (&value, G_TYPE_BOOLEAN); |
571 | + g_value_set_boolean(&value, enabled ? TRUE : FALSE); |
572 | + g_object_set_property(G_OBJECT(action), "enabled", &value); |
573 | + }; |
574 | + updateEnabled(UbuntuPlatformMenuItem::get_enabled(gplatformMenuItem)); |
575 | + // save the connection to disconnect in UbuntuGMenuModelExporter::clear() |
576 | + m_propertyConnections << connect(gplatformMenuItem, &UbuntuPlatformMenuItem::enabledChanged, this, updateEnabled); |
577 | + |
578 | + g_signal_connect(action, "activate", G_CALLBACK(activate_cb), gplatformMenuItem); |
579 | + |
580 | + m_actions.insert(name); |
581 | + g_action_map_add_action(G_ACTION_MAP(m_gactionGroup), G_ACTION(action)); |
582 | + g_object_unref(action); |
583 | +} |
584 | |
585 | === added file 'src/ubuntuappmenu/gmenumodelexporter.h' |
586 | --- src/ubuntuappmenu/gmenumodelexporter.h 1970-01-01 00:00:00 +0000 |
587 | +++ src/ubuntuappmenu/gmenumodelexporter.h 2016-12-09 17:04:44 +0000 |
588 | @@ -0,0 +1,83 @@ |
589 | +/* |
590 | + * Copyright (C) 2016 Canonical, Ltd. |
591 | + * |
592 | + * This program is free software: you can redistribute it and/or modify it under |
593 | + * the terms of the GNU Lesser General Public License version 3, as published by |
594 | + * the Free Software Foundation. |
595 | + * |
596 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
597 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
598 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
599 | + * Lesser General Public License for more details. |
600 | + * |
601 | + * You should have received a copy of the GNU Lesser General Public License |
602 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
603 | + */ |
604 | + |
605 | +#ifndef GMENUMODELEXPORTER_H |
606 | +#define GMENUMODELEXPORTER_H |
607 | + |
608 | +#include "gmenumodelplatformmenu.h" |
609 | + |
610 | +#include <gio/gio.h> |
611 | + |
612 | +#include <QTimer> |
613 | +#include <QSet> |
614 | +#include <QMetaObject> |
615 | + |
616 | +// Base class for a gmenumodel exporter |
617 | +class UbuntuGMenuModelExporter : public QObject |
618 | +{ |
619 | + Q_OBJECT |
620 | +public: |
621 | + virtual ~UbuntuGMenuModelExporter(); |
622 | + |
623 | + void exportModels(); |
624 | + void unexportModels(); |
625 | + |
626 | + QString menuPath() const { return m_menuPath;} |
627 | + |
628 | +protected: |
629 | + UbuntuGMenuModelExporter(QObject *parent); |
630 | + |
631 | + GMenuItem *createSubmenu(QPlatformMenu* platformMenu, UbuntuPlatformMenuItem* forItem); |
632 | + GMenuItem *createMenuItem(QPlatformMenuItem* platformMenuItem); |
633 | + GMenuItem *createSection(QList<QPlatformMenuItem*>::const_iterator iter, QList<QPlatformMenuItem*>::const_iterator end); |
634 | + void addAction(const QByteArray& name, UbuntuPlatformMenuItem* gplatformItem); |
635 | + |
636 | + void addSubmenuItems(UbuntuPlatformMenu* gplatformMenu, GMenu* menu); |
637 | + void processItemForGMenu(QPlatformMenuItem* item, GMenu* gmenu); |
638 | + |
639 | + void clear(); |
640 | + |
641 | +protected: |
642 | + GDBusConnection *m_connection; |
643 | + GMenu *m_gmainMenu; |
644 | + GSimpleActionGroup *m_gactionGroup; |
645 | + QSet<QByteArray> m_actions; |
646 | + guint m_exportedModel; |
647 | + guint m_exportedActions; |
648 | + QTimer m_structureTimer; |
649 | + QString m_menuPath; |
650 | + |
651 | + QVector<QMetaObject::Connection> m_propertyConnections; |
652 | +}; |
653 | + |
654 | +// Class which exports a qt platform menu bar. |
655 | +class UbuntuMenuBarExporter : public UbuntuGMenuModelExporter |
656 | +{ |
657 | +public: |
658 | + UbuntuMenuBarExporter(UbuntuPlatformMenuBar *parent); |
659 | + ~UbuntuMenuBarExporter(); |
660 | +}; |
661 | + |
662 | +// Class which exports a qt platform menu. |
663 | +// This will allow exporting of context menus. |
664 | +class UbuntuMenuExporter : public UbuntuGMenuModelExporter |
665 | +{ |
666 | +public: |
667 | + UbuntuMenuExporter(UbuntuPlatformMenu *parent); |
668 | + ~UbuntuMenuExporter(); |
669 | +}; |
670 | + |
671 | +#endif // GMENUMODELEXPORTER_H |
672 | |
673 | === added file 'src/ubuntuappmenu/gmenumodelplatformmenu.cpp' |
674 | --- src/ubuntuappmenu/gmenumodelplatformmenu.cpp 1970-01-01 00:00:00 +0000 |
675 | +++ src/ubuntuappmenu/gmenumodelplatformmenu.cpp 2016-12-09 17:04:44 +0000 |
676 | @@ -0,0 +1,526 @@ |
677 | +/* |
678 | + * Copyright (C) 2016 Canonical, Ltd. |
679 | + * |
680 | + * This program is free software: you can redistribute it and/or modify it under |
681 | + * the terms of the GNU Lesser General Public License version 3, as published by |
682 | + * the Free Software Foundation. |
683 | + * |
684 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
685 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
686 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
687 | + * Lesser General Public License for more details. |
688 | + * |
689 | + * You should have received a copy of the GNU Lesser General Public License |
690 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
691 | + */ |
692 | + |
693 | +// Local |
694 | +#include "gmenumodelplatformmenu.h" |
695 | +#include "gmenumodelexporter.h" |
696 | +#include "registry.h" |
697 | +#include "menuregistrar.h" |
698 | +#include "logging.h" |
699 | + |
700 | +// Qt |
701 | +#include <QDebug> |
702 | +#include <QWindow> |
703 | +#include <QCoreApplication> |
704 | + |
705 | +#define BAR_DEBUG_MSG qCDebug(ubuntuappmenu).nospace() << "UbuntuPlatformMenuBar[" << (void*)this <<"]::" << __func__ |
706 | +#define MENU_DEBUG_MSG qCDebug(ubuntuappmenu).nospace() << "UbuntuPlatformMenu[" << (void*)this <<"]::" << __func__ |
707 | +#define ITEM_DEBUG_MSG qCDebug(ubuntuappmenu).nospace() << "UbuntuPlatformMenuItem[" << (void*)this <<"]::" << __func__ |
708 | + |
709 | +namespace { |
710 | + |
711 | +int logRecusion = 0; |
712 | + |
713 | +} |
714 | + |
715 | +QDebug operator<<(QDebug stream, UbuntuPlatformMenuBar* bar) { |
716 | + if (bar) return bar->operator<<(stream); |
717 | + return stream; |
718 | +} |
719 | +QDebug operator<<(QDebug stream, UbuntuPlatformMenu* menu) { |
720 | + if (menu) return menu->operator<<(stream); |
721 | + return stream; |
722 | +} |
723 | +QDebug operator<<(QDebug stream, UbuntuPlatformMenuItem* menuItem) { |
724 | + if (menuItem) return menuItem->operator<<(stream); |
725 | + return stream; |
726 | +} |
727 | + |
728 | +UbuntuPlatformMenuBar::UbuntuPlatformMenuBar() |
729 | + : m_exporter(new UbuntuMenuBarExporter(this)) |
730 | + , m_registrar(new UbuntuMenuRegistrar()) |
731 | + , m_ready(false) |
732 | +{ |
733 | + BAR_DEBUG_MSG << "()"; |
734 | + |
735 | + connect(this, &UbuntuPlatformMenuBar::menuInserted, this, &UbuntuPlatformMenuBar::structureChanged); |
736 | + connect(this,&UbuntuPlatformMenuBar::menuRemoved, this, &UbuntuPlatformMenuBar::structureChanged); |
737 | +} |
738 | + |
739 | +UbuntuPlatformMenuBar::~UbuntuPlatformMenuBar() |
740 | +{ |
741 | + BAR_DEBUG_MSG << "()"; |
742 | +} |
743 | + |
744 | +void UbuntuPlatformMenuBar::insertMenu(QPlatformMenu *menu, QPlatformMenu *before) |
745 | +{ |
746 | + BAR_DEBUG_MSG << "(menu=" << menu << ", before=" << before << ")"; |
747 | + |
748 | + if (m_menus.contains(menu)) return; |
749 | + |
750 | + if (!before) { |
751 | + m_menus.push_back(menu); |
752 | + } else { |
753 | + for (auto iter = m_menus.begin(); iter != m_menus.end(); ++iter) { |
754 | + if (*iter == before) { |
755 | + m_menus.insert(iter, menu); |
756 | + break; |
757 | + } |
758 | + } |
759 | + } |
760 | + connect(static_cast<UbuntuPlatformMenu*>(menu), &UbuntuPlatformMenu::structureChanged, |
761 | + this, &UbuntuPlatformMenuBar::structureChanged); |
762 | + Q_EMIT menuInserted(menu); |
763 | +} |
764 | + |
765 | +void UbuntuPlatformMenuBar::removeMenu(QPlatformMenu *menu) |
766 | +{ |
767 | + BAR_DEBUG_MSG << "(menu=" << menu << ")"; |
768 | + |
769 | + QMutableListIterator<QPlatformMenu*> iterator(m_menus); |
770 | + while(iterator.hasNext()) { |
771 | + if (iterator.next() == menu) { |
772 | + iterator.remove(); |
773 | + break; |
774 | + } |
775 | + } |
776 | + disconnect(static_cast<UbuntuPlatformMenu*>(menu), &UbuntuPlatformMenu::structureChanged, |
777 | + this, &UbuntuPlatformMenuBar::structureChanged); |
778 | + Q_EMIT menuRemoved(menu); |
779 | +} |
780 | + |
781 | +void UbuntuPlatformMenuBar::syncMenu(QPlatformMenu *menu) |
782 | +{ |
783 | + BAR_DEBUG_MSG << "(menu=" << menu << ")"; |
784 | + |
785 | + Q_UNUSED(menu) |
786 | +} |
787 | + |
788 | +void UbuntuPlatformMenuBar::handleReparent(QWindow *parentWindow) |
789 | +{ |
790 | + BAR_DEBUG_MSG << "(parentWindow=" << parentWindow << ")"; |
791 | + |
792 | + setReady(true); |
793 | + m_registrar->registerMenuForWindow(parentWindow, QDBusObjectPath(m_exporter->menuPath())); |
794 | +} |
795 | + |
796 | +QPlatformMenu *UbuntuPlatformMenuBar::menuForTag(quintptr tag) const |
797 | +{ |
798 | + Q_FOREACH(QPlatformMenu* menu, m_menus) { |
799 | + if (menu->tag() == tag) { |
800 | + return menu; |
801 | + } |
802 | + } |
803 | + return nullptr; |
804 | +} |
805 | + |
806 | +const QList<QPlatformMenu *> UbuntuPlatformMenuBar::menus() const |
807 | +{ |
808 | + return m_menus; |
809 | +} |
810 | + |
811 | +QDebug UbuntuPlatformMenuBar::operator<<(QDebug stream) |
812 | +{ |
813 | + stream.nospace().noquote() << QString("%1").arg("", logRecusion, QLatin1Char('\t')) |
814 | + << "UbuntuPlatformMenuBar(this=" << (void*)this << ")" << endl; |
815 | + Q_FOREACH(QPlatformMenu* menu, m_menus) { |
816 | + auto myMenu = static_cast<UbuntuPlatformMenu*>(menu); |
817 | + if (myMenu) { |
818 | + logRecusion++; |
819 | + stream << myMenu; |
820 | + logRecusion--; |
821 | + } |
822 | + } |
823 | + |
824 | + return stream; |
825 | +} |
826 | + |
827 | +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) |
828 | +QPlatformMenu *UbuntuPlatformMenuBar::createMenu() const |
829 | +{ |
830 | + return new UbuntuPlatformMenu(); |
831 | +} |
832 | +#endif |
833 | + |
834 | +void UbuntuPlatformMenuBar::setReady(bool isReady) |
835 | +{ |
836 | + if (m_ready != isReady) { |
837 | + m_ready = isReady; |
838 | + Q_EMIT ready(); |
839 | + } |
840 | +} |
841 | + |
842 | +////////////////////////////////////////////////////////////// |
843 | + |
844 | +UbuntuPlatformMenu::UbuntuPlatformMenu() |
845 | + : m_parentWindow(nullptr) |
846 | + , m_exporter(nullptr) |
847 | + , m_registrar(nullptr) |
848 | +{ |
849 | + MENU_DEBUG_MSG << "()"; |
850 | + |
851 | + connect(this, &UbuntuPlatformMenu::menuItemInserted, this, &UbuntuPlatformMenu::structureChanged); |
852 | + connect(this, &UbuntuPlatformMenu::menuItemRemoved, this, &UbuntuPlatformMenu::structureChanged); |
853 | +} |
854 | + |
855 | +UbuntuPlatformMenu::~UbuntuPlatformMenu() |
856 | +{ |
857 | + MENU_DEBUG_MSG << "()"; |
858 | +} |
859 | + |
860 | +void UbuntuPlatformMenu::insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) |
861 | +{ |
862 | + MENU_DEBUG_MSG << "(menuItem=" << menuItem << ", before=" << before << ")"; |
863 | + |
864 | + if (m_menuItems.contains(menuItem)) return; |
865 | + |
866 | + if (!before) { |
867 | + m_menuItems.push_back(menuItem); |
868 | + } else { |
869 | + for (auto iter = m_menuItems.begin(); iter != m_menuItems.end(); ++iter) { |
870 | + if (*iter == before) { |
871 | + m_menuItems.insert(iter, menuItem); |
872 | + break; |
873 | + } |
874 | + } |
875 | + } |
876 | + |
877 | + Q_EMIT menuItemInserted(menuItem); |
878 | +} |
879 | + |
880 | +void UbuntuPlatformMenu::removeMenuItem(QPlatformMenuItem *menuItem) |
881 | +{ |
882 | + MENU_DEBUG_MSG << "(menuItem=" << menuItem << ")"; |
883 | + |
884 | + QMutableListIterator<QPlatformMenuItem*> iterator(m_menuItems); |
885 | + while(iterator.hasNext()) { |
886 | + if (iterator.next() == menuItem) { |
887 | + iterator.remove(); |
888 | + break; |
889 | + } |
890 | + } |
891 | + Q_EMIT menuItemRemoved(menuItem); |
892 | +} |
893 | + |
894 | +void UbuntuPlatformMenu::syncMenuItem(QPlatformMenuItem *menuItem) |
895 | +{ |
896 | + MENU_DEBUG_MSG << "(menuItem=" << menuItem << ")"; |
897 | + |
898 | + Q_UNUSED(menuItem) |
899 | +} |
900 | + |
901 | +void UbuntuPlatformMenu::syncSeparatorsCollapsible(bool enable) |
902 | +{ |
903 | + MENU_DEBUG_MSG << "(enable=" << enable << ")"; |
904 | + Q_UNUSED(enable) |
905 | +} |
906 | + |
907 | +void UbuntuPlatformMenu::setTag(quintptr tag) |
908 | +{ |
909 | + MENU_DEBUG_MSG << "(tag=" << tag << ")"; |
910 | + m_tag = tag; |
911 | +} |
912 | + |
913 | +quintptr UbuntuPlatformMenu::tag() const |
914 | +{ |
915 | + return m_tag; |
916 | +} |
917 | + |
918 | +void UbuntuPlatformMenu::setText(const QString &text) |
919 | +{ |
920 | + MENU_DEBUG_MSG << "(text=" << text << ")"; |
921 | + if (m_text != text) { |
922 | + m_text = text; |
923 | + Q_EMIT propertyChanged(); |
924 | + } |
925 | +} |
926 | + |
927 | +void UbuntuPlatformMenu::setIcon(const QIcon &icon) |
928 | +{ |
929 | + MENU_DEBUG_MSG << "(icon=" << icon.name() << ")"; |
930 | + |
931 | + if (!icon.isNull() || (!m_icon.isNull() && icon.isNull())) { |
932 | + m_icon = icon; |
933 | + Q_EMIT propertyChanged(); |
934 | + } |
935 | +} |
936 | + |
937 | +void UbuntuPlatformMenu::setEnabled(bool enabled) |
938 | +{ |
939 | + MENU_DEBUG_MSG << "(enabled=" << enabled << ")"; |
940 | + |
941 | + if (m_enabled != enabled) { |
942 | + m_enabled = enabled; |
943 | + Q_EMIT propertyChanged(); |
944 | + } |
945 | +} |
946 | + |
947 | +void UbuntuPlatformMenu::setVisible(bool isVisible) |
948 | +{ |
949 | + MENU_DEBUG_MSG << "(visible=" << isVisible << ")"; |
950 | + |
951 | + if (m_visible != isVisible) { |
952 | + m_visible = isVisible; |
953 | + Q_EMIT propertyChanged(); |
954 | + } |
955 | +} |
956 | + |
957 | +void UbuntuPlatformMenu::setMinimumWidth(int width) |
958 | +{ |
959 | + MENU_DEBUG_MSG << "(width=" << width << ")"; |
960 | + |
961 | + Q_UNUSED(width) |
962 | +} |
963 | + |
964 | +void UbuntuPlatformMenu::setFont(const QFont &font) |
965 | +{ |
966 | + MENU_DEBUG_MSG << "(font=" << font << ")"; |
967 | + |
968 | + Q_UNUSED(font) |
969 | +} |
970 | + |
971 | +void UbuntuPlatformMenu::showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item) |
972 | +{ |
973 | + MENU_DEBUG_MSG << "(parentWindow=" << parentWindow << ", targetRect=" << targetRect << ", item=" << item << ")"; |
974 | + |
975 | + if (!m_exporter) { |
976 | + m_exporter.reset(new UbuntuMenuExporter(this)); |
977 | + m_exporter->exportModels(); |
978 | + } |
979 | + |
980 | + if (parentWindow != m_parentWindow) { |
981 | + if (m_parentWindow) { |
982 | + m_registrar->unregisterMenu(); |
983 | + } |
984 | + |
985 | + m_parentWindow = parentWindow; |
986 | + |
987 | + if (m_parentWindow) { |
988 | + if (!m_registrar) m_registrar.reset(new UbuntuMenuRegistrar); |
989 | + m_registrar->registerMenuForWindow(const_cast<QWindow*>(m_parentWindow), |
990 | + QDBusObjectPath(m_exporter->menuPath())); |
991 | + } |
992 | + } |
993 | + |
994 | + Q_UNUSED(targetRect); |
995 | + Q_UNUSED(item); |
996 | + setVisible(true); |
997 | +} |
998 | + |
999 | +void UbuntuPlatformMenu::dismiss() |
1000 | +{ |
1001 | + MENU_DEBUG_MSG << "()"; |
1002 | + |
1003 | + if (m_registrar) { m_registrar->unregisterMenu(); } |
1004 | + if (m_exporter) { m_exporter->unexportModels(); } |
1005 | +} |
1006 | + |
1007 | +QPlatformMenuItem *UbuntuPlatformMenu::menuItemAt(int position) const |
1008 | +{ |
1009 | + if (position < 0 || position >= m_menuItems.count()) return nullptr; |
1010 | + return m_menuItems.at(position); |
1011 | +} |
1012 | + |
1013 | +QPlatformMenuItem *UbuntuPlatformMenu::menuItemForTag(quintptr tag) const |
1014 | +{ |
1015 | + Q_FOREACH(QPlatformMenuItem* menuItem, m_menuItems) { |
1016 | + if (menuItem->tag() == tag) { |
1017 | + return menuItem; |
1018 | + } |
1019 | + } |
1020 | + return nullptr; |
1021 | +} |
1022 | + |
1023 | +QPlatformMenuItem *UbuntuPlatformMenu::createMenuItem() const |
1024 | +{ |
1025 | + return new UbuntuPlatformMenuItem(); |
1026 | +} |
1027 | + |
1028 | +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) |
1029 | +QPlatformMenu *UbuntuPlatformMenu::createSubMenu() const |
1030 | +{ |
1031 | + return new UbuntuPlatformMenu(); |
1032 | +} |
1033 | +#endif |
1034 | + |
1035 | +const QList<QPlatformMenuItem *> UbuntuPlatformMenu::menuItems() const |
1036 | +{ |
1037 | + return m_menuItems; |
1038 | +} |
1039 | + |
1040 | +QDebug UbuntuPlatformMenu::operator<<(QDebug stream) |
1041 | +{ |
1042 | + stream.nospace().noquote() << QString("%1").arg("", logRecusion, QLatin1Char('\t')) |
1043 | + << "UbuntuPlatformMenu(this=" << (void*)this << ", text=\"" << m_text << "\")" << endl; |
1044 | + Q_FOREACH(QPlatformMenuItem* item, m_menuItems) { |
1045 | + logRecusion++; |
1046 | + auto myItem = static_cast<UbuntuPlatformMenuItem*>(item); |
1047 | + if (myItem) { |
1048 | + stream << myItem; |
1049 | + } |
1050 | + logRecusion--; |
1051 | + } |
1052 | + return stream; |
1053 | +} |
1054 | + |
1055 | +////////////////////////////////////////////////////////////// |
1056 | + |
1057 | +UbuntuPlatformMenuItem::UbuntuPlatformMenuItem() |
1058 | + : m_menu(nullptr) |
1059 | +{ |
1060 | + ITEM_DEBUG_MSG << "()"; |
1061 | +} |
1062 | + |
1063 | +UbuntuPlatformMenuItem::~UbuntuPlatformMenuItem() |
1064 | +{ |
1065 | + ITEM_DEBUG_MSG << "()"; |
1066 | +} |
1067 | + |
1068 | +void UbuntuPlatformMenuItem::setTag(quintptr tag) |
1069 | +{ |
1070 | + ITEM_DEBUG_MSG << "(tag=" << tag << ")"; |
1071 | + m_tag = tag; |
1072 | +} |
1073 | + |
1074 | +quintptr UbuntuPlatformMenuItem::tag() const |
1075 | +{ |
1076 | + return m_tag; |
1077 | +} |
1078 | + |
1079 | +void UbuntuPlatformMenuItem::setText(const QString &text) |
1080 | +{ |
1081 | + ITEM_DEBUG_MSG << "(text=" << text << ")"; |
1082 | + if (m_text != text) { |
1083 | + m_text = text; |
1084 | + Q_EMIT propertyChanged(); |
1085 | + } |
1086 | +} |
1087 | + |
1088 | +void UbuntuPlatformMenuItem::setIcon(const QIcon &icon) |
1089 | +{ |
1090 | + ITEM_DEBUG_MSG << "(icon=" << icon.name() << ")"; |
1091 | + |
1092 | + if (!icon.isNull() || (!m_icon.isNull() && icon.isNull())) { |
1093 | + m_icon = icon; |
1094 | + Q_EMIT propertyChanged(); |
1095 | + } |
1096 | +} |
1097 | + |
1098 | +void UbuntuPlatformMenuItem::setVisible(bool isVisible) |
1099 | +{ |
1100 | + ITEM_DEBUG_MSG << "(visible=" << isVisible << ")"; |
1101 | + if (m_visible != isVisible) { |
1102 | + m_visible = isVisible; |
1103 | + Q_EMIT propertyChanged(); |
1104 | + } |
1105 | +} |
1106 | + |
1107 | +void UbuntuPlatformMenuItem::setIsSeparator(bool isSeparator) |
1108 | +{ |
1109 | + ITEM_DEBUG_MSG << "(separator=" << isSeparator << ")"; |
1110 | + if (m_separator != isSeparator) { |
1111 | + m_separator = isSeparator; |
1112 | + Q_EMIT propertyChanged(); |
1113 | + } |
1114 | +} |
1115 | + |
1116 | +void UbuntuPlatformMenuItem::setFont(const QFont &font) |
1117 | +{ |
1118 | + ITEM_DEBUG_MSG << "(font=" << font << ")"; |
1119 | + Q_UNUSED(font); |
1120 | +} |
1121 | + |
1122 | +void UbuntuPlatformMenuItem::setRole(QPlatformMenuItem::MenuRole role) |
1123 | +{ |
1124 | + ITEM_DEBUG_MSG << "(role=" << role << ")"; |
1125 | + Q_UNUSED(role); |
1126 | +} |
1127 | + |
1128 | +void UbuntuPlatformMenuItem::setCheckable(bool checkable) |
1129 | +{ |
1130 | + ITEM_DEBUG_MSG << "(checkable=" << checkable << ")"; |
1131 | + if (m_checkable != checkable) { |
1132 | + m_checkable = checkable; |
1133 | + Q_EMIT propertyChanged(); |
1134 | + } |
1135 | +} |
1136 | + |
1137 | +void UbuntuPlatformMenuItem::setChecked(bool isChecked) |
1138 | +{ |
1139 | + ITEM_DEBUG_MSG << "(checked=" << isChecked << ")"; |
1140 | + if (m_checked != isChecked) { |
1141 | + m_checked = isChecked; |
1142 | + Q_EMIT checkedChanged(isChecked); |
1143 | + Q_EMIT propertyChanged(); |
1144 | + } |
1145 | +} |
1146 | + |
1147 | +void UbuntuPlatformMenuItem::setShortcut(const QKeySequence &shortcut) |
1148 | +{ |
1149 | + ITEM_DEBUG_MSG << "(shortcut=" << shortcut << ")"; |
1150 | + if (m_shortcut != shortcut) { |
1151 | + m_shortcut = shortcut; |
1152 | + Q_EMIT propertyChanged(); |
1153 | + } |
1154 | +} |
1155 | + |
1156 | +void UbuntuPlatformMenuItem::setEnabled(bool enabled) |
1157 | +{ |
1158 | + ITEM_DEBUG_MSG << "(enabled=" << enabled << ")"; |
1159 | + if (m_enabled != enabled) { |
1160 | + m_enabled = enabled; |
1161 | + Q_EMIT enabledChanged(enabled); |
1162 | + Q_EMIT propertyChanged(); |
1163 | + } |
1164 | +} |
1165 | + |
1166 | +void UbuntuPlatformMenuItem::setIconSize(int size) |
1167 | +{ |
1168 | + ITEM_DEBUG_MSG << "(size=" << size << ")"; |
1169 | + Q_UNUSED(size); |
1170 | +} |
1171 | + |
1172 | +void UbuntuPlatformMenuItem::setMenu(QPlatformMenu *menu) |
1173 | +{ |
1174 | + ITEM_DEBUG_MSG << "(menu=" << menu << ")"; |
1175 | + if (m_menu != menu) { |
1176 | + m_menu = menu; |
1177 | + Q_EMIT propertyChanged(); |
1178 | + } |
1179 | +} |
1180 | + |
1181 | +QPlatformMenu *UbuntuPlatformMenuItem::menu() const |
1182 | +{ |
1183 | + return m_menu; |
1184 | +} |
1185 | + |
1186 | +QDebug UbuntuPlatformMenuItem::operator<<(QDebug stream) |
1187 | +{ |
1188 | + QString properties = "text=\"" + m_text + "\""; |
1189 | + |
1190 | + stream.nospace().noquote() << QString("%1").arg("", logRecusion, QLatin1Char('\t')) |
1191 | + << "UbuntuPlatformMenuItem(this=" << (void*)this << ", " |
1192 | + << (m_separator ? "Separator" : properties) << ")" << endl; |
1193 | + if (m_menu) { |
1194 | + auto myMenu = static_cast<UbuntuPlatformMenu*>(m_menu); |
1195 | + if (myMenu) { |
1196 | + logRecusion++; |
1197 | + stream << myMenu; |
1198 | + logRecusion--; |
1199 | + } |
1200 | + } |
1201 | + return stream; |
1202 | +} |
1203 | |
1204 | === added file 'src/ubuntuappmenu/gmenumodelplatformmenu.h' |
1205 | --- src/ubuntuappmenu/gmenumodelplatformmenu.h 1970-01-01 00:00:00 +0000 |
1206 | +++ src/ubuntuappmenu/gmenumodelplatformmenu.h 2016-12-09 17:04:44 +0000 |
1207 | @@ -0,0 +1,181 @@ |
1208 | +/* |
1209 | + * Copyright (C) 2016 Canonical, Ltd. |
1210 | + * |
1211 | + * This program is free software: you can redistribute it and/or modify it under |
1212 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1213 | + * the Free Software Foundation. |
1214 | + * |
1215 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1216 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1217 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1218 | + * Lesser General Public License for more details. |
1219 | + * |
1220 | + * You should have received a copy of the GNU Lesser General Public License |
1221 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1222 | + */ |
1223 | + |
1224 | +#ifndef EXPORTEDPLATFORMMENUBAR_H |
1225 | +#define EXPORTEDPLATFORMMENUBAR_H |
1226 | + |
1227 | +#include <qpa/qplatformmenu.h> |
1228 | + |
1229 | +// Local |
1230 | +class UbuntuGMenuModelExporter; |
1231 | +class UbuntuMenuRegistrar; |
1232 | +class QWindow; |
1233 | + |
1234 | +class UbuntuPlatformMenuBar : public QPlatformMenuBar |
1235 | +{ |
1236 | + Q_OBJECT |
1237 | +public: |
1238 | + UbuntuPlatformMenuBar(); |
1239 | + ~UbuntuPlatformMenuBar(); |
1240 | + |
1241 | + QString exportedPath() const; |
1242 | + |
1243 | + virtual void insertMenu(QPlatformMenu *menu, QPlatformMenu* before) override; |
1244 | + virtual void removeMenu(QPlatformMenu *menu) override; |
1245 | + virtual void syncMenu(QPlatformMenu *menu) override; |
1246 | + virtual void handleReparent(QWindow *newParentWindow) override; |
1247 | + virtual QPlatformMenu *menuForTag(quintptr tag) const override; |
1248 | + |
1249 | + const QList<QPlatformMenu*> menus() const; |
1250 | + |
1251 | + QDebug operator<<(QDebug stream); |
1252 | + |
1253 | +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) |
1254 | + virtual QPlatformMenu *createMenu() const override; |
1255 | +#endif |
1256 | + |
1257 | +Q_SIGNALS: |
1258 | + void menuInserted(QPlatformMenu *menu); |
1259 | + void menuRemoved(QPlatformMenu *menu); |
1260 | + |
1261 | + void structureChanged(); |
1262 | + void ready(); |
1263 | + |
1264 | +private: |
1265 | + void setReady(bool); |
1266 | + |
1267 | + QList<QPlatformMenu*> m_menus; |
1268 | + QScopedPointer<UbuntuGMenuModelExporter> m_exporter; |
1269 | + QScopedPointer<UbuntuMenuRegistrar> m_registrar; |
1270 | + bool m_ready; |
1271 | +}; |
1272 | + |
1273 | +#define MENU_PROPERTY(class, name, type, defaultValue) \ |
1274 | + static type get_##name(const class *menuItem) { return menuItem->m_##name; } \ |
1275 | + type m_##name = defaultValue; |
1276 | + |
1277 | +class Q_DECL_EXPORT UbuntuPlatformMenu : public QPlatformMenu |
1278 | +{ |
1279 | + Q_OBJECT |
1280 | +public: |
1281 | + UbuntuPlatformMenu(); |
1282 | + ~UbuntuPlatformMenu(); |
1283 | + |
1284 | + virtual void insertMenuItem(QPlatformMenuItem *menuItem, QPlatformMenuItem *before) override; |
1285 | + virtual void removeMenuItem(QPlatformMenuItem *menuItem) override; |
1286 | + virtual void syncMenuItem(QPlatformMenuItem *menuItem) override; |
1287 | + virtual void syncSeparatorsCollapsible(bool enable) override; |
1288 | + |
1289 | + virtual void setTag(quintptr tag) override; |
1290 | + virtual quintptr tag() const override; |
1291 | + |
1292 | + virtual void setText(const QString &text) override; |
1293 | + virtual void setIcon(const QIcon &icon) override; |
1294 | + virtual void setEnabled(bool isEnabled) override; |
1295 | + virtual void setVisible(bool isVisible) override; |
1296 | + virtual void setMinimumWidth(int width) override; |
1297 | + virtual void setFont(const QFont &font) override; |
1298 | + |
1299 | + virtual void showPopup(const QWindow *parentWindow, const QRect &targetRect, const QPlatformMenuItem *item); |
1300 | + |
1301 | + virtual void dismiss(); // Closes this and all its related menu popups |
1302 | + |
1303 | + virtual QPlatformMenuItem *menuItemAt(int position) const override; |
1304 | + virtual QPlatformMenuItem *menuItemForTag(quintptr tag) const override; |
1305 | + |
1306 | + virtual QPlatformMenuItem *createMenuItem() const override; |
1307 | +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) |
1308 | + virtual QPlatformMenu *createSubMenu() const override; |
1309 | +#endif |
1310 | + |
1311 | + int id() const; |
1312 | + |
1313 | + const QList<QPlatformMenuItem*> menuItems() const; |
1314 | + |
1315 | + QDebug operator<<(QDebug stream); |
1316 | + |
1317 | +Q_SIGNALS: |
1318 | + void menuItemInserted(QPlatformMenuItem *menuItem); |
1319 | + void menuItemRemoved(QPlatformMenuItem *menuItem); |
1320 | + void structureChanged(); |
1321 | + void propertyChanged(); |
1322 | + |
1323 | +private: |
1324 | + MENU_PROPERTY(UbuntuPlatformMenu, visible, bool, true) |
1325 | + MENU_PROPERTY(UbuntuPlatformMenu, text, QString, QString()) |
1326 | + MENU_PROPERTY(UbuntuPlatformMenu, enabled, bool, true) |
1327 | + MENU_PROPERTY(UbuntuPlatformMenu, icon, QIcon, QIcon()) |
1328 | + |
1329 | + quintptr m_tag; |
1330 | + QList<QPlatformMenuItem*> m_menuItems; |
1331 | + const QWindow* m_parentWindow; |
1332 | + QScopedPointer<UbuntuGMenuModelExporter> m_exporter; |
1333 | + QScopedPointer<UbuntuMenuRegistrar> m_registrar; |
1334 | + |
1335 | + friend class UbuntuGMenuModelExporter; |
1336 | +}; |
1337 | + |
1338 | + |
1339 | +class Q_DECL_EXPORT UbuntuPlatformMenuItem : public QPlatformMenuItem |
1340 | +{ |
1341 | + Q_OBJECT |
1342 | +public: |
1343 | + UbuntuPlatformMenuItem(); |
1344 | + ~UbuntuPlatformMenuItem(); |
1345 | + |
1346 | + virtual void setTag(quintptr tag) override; |
1347 | + virtual quintptr tag() const override; |
1348 | + |
1349 | + virtual void setText(const QString &text) override; |
1350 | + virtual void setIcon(const QIcon &icon) override; |
1351 | + virtual void setMenu(QPlatformMenu *menu) override; |
1352 | + virtual void setVisible(bool isVisible) override; |
1353 | + virtual void setIsSeparator(bool isSeparator) override; |
1354 | + virtual void setFont(const QFont &font) override; |
1355 | + virtual void setRole(MenuRole role) override; |
1356 | + virtual void setCheckable(bool checkable) override; |
1357 | + virtual void setChecked(bool isChecked) override; |
1358 | + virtual void setShortcut(const QKeySequence& shortcut) override; |
1359 | + virtual void setEnabled(bool enabled) override; |
1360 | + virtual void setIconSize(int size) override; |
1361 | + |
1362 | + QPlatformMenu* menu() const; |
1363 | + |
1364 | + QDebug operator<<(QDebug stream); |
1365 | + |
1366 | +Q_SIGNALS: |
1367 | + void checkedChanged(bool); |
1368 | + void enabledChanged(bool); |
1369 | + void propertyChanged(); |
1370 | + |
1371 | +private: |
1372 | + MENU_PROPERTY(UbuntuPlatformMenuItem, separator, bool, false) |
1373 | + MENU_PROPERTY(UbuntuPlatformMenuItem, visible, bool, true) |
1374 | + MENU_PROPERTY(UbuntuPlatformMenuItem, text, QString, QString()) |
1375 | + MENU_PROPERTY(UbuntuPlatformMenuItem, enabled, bool, true) |
1376 | + MENU_PROPERTY(UbuntuPlatformMenuItem, checkable, bool, false) |
1377 | + MENU_PROPERTY(UbuntuPlatformMenuItem, checked, bool, false) |
1378 | + MENU_PROPERTY(UbuntuPlatformMenuItem, shortcut, QKeySequence, QKeySequence()) |
1379 | + MENU_PROPERTY(UbuntuPlatformMenuItem, icon, QIcon, QIcon()) |
1380 | + MENU_PROPERTY(UbuntuPlatformMenuItem, iconSize, int, 16) |
1381 | + MENU_PROPERTY(UbuntuPlatformMenuItem, menu, QPlatformMenu*, nullptr) |
1382 | + |
1383 | + |
1384 | + quintptr m_tag; |
1385 | + friend class UbuntuGMenuModelExporter; |
1386 | +}; |
1387 | + |
1388 | +#endif // EXPORTEDPLATFORMMENUBAR_H |
1389 | |
1390 | === added file 'src/ubuntuappmenu/logging.h' |
1391 | --- src/ubuntuappmenu/logging.h 1970-01-01 00:00:00 +0000 |
1392 | +++ src/ubuntuappmenu/logging.h 2016-12-09 17:04:44 +0000 |
1393 | @@ -0,0 +1,27 @@ |
1394 | +/* |
1395 | + * Copyright (C) 2016 Canonical, Ltd. |
1396 | + * |
1397 | + * This program is free software: you can redistribute it and/or modify it under |
1398 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1399 | + * the Free Software Foundation. |
1400 | + * |
1401 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1402 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1403 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1404 | + * Lesser General Public License for more details. |
1405 | + * |
1406 | + * You should have received a copy of the GNU Lesser General Public License |
1407 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1408 | + */ |
1409 | + |
1410 | +#ifndef QUBUNTUTHEMELOGGING_H |
1411 | +#define QUBUNTUTHEMELOGGING_H |
1412 | + |
1413 | +#include <QLoggingCategory> |
1414 | + |
1415 | +#define ASSERT(cond) ((!(cond)) ? qt_assert(#cond,__FILE__,__LINE__) : qt_noop()) |
1416 | + |
1417 | +Q_DECLARE_LOGGING_CATEGORY(ubuntuappmenu) |
1418 | +Q_DECLARE_LOGGING_CATEGORY(ubuntuappmenuRegistrar) |
1419 | + |
1420 | +#endif // QUBUNTUTHEMELOGGING_H |
1421 | |
1422 | === added file 'src/ubuntuappmenu/menuregistrar.cpp' |
1423 | --- src/ubuntuappmenu/menuregistrar.cpp 1970-01-01 00:00:00 +0000 |
1424 | +++ src/ubuntuappmenu/menuregistrar.cpp 2016-12-09 17:04:44 +0000 |
1425 | @@ -0,0 +1,137 @@ |
1426 | +/* |
1427 | + * Copyright (C) 2016 Canonical, Ltd. |
1428 | + * |
1429 | + * This program is free software: you can redistribute it and/or modify it under |
1430 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1431 | + * the Free Software Foundation. |
1432 | + * |
1433 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1434 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1435 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1436 | + * Lesser General Public License for more details. |
1437 | + * |
1438 | + * You should have received a copy of the GNU Lesser General Public License |
1439 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1440 | + */ |
1441 | + |
1442 | +#include "menuregistrar.h" |
1443 | +#include "registry.h" |
1444 | +#include "logging.h" |
1445 | + |
1446 | +#include <QDebug> |
1447 | +#include <QDBusObjectPath> |
1448 | +#include <QGuiApplication> |
1449 | +#include <qpa/qplatformnativeinterface.h> |
1450 | +#include <qpa/qplatformwindow.h> |
1451 | + |
1452 | +namespace { |
1453 | + |
1454 | +bool isMirClient() { |
1455 | + return qGuiApp->platformName() == "ubuntumirclient"; |
1456 | +} |
1457 | + |
1458 | +} |
1459 | + |
1460 | +UbuntuMenuRegistrar::UbuntuMenuRegistrar() |
1461 | + : m_connection(nullptr) |
1462 | + , m_registeredProcessId(~0) |
1463 | +{ |
1464 | + GError *error = NULL; |
1465 | + m_connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error); |
1466 | + if (!m_connection) { |
1467 | + qCWarning(ubuntuappmenuRegistrar, "Failed to retreive session bus - %s", error ? error->message : "unknown error"); |
1468 | + g_error_free (error); |
1469 | + return; |
1470 | + } |
1471 | + m_service = g_dbus_connection_get_unique_name(m_connection); |
1472 | + connect(UbuntuMenuRegistry::instance(), &UbuntuMenuRegistry::serviceChanged, this, &UbuntuMenuRegistrar::onRegistrarServiceChanged); |
1473 | + |
1474 | + if (isMirClient()) { |
1475 | + auto nativeInterface = qGuiApp->platformNativeInterface(); |
1476 | + connect(nativeInterface, &QPlatformNativeInterface::windowPropertyChanged, this, [this](QPlatformWindow* window, const QString &property) { |
1477 | + if (property != QStringLiteral("persistentSurfaceId")) { |
1478 | + return; |
1479 | + } |
1480 | + if (window->window() == m_window) { |
1481 | + registerMenuForWindow(m_window, m_path); |
1482 | + } |
1483 | + }); |
1484 | + } |
1485 | +} |
1486 | + |
1487 | +UbuntuMenuRegistrar::~UbuntuMenuRegistrar() |
1488 | +{ |
1489 | + if (m_connection) { |
1490 | + g_object_unref(m_connection); |
1491 | + } |
1492 | + unregisterMenu(); |
1493 | +} |
1494 | + |
1495 | +void UbuntuMenuRegistrar::registerMenuForWindow(QWindow* window, const QDBusObjectPath& path) |
1496 | +{ |
1497 | + unregisterMenu(); |
1498 | + |
1499 | + m_window = window; |
1500 | + m_path = path; |
1501 | + |
1502 | + registerMenu(); |
1503 | +} |
1504 | + |
1505 | +void UbuntuMenuRegistrar::registerMenu() |
1506 | +{ |
1507 | + if (UbuntuMenuRegistry::instance()->isConnected() && m_window) { |
1508 | + if (isMirClient()) { |
1509 | + registerSurfaceMenu(); |
1510 | + } else { |
1511 | + registerApplicationMenu(); |
1512 | + } |
1513 | + } |
1514 | +} |
1515 | + |
1516 | +void UbuntuMenuRegistrar::unregisterMenu() |
1517 | +{ |
1518 | + if (!m_registeredSurfaceId.isEmpty()) { |
1519 | + unregisterSurfaceMenu(); |
1520 | + } else if (m_registeredProcessId != ~0) { |
1521 | + unregisterApplicationMenu(); |
1522 | + } |
1523 | +} |
1524 | + |
1525 | +void UbuntuMenuRegistrar::registerSurfaceMenu() |
1526 | +{ |
1527 | + auto nativeInterface = qGuiApp->platformNativeInterface(); |
1528 | + QByteArray persistentSurfaceId = nativeInterface->windowProperty(m_window->handle(), "persistentSurfaceId", QByteArray()).toByteArray(); |
1529 | + if (persistentSurfaceId.isEmpty()) return; |
1530 | + |
1531 | + UbuntuMenuRegistry::instance()->registerSurfaceMenu(persistentSurfaceId, m_path, m_service); |
1532 | + m_registeredSurfaceId = persistentSurfaceId; |
1533 | +} |
1534 | + |
1535 | +void UbuntuMenuRegistrar::unregisterSurfaceMenu() |
1536 | +{ |
1537 | + if (UbuntuMenuRegistry::instance()->isConnected()) { |
1538 | + UbuntuMenuRegistry::instance()->unregisterSurfaceMenu(m_registeredSurfaceId, m_path); |
1539 | + } |
1540 | + m_registeredSurfaceId.clear(); |
1541 | +} |
1542 | + |
1543 | +void UbuntuMenuRegistrar::registerApplicationMenu() |
1544 | +{ |
1545 | + pid_t pid = getpid(); |
1546 | + UbuntuMenuRegistry::instance()->registerApplicationMenu(pid, m_path, m_service); |
1547 | + m_registeredProcessId = pid; |
1548 | +} |
1549 | + |
1550 | +void UbuntuMenuRegistrar::unregisterApplicationMenu() |
1551 | +{ |
1552 | + if (UbuntuMenuRegistry::instance()->isConnected()) { |
1553 | + UbuntuMenuRegistry::instance()->unregisterApplicationMenu(m_registeredProcessId, m_path); |
1554 | + } |
1555 | + m_registeredProcessId = ~0; |
1556 | +} |
1557 | + |
1558 | +void UbuntuMenuRegistrar::onRegistrarServiceChanged() |
1559 | +{ |
1560 | + unregisterMenu(); |
1561 | + registerMenu(); |
1562 | +} |
1563 | |
1564 | === added file 'src/ubuntuappmenu/menuregistrar.h' |
1565 | --- src/ubuntuappmenu/menuregistrar.h 1970-01-01 00:00:00 +0000 |
1566 | +++ src/ubuntuappmenu/menuregistrar.h 2016-12-09 17:04:44 +0000 |
1567 | @@ -0,0 +1,59 @@ |
1568 | +/* |
1569 | + * Copyright (C) 2016 Canonical, Ltd. |
1570 | + * |
1571 | + * This program is free software: you can redistribute it and/or modify it under |
1572 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1573 | + * the Free Software Foundation. |
1574 | + * |
1575 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1576 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1577 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1578 | + * Lesser General Public License for more details. |
1579 | + * |
1580 | + * You should have received a copy of the GNU Lesser General Public License |
1581 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1582 | + */ |
1583 | + |
1584 | +#ifndef MENUREGISTRAR_H |
1585 | +#define MENUREGISTRAR_H |
1586 | + |
1587 | +#include <QObject> |
1588 | +#include <QWindow> |
1589 | +#include <QPointer> |
1590 | +#include <QDBusObjectPath> |
1591 | + |
1592 | +#include <gio/gio.h> |
1593 | + |
1594 | +class UbuntuMenuRegistrar : public QObject |
1595 | +{ |
1596 | + Q_OBJECT |
1597 | +public: |
1598 | + UbuntuMenuRegistrar(); |
1599 | + ~UbuntuMenuRegistrar(); |
1600 | + |
1601 | + void registerMenuForWindow(QWindow* window, const QDBusObjectPath& path); |
1602 | + void unregisterMenu(); |
1603 | + |
1604 | +private Q_SLOTS: |
1605 | + void registerSurfaceMenu(); |
1606 | + void onRegistrarServiceChanged(); |
1607 | + |
1608 | +private: |
1609 | + void registerMenu(); |
1610 | + |
1611 | + void registerApplicationMenu(); |
1612 | + void unregisterApplicationMenu(); |
1613 | + |
1614 | + void unregisterSurfaceMenu(); |
1615 | + |
1616 | + GDBusConnection *m_connection; |
1617 | + QString m_service; |
1618 | + QDBusObjectPath m_path; |
1619 | + QPointer<QWindow> m_window; |
1620 | + QString m_registeredSurfaceId; |
1621 | + pid_t m_registeredProcessId; |
1622 | +}; |
1623 | + |
1624 | + |
1625 | +#endif // MENUREGISTRAR_H |
1626 | + |
1627 | |
1628 | === added file 'src/ubuntuappmenu/registry.cpp' |
1629 | --- src/ubuntuappmenu/registry.cpp 1970-01-01 00:00:00 +0000 |
1630 | +++ src/ubuntuappmenu/registry.cpp 2016-12-09 17:04:44 +0000 |
1631 | @@ -0,0 +1,97 @@ |
1632 | +/* |
1633 | + * Copyright (C) 2016 Canonical, Ltd. |
1634 | + * |
1635 | + * This program is free software: you can redistribute it and/or modify it under |
1636 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1637 | + * the Free Software Foundation. |
1638 | + * |
1639 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1640 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1641 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1642 | + * Lesser General Public License for more details. |
1643 | + * |
1644 | + * You should have received a copy of the GNU Lesser General Public License |
1645 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1646 | + */ |
1647 | + |
1648 | +#include "registry.h" |
1649 | +#include "logging.h" |
1650 | +#include "menuregistrar_interface.h" |
1651 | + |
1652 | +#include <QDBusObjectPath> |
1653 | +#include <QDBusServiceWatcher> |
1654 | + |
1655 | +Q_LOGGING_CATEGORY(ubuntuappmenuRegistrar, "ubuntuappmenu.registrar", QtWarningMsg) |
1656 | + |
1657 | +#define REGISTRAR_SERVICE "com.ubuntu.MenuRegistrar" |
1658 | +#define REGISTRY_OBJECT_PATH "/com/ubuntu/MenuRegistrar" |
1659 | + |
1660 | +UbuntuMenuRegistry *UbuntuMenuRegistry::instance() |
1661 | +{ |
1662 | + static UbuntuMenuRegistry* registry(new UbuntuMenuRegistry()); |
1663 | + return registry; |
1664 | +} |
1665 | + |
1666 | +UbuntuMenuRegistry::UbuntuMenuRegistry(QObject* parent) |
1667 | + : QObject(parent) |
1668 | + , m_serviceWatcher(new QDBusServiceWatcher(REGISTRAR_SERVICE, QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange, this)) |
1669 | + , m_interface(new ComUbuntuMenuRegistrarInterface(REGISTRAR_SERVICE, REGISTRY_OBJECT_PATH, QDBusConnection::sessionBus(), this)) |
1670 | + , m_connected(m_interface->isValid()) |
1671 | +{ |
1672 | + connect(m_serviceWatcher.data(), &QDBusServiceWatcher::serviceOwnerChanged, this, &UbuntuMenuRegistry::serviceOwnerChanged); |
1673 | +} |
1674 | + |
1675 | +UbuntuMenuRegistry::~UbuntuMenuRegistry() |
1676 | +{ |
1677 | +} |
1678 | + |
1679 | +void UbuntuMenuRegistry::registerApplicationMenu(pid_t pid, QDBusObjectPath menuObjectPath, const QString &service) |
1680 | +{ |
1681 | + qCDebug(ubuntuappmenuRegistrar, "UbuntuMenuRegistry::registerMenu(pid=%d, menuObjectPath=%s, service=%s)", |
1682 | + pid, |
1683 | + qPrintable(menuObjectPath.path()), |
1684 | + qPrintable(service)); |
1685 | + |
1686 | + m_interface->RegisterAppMenu(pid, menuObjectPath, menuObjectPath, service); |
1687 | +} |
1688 | + |
1689 | +void UbuntuMenuRegistry::unregisterApplicationMenu(pid_t pid, QDBusObjectPath menuObjectPath) |
1690 | +{ |
1691 | + qCDebug(ubuntuappmenuRegistrar, "UbuntuMenuRegistry::unregisterSurfaceMenu(pid=%d, menuObjectPath=%s)", |
1692 | + pid, |
1693 | + qPrintable(menuObjectPath.path())); |
1694 | + |
1695 | + m_interface->UnregisterAppMenu(pid, menuObjectPath); |
1696 | +} |
1697 | + |
1698 | +void UbuntuMenuRegistry::registerSurfaceMenu(const QString &surfaceId, QDBusObjectPath menuObjectPath, const QString &service) |
1699 | +{ |
1700 | + qCDebug(ubuntuappmenuRegistrar, "UbuntuMenuRegistry::registerMenu(surfaceId=%s, menuObjectPath=%s, service=%s)", |
1701 | + qPrintable(surfaceId), |
1702 | + qPrintable(menuObjectPath.path()), |
1703 | + qPrintable(service)); |
1704 | + |
1705 | + m_interface->RegisterSurfaceMenu(surfaceId, menuObjectPath, menuObjectPath, service); |
1706 | +} |
1707 | + |
1708 | +void UbuntuMenuRegistry::unregisterSurfaceMenu(const QString &surfaceId, QDBusObjectPath menuObjectPath) |
1709 | +{ |
1710 | + qCDebug(ubuntuappmenuRegistrar, "UbuntuMenuRegistry::unregisterSurfaceMenu(surfaceId=%s, menuObjectPath=%s)", |
1711 | + qPrintable(surfaceId), |
1712 | + qPrintable(menuObjectPath.path())); |
1713 | + |
1714 | + m_interface->UnregisterSurfaceMenu(surfaceId, menuObjectPath); |
1715 | +} |
1716 | + |
1717 | + |
1718 | +void UbuntuMenuRegistry::serviceOwnerChanged(const QString &serviceName, const QString& oldOwner, const QString &newOwner) |
1719 | +{ |
1720 | + qCDebug(ubuntuappmenuRegistrar, "UbuntuMenuRegistry::serviceOwnerChanged(newOwner=%s)", qPrintable(newOwner)); |
1721 | + |
1722 | + if (serviceName != REGISTRAR_SERVICE) return; |
1723 | + |
1724 | + if (oldOwner != newOwner) { |
1725 | + m_connected = !newOwner.isEmpty(); |
1726 | + Q_EMIT serviceChanged(); |
1727 | + } |
1728 | +} |
1729 | |
1730 | === added file 'src/ubuntuappmenu/registry.h' |
1731 | --- src/ubuntuappmenu/registry.h 1970-01-01 00:00:00 +0000 |
1732 | +++ src/ubuntuappmenu/registry.h 2016-12-09 17:04:44 +0000 |
1733 | @@ -0,0 +1,56 @@ |
1734 | +/* |
1735 | + * Copyright (C) 2016 Canonical, Ltd. |
1736 | + * |
1737 | + * This program is free software: you can redistribute it and/or modify it under |
1738 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1739 | + * the Free Software Foundation. |
1740 | + * |
1741 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1742 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1743 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1744 | + * Lesser General Public License for more details. |
1745 | + * |
1746 | + * You should have received a copy of the GNU Lesser General Public License |
1747 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1748 | + */ |
1749 | + |
1750 | +#ifndef UBUNTU_MENU_REGISTRY_H |
1751 | +#define UBUNTU_MENU_REGISTRY_H |
1752 | + |
1753 | +#include <QObject> |
1754 | +#include <QScopedPointer> |
1755 | + |
1756 | +class ComUbuntuMenuRegistrarInterface; |
1757 | +class QDBusObjectPath; |
1758 | +class QDBusServiceWatcher; |
1759 | + |
1760 | +class UbuntuMenuRegistry : public QObject |
1761 | +{ |
1762 | + Q_OBJECT |
1763 | +public: |
1764 | + UbuntuMenuRegistry(QObject* parent = nullptr); |
1765 | + virtual ~UbuntuMenuRegistry(); |
1766 | + |
1767 | + static UbuntuMenuRegistry *instance(); |
1768 | + |
1769 | + void registerApplicationMenu(pid_t pid, QDBusObjectPath menuObjectPath, const QString &service); |
1770 | + void unregisterApplicationMenu(pid_t pid, QDBusObjectPath menuObjectPath); |
1771 | + |
1772 | + void registerSurfaceMenu(const QString &surfaceId, QDBusObjectPath menuObjectPath, const QString &service); |
1773 | + void unregisterSurfaceMenu(const QString &surfaceId, QDBusObjectPath menuObjectPath); |
1774 | + |
1775 | + bool isConnected() const { return m_connected; } |
1776 | + |
1777 | +Q_SIGNALS: |
1778 | + void serviceChanged(); |
1779 | + |
1780 | +private Q_SLOTS: |
1781 | + void serviceOwnerChanged(const QString &serviceName, const QString& oldOwner, const QString &newOwner); |
1782 | + |
1783 | +private: |
1784 | + QScopedPointer<QDBusServiceWatcher> m_serviceWatcher; |
1785 | + QScopedPointer<ComUbuntuMenuRegistrarInterface> m_interface; |
1786 | + bool m_connected; |
1787 | +}; |
1788 | + |
1789 | +#endif // UBUNTU_MENU_REGISTRY_H |
1790 | |
1791 | === renamed file 'src/ubuntumirclient/theme.cpp' => 'src/ubuntuappmenu/theme.cpp' |
1792 | --- src/ubuntumirclient/theme.cpp 2014-06-18 23:10:00 +0000 |
1793 | +++ src/ubuntuappmenu/theme.cpp 2016-12-09 17:04:44 +0000 |
1794 | @@ -1,5 +1,5 @@ |
1795 | /* |
1796 | - * Copyright (C) 2014 Canonical, Ltd. |
1797 | + * Copyright (C) 2016 Canonical, Ltd. |
1798 | * |
1799 | * This program is free software: you can redistribute it and/or modify it under |
1800 | * the terms of the GNU Lesser General Public License version 3, as published by |
1801 | @@ -15,20 +15,35 @@ |
1802 | */ |
1803 | |
1804 | #include "theme.h" |
1805 | +#include "gmenumodelplatformmenu.h" |
1806 | +#include "logging.h" |
1807 | |
1808 | #include <QtCore/QVariant> |
1809 | - |
1810 | -const char *UbuntuTheme::name = "ubuntu"; |
1811 | - |
1812 | -UbuntuTheme::UbuntuTheme() |
1813 | -{ |
1814 | -} |
1815 | - |
1816 | -UbuntuTheme::~UbuntuTheme() |
1817 | -{ |
1818 | -} |
1819 | - |
1820 | -QVariant UbuntuTheme::themeHint(ThemeHint hint) const |
1821 | +#include <QDebug> |
1822 | + |
1823 | +Q_LOGGING_CATEGORY(ubuntuappmenu, "ubuntuappmenu", QtWarningMsg) |
1824 | +const char *UbuntuAppMenuTheme::name = "ubuntuappmenu"; |
1825 | + |
1826 | +namespace { |
1827 | + |
1828 | +bool useLocalMenu() { |
1829 | + QByteArray menuProxy = qgetenv("UBUNTU_MENUPROXY"); |
1830 | + bool menuProxyIsZero = !menuProxy.isEmpty() && menuProxy.at(0) == '0'; |
1831 | + return menuProxyIsZero; |
1832 | +} |
1833 | + |
1834 | +} |
1835 | + |
1836 | +UbuntuAppMenuTheme::UbuntuAppMenuTheme() |
1837 | +{ |
1838 | + qCDebug(ubuntuappmenu, "UbuntuAppMenuTheme::UbuntuAppMenuTheme() - useLocalMenu=%s", useLocalMenu() ? "true" : "false"); |
1839 | +} |
1840 | + |
1841 | +UbuntuAppMenuTheme::~UbuntuAppMenuTheme() |
1842 | +{ |
1843 | +} |
1844 | + |
1845 | +QVariant UbuntuAppMenuTheme::themeHint(ThemeHint hint) const |
1846 | { |
1847 | if (hint == QPlatformTheme::SystemIconThemeName) { |
1848 | QByteArray iconTheme = qgetenv("QTUBUNTU_ICON_THEME"); |
1849 | @@ -41,3 +56,21 @@ |
1850 | return QGenericUnixTheme::themeHint(hint); |
1851 | } |
1852 | } |
1853 | + |
1854 | +QPlatformMenuItem *UbuntuAppMenuTheme::createPlatformMenuItem() const |
1855 | +{ |
1856 | + if (useLocalMenu()) return QGenericUnixTheme::createPlatformMenuItem(); |
1857 | + return new UbuntuPlatformMenuItem(); |
1858 | +} |
1859 | + |
1860 | +QPlatformMenu *UbuntuAppMenuTheme::createPlatformMenu() const |
1861 | +{ |
1862 | + if (useLocalMenu()) return QGenericUnixTheme::createPlatformMenu(); |
1863 | + return new UbuntuPlatformMenu(); |
1864 | +} |
1865 | + |
1866 | +QPlatformMenuBar *UbuntuAppMenuTheme::createPlatformMenuBar() const |
1867 | +{ |
1868 | + if (useLocalMenu()) return QGenericUnixTheme::createPlatformMenuBar(); |
1869 | + return new UbuntuPlatformMenuBar(); |
1870 | +} |
1871 | |
1872 | === renamed file 'src/ubuntumirclient/theme.h' => 'src/ubuntuappmenu/theme.h' |
1873 | --- src/ubuntumirclient/theme.h 2014-06-18 23:10:00 +0000 |
1874 | +++ src/ubuntuappmenu/theme.h 2016-12-09 17:04:44 +0000 |
1875 | @@ -1,5 +1,5 @@ |
1876 | /* |
1877 | - * Copyright (C) 2014 Canonical, Ltd. |
1878 | + * Copyright (C) 2016 Canonical, Ltd. |
1879 | * |
1880 | * This program is free software: you can redistribute it and/or modify it under |
1881 | * the terms of the GNU Lesser General Public License version 3, as published by |
1882 | @@ -19,15 +19,20 @@ |
1883 | |
1884 | #include <QtPlatformSupport/private/qgenericunixthemes_p.h> |
1885 | |
1886 | -class UbuntuTheme : public QGenericUnixTheme |
1887 | +class UbuntuAppMenuTheme : public QGenericUnixTheme |
1888 | { |
1889 | public: |
1890 | static const char* name; |
1891 | - UbuntuTheme(); |
1892 | - virtual ~UbuntuTheme(); |
1893 | + UbuntuAppMenuTheme(); |
1894 | + virtual ~UbuntuAppMenuTheme(); |
1895 | |
1896 | // From QPlatformTheme |
1897 | QVariant themeHint(ThemeHint hint) const override; |
1898 | + |
1899 | + // For the menus |
1900 | + virtual QPlatformMenuItem* createPlatformMenuItem() const override; |
1901 | + virtual QPlatformMenu* createPlatformMenu() const override; |
1902 | + virtual QPlatformMenuBar* createPlatformMenuBar() const override; |
1903 | }; |
1904 | |
1905 | #endif // UBUNTU_THEME_H |
1906 | |
1907 | === added file 'src/ubuntuappmenu/themeplugin.cpp' |
1908 | --- src/ubuntuappmenu/themeplugin.cpp 1970-01-01 00:00:00 +0000 |
1909 | +++ src/ubuntuappmenu/themeplugin.cpp 2016-12-09 17:04:44 +0000 |
1910 | @@ -0,0 +1,36 @@ |
1911 | +/* |
1912 | + * Copyright (C) 2016 Canonical, Ltd. |
1913 | + * |
1914 | + * This program is free software: you can redistribute it and/or modify it under |
1915 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1916 | + * the Free Software Foundation. |
1917 | + * |
1918 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1919 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1920 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1921 | + * Lesser General Public License for more details. |
1922 | + * |
1923 | + * You should have received a copy of the GNU Lesser General Public License |
1924 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1925 | + */ |
1926 | + |
1927 | +#include "themeplugin.h" |
1928 | +#include "theme.h" |
1929 | + |
1930 | +#include <QDebug> |
1931 | + |
1932 | +/////////////////////////////////////////////////////////// |
1933 | + |
1934 | +UbuntuAppMenuThemePlugin::UbuntuAppMenuThemePlugin(QObject *parent) |
1935 | + : QPlatformThemePlugin(parent) |
1936 | +{ |
1937 | +} |
1938 | + |
1939 | +QPlatformTheme * |
1940 | +UbuntuAppMenuThemePlugin::create(const QString &key, const QStringList&) |
1941 | +{ |
1942 | + if (key.compare(QLatin1String(UbuntuAppMenuTheme::name), Qt::CaseInsensitive)) |
1943 | + return 0; |
1944 | + |
1945 | + return new UbuntuAppMenuTheme(); |
1946 | +} |
1947 | |
1948 | === added file 'src/ubuntuappmenu/themeplugin.h' |
1949 | --- src/ubuntuappmenu/themeplugin.h 1970-01-01 00:00:00 +0000 |
1950 | +++ src/ubuntuappmenu/themeplugin.h 2016-12-09 17:04:44 +0000 |
1951 | @@ -0,0 +1,34 @@ |
1952 | +/* |
1953 | + * Copyright (C) 2016 Canonical, Ltd. |
1954 | + * |
1955 | + * This program is free software: you can redistribute it and/or modify it under |
1956 | + * the terms of the GNU Lesser General Public License version 3, as published by |
1957 | + * the Free Software Foundation. |
1958 | + * |
1959 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
1960 | + * ANY WARRANTY; without even the implied warranties of MERCHANTABILITY, |
1961 | + * SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
1962 | + * Lesser General Public License for more details. |
1963 | + * |
1964 | + * You should have received a copy of the GNU Lesser General Public License |
1965 | + * along with this program. If not, see <http://www.gnu.org/licenses/>. |
1966 | + */ |
1967 | + |
1968 | +#ifndef UBUNTUTHEMEPLUGIN_H |
1969 | +#define UBUNTUTHEMEPLUGIN_H |
1970 | + |
1971 | +#include <qpa/qplatformthemeplugin.h> |
1972 | + |
1973 | +class UbuntuAppMenuThemePlugin : public QPlatformThemePlugin |
1974 | +{ |
1975 | + Q_OBJECT |
1976 | + Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QPA.QPlatformThemeFactoryInterface.5.1" FILE "ubuntuappmenu.json") |
1977 | +public: |
1978 | + UbuntuAppMenuThemePlugin(QObject *parent = 0); |
1979 | + |
1980 | + virtual QPlatformTheme *create(const QString &key, const QStringList ¶mList); |
1981 | + |
1982 | + static const char *name; |
1983 | +}; |
1984 | + |
1985 | +#endif |
1986 | |
1987 | === added file 'src/ubuntuappmenu/ubuntuappmenu.json' |
1988 | --- src/ubuntuappmenu/ubuntuappmenu.json 1970-01-01 00:00:00 +0000 |
1989 | +++ src/ubuntuappmenu/ubuntuappmenu.json 2016-12-09 17:04:44 +0000 |
1990 | @@ -0,0 +1,3 @@ |
1991 | +{ |
1992 | + "Keys": [ "ubuntuappmenu" ] |
1993 | +} |
1994 | |
1995 | === added file 'src/ubuntuappmenu/ubuntuappmenu.pro' |
1996 | --- src/ubuntuappmenu/ubuntuappmenu.pro 1970-01-01 00:00:00 +0000 |
1997 | +++ src/ubuntuappmenu/ubuntuappmenu.pro 2016-12-09 17:04:44 +0000 |
1998 | @@ -0,0 +1,41 @@ |
1999 | +TARGET = ubuntuappmenu |
2000 | +TEMPLATE = lib |
2001 | + |
2002 | +QT -= gui |
2003 | +QT += core-private platformsupport-private dbus |
2004 | + |
2005 | +CONFIG += plugin no_keywords |
2006 | + |
2007 | +# CONFIG += c++11 # only enables C++0x |
2008 | +QMAKE_CXXFLAGS += -fvisibility=hidden -fvisibility-inlines-hidden -std=c++11 -Werror -Wall |
2009 | +QMAKE_LFLAGS += -std=c++11 -Wl,-no-undefined |
2010 | + |
2011 | +CONFIG += link_pkgconfig |
2012 | +PKGCONFIG += gio-2.0 |
2013 | + |
2014 | +DBUS_INTERFACES += com.ubuntu.MenuRegistrar.xml |
2015 | + |
2016 | +HEADERS += \ |
2017 | + theme.h \ |
2018 | + gmenumodelexporter.h \ |
2019 | + gmenumodelplatformmenu.h \ |
2020 | + logging.h \ |
2021 | + menuregistrar.h \ |
2022 | + registry.h \ |
2023 | + themeplugin.h |
2024 | + |
2025 | +SOURCES += \ |
2026 | + theme.cpp \ |
2027 | + gmenumodelexporter.cpp \ |
2028 | + gmenumodelplatformmenu.cpp \ |
2029 | + menuregistrar.cpp \ |
2030 | + registry.cpp \ |
2031 | + themeplugin.cpp |
2032 | + |
2033 | +OTHER_FILES += \ |
2034 | + ubuntuappmenu.json |
2035 | + |
2036 | +# Installation path |
2037 | +target.path += $$[QT_INSTALL_PLUGINS]/platformthemes |
2038 | + |
2039 | +INSTALLS += target |
2040 | |
2041 | === modified file 'src/ubuntumirclient/integration.cpp' |
2042 | --- src/ubuntumirclient/integration.cpp 2016-12-01 22:28:20 +0000 |
2043 | +++ src/ubuntumirclient/integration.cpp 2016-12-09 17:04:44 +0000 |
2044 | @@ -25,7 +25,6 @@ |
2045 | #include "logging.h" |
2046 | #include "nativeinterface.h" |
2047 | #include "screen.h" |
2048 | -#include "theme.h" |
2049 | #include "window.h" |
2050 | |
2051 | // Qt |
2052 | @@ -39,6 +38,7 @@ |
2053 | #include <QtPlatformSupport/private/qgenericunixfontdatabase_p.h> |
2054 | #include <QtPlatformSupport/private/qgenericunixeventdispatcher_p.h> |
2055 | #include <QtPlatformSupport/private/qeglpbuffer_p.h> |
2056 | +#include <QtPlatformSupport/private/qgenericunixthemes_p.h> |
2057 | #include <QtPlatformSupport/private/bridge_p.h> |
2058 | #include <QOpenGLContext> |
2059 | #include <QOffscreenSurface> |
2060 | @@ -48,6 +48,27 @@ |
2061 | #include <ubuntu/application/id.h> |
2062 | #include <ubuntu/application/options.h> |
2063 | |
2064 | + |
2065 | +class UbuntuIconTheme : public QGenericUnixTheme |
2066 | +{ |
2067 | +public: |
2068 | + UbuntuIconTheme() {} |
2069 | + |
2070 | + // From QPlatformTheme |
2071 | + QVariant themeHint(ThemeHint hint) const override { |
2072 | + if (hint == QPlatformTheme::SystemIconThemeName) { |
2073 | + QByteArray iconTheme = qgetenv("QTUBUNTU_ICON_THEME"); |
2074 | + if (iconTheme.isEmpty()) { |
2075 | + return QVariant(QStringLiteral("ubuntu-mobile")); |
2076 | + } else { |
2077 | + return QVariant(QString(iconTheme)); |
2078 | + } |
2079 | + } else { |
2080 | + return QGenericUnixTheme::themeHint(hint); |
2081 | + } |
2082 | + } |
2083 | +}; |
2084 | + |
2085 | static void resumedCallback(const UApplicationOptions */*options*/, void *context) |
2086 | { |
2087 | auto integration = static_cast<UbuntuClientIntegration*>(context); |
2088 | @@ -294,13 +315,13 @@ |
2089 | |
2090 | QStringList UbuntuClientIntegration::themeNames() const |
2091 | { |
2092 | - return QStringList(UbuntuTheme::name); |
2093 | + return QStringList(QStringLiteral("ubuntuappmenu")); |
2094 | } |
2095 | |
2096 | QPlatformTheme* UbuntuClientIntegration::createPlatformTheme(const QString& name) const |
2097 | { |
2098 | Q_UNUSED(name); |
2099 | - return new UbuntuTheme; |
2100 | + return new UbuntuIconTheme; |
2101 | } |
2102 | |
2103 | QVariant UbuntuClientIntegration::styleHint(StyleHint hint) const |
2104 | |
2105 | === modified file 'src/ubuntumirclient/nativeinterface.cpp' |
2106 | --- src/ubuntumirclient/nativeinterface.cpp 2016-06-21 12:17:55 +0000 |
2107 | +++ src/ubuntumirclient/nativeinterface.cpp 2016-12-09 17:04:44 +0000 |
2108 | @@ -162,6 +162,7 @@ |
2109 | if (w) { |
2110 | propertyMap.insert("scale", w->scale()); |
2111 | propertyMap.insert("formFactor", w->formFactor()); |
2112 | + propertyMap.insert("persistentSurfaceId", w->persistentSurfaceId()); |
2113 | } |
2114 | return propertyMap; |
2115 | } |
2116 | @@ -177,6 +178,8 @@ |
2117 | return w->scale(); |
2118 | } else if (name == QStringLiteral("formFactor")) { |
2119 | return w->formFactor(); |
2120 | + } else if (name == QStringLiteral("persistentSurfaceId")) { |
2121 | + return w->persistentSurfaceId(); |
2122 | } else { |
2123 | return QVariant(); |
2124 | } |
2125 | |
2126 | === modified file 'src/ubuntumirclient/plugin.cpp' |
2127 | --- src/ubuntumirclient/plugin.cpp 2016-06-24 12:36:24 +0000 |
2128 | +++ src/ubuntumirclient/plugin.cpp 2016-12-09 17:04:44 +0000 |
2129 | @@ -17,6 +17,7 @@ |
2130 | #include "plugin.h" |
2131 | #include "integration.h" |
2132 | #include "logging.h" |
2133 | +#include "qpa/qplatformwindow.h" |
2134 | |
2135 | Q_LOGGING_CATEGORY(ubuntumirclient, "ubuntumirclient", QtWarningMsg) |
2136 | |
2137 | @@ -24,6 +25,8 @@ |
2138 | const QStringList &/*paramList*/, |
2139 | int &argc, char **argv) |
2140 | { |
2141 | + qRegisterMetaType<QPlatformWindow*>("QPlatformWindow*"); |
2142 | + |
2143 | if (system.toLower() == QLatin1String("ubuntumirclient")) { |
2144 | #ifdef PLATFORM_API_TOUCH |
2145 | setenv("UBUNTU_PLATFORM_API_BACKEND", "touch_mirclient", 1); |
2146 | |
2147 | === modified file 'src/ubuntumirclient/ubuntumirclient.pro' |
2148 | --- src/ubuntumirclient/ubuntumirclient.pro 2016-11-04 13:20:58 +0000 |
2149 | +++ src/ubuntumirclient/ubuntumirclient.pro 2016-12-09 17:04:44 +0000 |
2150 | @@ -28,7 +28,6 @@ |
2151 | plugin.cpp \ |
2152 | screen.cpp \ |
2153 | screenobserver.cpp \ |
2154 | - theme.cpp \ |
2155 | window.cpp \ |
2156 | appstatecontroller.cpp |
2157 | |
2158 | @@ -41,17 +40,19 @@ |
2159 | glcontext.h \ |
2160 | input.h \ |
2161 | integration.h \ |
2162 | - logging.h \ |
2163 | nativeinterface.h \ |
2164 | orientationchangeevent_p.h \ |
2165 | platformservices.h \ |
2166 | plugin.h \ |
2167 | screenobserver.h \ |
2168 | screen.h \ |
2169 | - theme.h \ |
2170 | window.h \ |
2171 | + logging.h \ |
2172 | appstatecontroller.h |
2173 | |
2174 | +OTHER_FILES += \ |
2175 | + ubuntumirclient.json |
2176 | + |
2177 | # Installation path |
2178 | target.path += $$[QT_INSTALL_PLUGINS]/platforms |
2179 | |
2180 | |
2181 | === modified file 'src/ubuntumirclient/window.cpp' |
2182 | --- src/ubuntumirclient/window.cpp 2016-11-23 12:11:14 +0000 |
2183 | +++ src/ubuntumirclient/window.cpp 2016-12-09 17:04:44 +0000 |
2184 | @@ -712,6 +712,11 @@ |
2185 | w, w->screen()->handle(), input, mSurface.get(), qPrintable(window()->title()), roleFor(window())); |
2186 | |
2187 | updatePanelHeightHack(mSurface->state() != mir_surface_state_fullscreen); |
2188 | + |
2189 | + // queue the windowPropertyChanged signal. If it's emitted directly, the platformWindow will not yet be set for the window. |
2190 | + QMetaObject::invokeMethod(mNativeInterface, "windowPropertyChanged", Qt::QueuedConnection, |
2191 | + Q_ARG(QPlatformWindow*, this), |
2192 | + Q_ARG(QString, "persistentSurfaceId")); |
2193 | } |
2194 | |
2195 | UbuntuWindow::~UbuntuWindow() |
2196 | |
2197 | === modified file 'src/ubuntumirclient/window.h' |
2198 | --- src/ubuntumirclient/window.h 2016-11-23 12:11:14 +0000 |
2199 | +++ src/ubuntumirclient/window.h 2016-12-09 17:04:44 +0000 |
2200 | @@ -92,4 +92,6 @@ |
2201 | MirFormFactor mFormFactor; |
2202 | }; |
2203 | |
2204 | +Q_DECLARE_METATYPE(QPlatformWindow*); |
2205 | + |
2206 | #endif // UBUNTU_WINDOW_H |
FAILED: Continuous integration, rev:284 /unity8- jenkins. ubuntu. com/job/ lp-qtubuntu- ci/74/ /unity8- jenkins. ubuntu. com/job/ build/1944/ console /unity8- jenkins. ubuntu. com/job/ build-0- fetch/1970 /unity8- jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= vivid+overlay/ 1904 /unity8- jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= xenial+ overlay/ 1904 /unity8- jenkins. ubuntu. com/job/ build-1- sourcepkg/ release= yakkety/ 1904 /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= vivid+overlay/ 1895/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= xenial+ overlay/ 1895/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=amd64, release= yakkety/ 1895/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= vivid+overlay/ 1895/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= xenial+ overlay/ 1895/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=armhf, release= yakkety/ 1895/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= vivid+overlay/ 1895/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= xenial+ overlay/ 1895/console /unity8- jenkins. ubuntu. com/job/ build-2- binpkg/ arch=i386, release= yakkety/ 1895/console
https:/
Executed test runs:
FAILURE: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
SUCCESS: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
FAILURE: https:/
Click here to trigger a rebuild: /unity8- jenkins. ubuntu. com/job/ lp-qtubuntu- ci/74/rebuild
https:/