1
/* This file is part of the appmenu-qt project
2
Copyright 2011 Canonical
3
Author: Aurelien Gateau <aurelien.gateau@canonical.com>
5
appmenu-qt is free software; you can redistribute it and/or modify it under
6
the terms of the GNU Lesser General Public License (LGPL) as published by
7
the Free Software Foundation; either version 3 of the License.
9
appmenu-qt is distributed in the hope that it will be useful, but WITHOUT
10
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
14
You should have received a copy of the GNU Lesser General Public License
15
along with appmenu-qt. If not, see <http://www.gnu.org/licenses/>.
17
#include "appmenuplatformmenubar.h"
20
#include <dbusmenuexporter.h>
23
#include <QActionEvent>
24
#include <QApplication>
28
#include <QDBusInterface>
29
#include <QDBusObjectPath>
30
#include <QDBusPendingCall>
31
#include <QDBusServiceWatcher>
36
static const char* REGISTRAR_SERVICE = "com.canonical.AppMenu.Registrar";
37
static const char* REGISTRAR_PATH = "/com/canonical/AppMenu/Registrar";
38
static const char* REGISTRAR_IFACE = "com.canonical.AppMenu.Registrar";
40
#define LOG qDebug() << __FUNCTION__
41
#define LOG_VAR(x) qDebug() << __FUNCTION__ << #x ":" << x
42
#define WARN qWarning() << __FUNCTION__
47
MenuBarAdapter(QMenuBar*, const QString&);
49
void addAction(QAction*, QAction* before=0);
50
void removeAction(QAction*);
51
void syncAction(QAction*) {}
53
bool registerWindow();
55
void popupAction(QAction*);
57
uint m_registeredWinId;
59
DBusMenuExporter* m_exporter;
67
AppMenuPlatformMenuBar::~AppMenuPlatformMenuBar()
72
void AppMenuPlatformMenuBar::init(QMenuBar* _menuBar)
74
m_nativeMenuBar = NMB_Auto;
78
static int menuBarId = 1;
79
m_objectPath = QString(QLatin1String("/MenuBar/%1")).arg(menuBarId++);
80
m_registrarWatcher = new QDBusServiceWatcher(
82
QDBusConnection::sessionBus(),
83
QDBusServiceWatcher::WatchForOwnerChange,
85
// m_adapter will be created in handleReparent()
88
connect(m_registrarWatcher, SIGNAL(serviceOwnerChanged(const QString&, const QString&, const QString&)),
89
SLOT(slotMenuBarServiceChanged(const QString&, const QString&, const QString&)));
92
void AppMenuPlatformMenuBar::setVisible(bool visible)
94
m_menuBar->QWidget::setVisible(visible);
97
void AppMenuPlatformMenuBar::actionEvent(QActionEvent* e)
100
if (e->type() == QEvent::ActionAdded) {
101
m_adapter->addAction(e->action(), e->before());
102
} else if (e->type() == QEvent::ActionRemoved) {
103
m_adapter->removeAction(e->action());
104
} else if (e->type() == QEvent::ActionChanged) {
105
m_adapter->syncAction(e->action());
110
void AppMenuPlatformMenuBar::handleReparent(QWidget* oldParent, QWidget* newParent, QWidget* oldWindow, QWidget* newWindow)
114
if (isNativeMenuBar()) {
116
if (oldWindow != newWindow) {
117
m_adapter->registerWindow();
125
bool AppMenuPlatformMenuBar::allowCornerWidgets() const
127
return !isNativeMenuBar();
130
void AppMenuPlatformMenuBar::popupAction(QAction* act)
132
if (act && act->menu()) {
133
m_adapter->popupAction(act);
137
void AppMenuPlatformMenuBar::setNativeMenuBar(bool native)
139
if (m_nativeMenuBar == NMB_DisabledByEnv) {
140
WARN << "native menubar disabled by environment variable";
143
NativeMenuBar newValue = native ? NMB_Enabled : NMB_Disabled;
144
if (m_nativeMenuBar == NMB_Auto || m_nativeMenuBar != newValue) {
145
m_nativeMenuBar = newValue;
146
if (m_nativeMenuBar == NMB_Disabled) {
152
bool AppMenuPlatformMenuBar::isNativeMenuBar() const
154
if (m_nativeMenuBar == NMB_DisabledByEnv) {
157
if (m_nativeMenuBar == NMB_Auto) {
158
return !QApplication::instance()->testAttribute(Qt::AA_DontUseNativeMenuBar);
160
return m_nativeMenuBar == NMB_Enabled;
163
bool AppMenuPlatformMenuBar::shortcutsHandledByNativeMenuBar() const
168
bool AppMenuPlatformMenuBar::menuBarEventFilter(QObject*, QEvent* event)
170
if (event->type() == QEvent::WinIdChange) {
171
if (isNativeMenuBar() && m_adapter) {
172
QMetaObject::invokeMethod(this, "registerWindow", Qt::QueuedConnection);
176
if (event->type() == QEvent::ShortcutOverride) {
177
QKeyEvent *kev = static_cast<QKeyEvent*>(event);
178
if ((kev->key() == Qt::Key_Alt || kev->key() == Qt::Key_Meta)
179
&& kev->modifiers() == Qt::AltModifier) {
186
bool AppMenuPlatformMenuBar::eventFilter(QObject*, QEvent* event)
189
WARN << "called with m_altPressed=false. Should not happen.";
192
switch (event->type()) {
193
case QEvent::KeyRelease:
194
case QEvent::MouseButtonPress:
195
case QEvent::MouseButtonRelease:
196
case QEvent::MouseMove:
197
case QEvent::FocusIn:
198
case QEvent::FocusOut:
199
case QEvent::ActivationChange:
200
setAltPressed(false);
208
void AppMenuPlatformMenuBar::setAltPressed(bool pressed)
210
m_altPressed = pressed;
212
qApp->installEventFilter(this);
214
qApp->removeEventFilter(this);
217
m_adapter->m_exporter->setStatus(m_altPressed ? "notice" : "normal");
221
void AppMenuPlatformMenuBar::slotMenuBarServiceChanged(const QString &/*serviceName*/, const QString &/*oldOwner*/, const QString& newOwner)
223
if (m_nativeMenuBar == NMB_DisabledByEnv || m_nativeMenuBar == NMB_Disabled) {
227
if (newOwner.isEmpty()) {
229
QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, true);
230
m_menuBar->updateGeometry();
231
m_menuBar->setVisible(false);
232
m_menuBar->setVisible(true);
236
// We reach this point if there is a registrar
237
QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, false);
238
m_menuBar->updateGeometry();
239
m_menuBar->setVisible(true);
240
m_menuBar->setVisible(false);
246
void AppMenuPlatformMenuBar::registerWindow()
249
m_adapter->registerWindow();
253
void AppMenuPlatformMenuBar::createMenuBar()
255
static bool firstCall = true;
256
static bool envSaysNo = !qgetenv("QT_X11_NO_NATIVE_MENUBAR").isEmpty();
257
static bool envSaysBoth = qgetenv("APPMENU_DISPLAY_BOTH") == "1";
259
if (!m_menuBar->parentWidget()) {
265
if (!firstCall && !envSaysBoth && QApplication::testAttribute(Qt::AA_DontUseNativeMenuBar)) {
272
m_nativeMenuBar = NMB_DisabledByEnv;
273
QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, true);
278
m_adapter = new MenuBarAdapter(m_menuBar, m_objectPath);
279
if (!m_adapter->registerWindow()) {
285
bool dontUseNativeMenuBar = !m_adapter;
287
// Make the rest of Qt think we do not use the native menubar, so
288
// that space for the menubar widget is correctly allocated
289
dontUseNativeMenuBar = true;
291
QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, dontUseNativeMenuBar);
295
void AppMenuPlatformMenuBar::destroyMenuBar()
301
///////////////////////////////////////////////////////////
302
MenuBarAdapter::MenuBarAdapter(QMenuBar* _menuBar, const QString& _objectPath)
303
: m_registeredWinId(0)
305
, m_rootMenu(new QMenu)
306
, m_menuBar(_menuBar)
307
, m_objectPath(_objectPath)
311
MenuBarAdapter::~MenuBarAdapter()
319
bool MenuBarAdapter::registerWindow()
321
if (!m_menuBar->window()) {
322
WARN << "No parent for this menubar";
326
uint winId = m_menuBar->window()->winId();
327
if (winId == m_registeredWinId) {
331
QDBusInterface host(REGISTRAR_SERVICE, REGISTRAR_PATH, REGISTRAR_IFACE);
332
if (!host.isValid()) {
336
Q_FOREACH(QAction *action, m_menuBar->actions()) {
337
if (!action->isSeparator()) {
338
m_rootMenu->addAction(action);
342
if (m_rootMenu->actions().isEmpty()) {
347
m_exporter = new DBusMenuExporter(m_objectPath, m_rootMenu);
350
m_registeredWinId = winId;
351
QVariant path = QVariant::fromValue<QDBusObjectPath>(QDBusObjectPath(m_objectPath));
352
host.asyncCall(QLatin1String("RegisterWindow"), QVariant(winId), path);
356
void MenuBarAdapter::addAction(QAction* action, QAction* before)
358
if (!action->isSeparator()) {
359
m_rootMenu->insertAction(before, action);
361
if (!m_registeredWinId) {
366
void MenuBarAdapter::removeAction(QAction* action)
368
m_rootMenu->removeAction(action);
371
void MenuBarAdapter::popupAction(QAction* action)
373
m_exporter->activateAction(action);
376
///////////////////////////////////////////////////////////
377
QAbstractPlatformMenuBar* AppMenuPlatformMenuBarFactory::create()
379
return new AppMenuPlatformMenuBar;
382
Q_EXPORT_PLUGIN2(appmenuplatformmenubar, AppMenuPlatformMenuBarFactory)
386
#include <appmenuplatformmenubar.moc>