~ubuntu-branches/ubuntu/oneiric/appmenu-qt/oneiric-proposed

« back to all changes in this revision

Viewing changes to src/appmenuplatformmenubar.cpp

  • Committer: Package Import Robot
  • Author(s): Didier Roche
  • Date: 2011-09-02 13:32:01 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: package-import@ubuntu.com-20110902133201-o88yxapf6ajfl7yk
Tags: 0.2.2-0ubuntu1
* New upstream release
  - Update "Status" dbusmenu property, making it possible to show menubar
    in Unity panel by pressing Alt (LP: #737419).
  - Improve robustness: menubar gets correctly exposed and unexposed when
    menubar renderer is started or stopped (LP: #838115, #602913).
  - Change licence to LGPLv3.
* debian/copyright:
  - update for new licence
* debian/control:
  - dep on libdbusmenu-qt-dev 0.9.0
  - bump Standards-Version to latest

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* This file is part of the appmenu-qt project
 
2
   Copyright 2011 Canonical
 
3
   Author: Aurelien Gateau <aurelien.gateau@canonical.com>
 
4
 
 
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.
 
8
 
 
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
 
12
   for more details.
 
13
 
 
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/>.
 
16
*/
 
17
#include "appmenuplatformmenubar.h"
 
18
 
 
19
// dbusmenu-qt
 
20
#include <dbusmenuexporter.h>
 
21
 
 
22
// Qt
 
23
#include <QActionEvent>
 
24
#include <QApplication>
 
25
#include <QDebug>
 
26
#include <QMenu>
 
27
#include <QMenuBar>
 
28
#include <QDBusInterface>
 
29
#include <QDBusObjectPath>
 
30
#include <QDBusPendingCall>
 
31
#include <QDBusServiceWatcher>
 
32
#include <QStyle>
 
33
 
 
34
QT_BEGIN_NAMESPACE
 
35
 
 
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";
 
39
 
 
40
#define LOG qDebug() << __FUNCTION__
 
41
#define LOG_VAR(x) qDebug() << __FUNCTION__ << #x ":" << x
 
42
#define WARN qWarning() << __FUNCTION__
 
43
 
 
44
class MenuBarAdapter
 
45
{
 
46
public:
 
47
    MenuBarAdapter(QMenuBar*, const QString&);
 
48
    ~MenuBarAdapter();
 
49
    void addAction(QAction*, QAction* before=0);
 
50
    void removeAction(QAction*);
 
51
    void syncAction(QAction*) {}
 
52
 
 
53
    bool registerWindow();
 
54
 
 
55
    void popupAction(QAction*);
 
56
 
 
57
    uint m_registeredWinId;
 
58
 
 
59
    DBusMenuExporter* m_exporter;
 
60
 
 
61
private:
 
62
    QMenu* m_rootMenu;
 
63
    QMenuBar* m_menuBar;
 
64
    QString m_objectPath;
 
65
};
 
66
 
 
67
AppMenuPlatformMenuBar::~AppMenuPlatformMenuBar()
 
68
{
 
69
    destroyMenuBar();
 
70
}
 
71
 
 
72
void AppMenuPlatformMenuBar::init(QMenuBar* _menuBar)
 
73
{
 
74
    m_nativeMenuBar = NMB_Auto;
 
75
    m_altPressed = false;
 
76
    m_menuBar = _menuBar;
 
77
 
 
78
    static int menuBarId = 1;
 
79
    m_objectPath = QString(QLatin1String("/MenuBar/%1")).arg(menuBarId++);
 
80
    m_registrarWatcher = new QDBusServiceWatcher(
 
81
        REGISTRAR_SERVICE,
 
82
        QDBusConnection::sessionBus(),
 
83
        QDBusServiceWatcher::WatchForOwnerChange,
 
84
        m_menuBar);
 
85
    // m_adapter will be created in handleReparent()
 
86
    m_adapter = 0;
 
87
 
 
88
    connect(m_registrarWatcher, SIGNAL(serviceOwnerChanged(const QString&, const QString&, const QString&)),
 
89
            SLOT(slotMenuBarServiceChanged(const QString&, const QString&, const QString&)));
 
90
}
 
91
 
 
92
void AppMenuPlatformMenuBar::setVisible(bool visible)
 
93
{
 
94
    m_menuBar->QWidget::setVisible(visible);
 
95
}
 
96
 
 
97
void AppMenuPlatformMenuBar::actionEvent(QActionEvent* e)
 
98
{
 
99
    if (m_adapter) {
 
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());
 
106
        }
 
107
    }
 
108
}
 
109
 
 
110
void AppMenuPlatformMenuBar::handleReparent(QWidget* oldParent, QWidget* newParent, QWidget* oldWindow, QWidget* newWindow)
 
111
{
 
112
    Q_UNUSED(oldParent)
 
113
    Q_UNUSED(newParent)
 
114
    if (isNativeMenuBar()) {
 
115
        if (m_adapter) {
 
116
            if (oldWindow != newWindow) {
 
117
                m_adapter->registerWindow();
 
118
            }
 
119
        } else {
 
120
            createMenuBar();
 
121
        }
 
122
    }
 
123
}
 
124
 
 
125
bool AppMenuPlatformMenuBar::allowCornerWidgets() const
 
126
{
 
127
    return !isNativeMenuBar();
 
128
}
 
129
 
 
130
void AppMenuPlatformMenuBar::popupAction(QAction* act)
 
131
{
 
132
    if (act && act->menu()) {
 
133
        m_adapter->popupAction(act);
 
134
    }
 
135
}
 
136
 
 
137
void AppMenuPlatformMenuBar::setNativeMenuBar(bool native)
 
138
{
 
139
    if (m_nativeMenuBar == NMB_DisabledByEnv) {
 
140
        WARN << "native menubar disabled by environment variable";
 
141
        return;
 
142
    }
 
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) {
 
147
            destroyMenuBar();
 
148
        }
 
149
    }
 
150
}
 
151
 
 
152
bool AppMenuPlatformMenuBar::isNativeMenuBar() const
 
153
{
 
154
    if (m_nativeMenuBar == NMB_DisabledByEnv) {
 
155
        return false;
 
156
    }
 
157
    if (m_nativeMenuBar == NMB_Auto) {
 
158
        return !QApplication::instance()->testAttribute(Qt::AA_DontUseNativeMenuBar);
 
159
    }
 
160
    return m_nativeMenuBar == NMB_Enabled;
 
161
}
 
162
 
 
163
bool AppMenuPlatformMenuBar::shortcutsHandledByNativeMenuBar() const
 
164
{
 
165
    return false;
 
166
}
 
167
 
 
168
bool AppMenuPlatformMenuBar::menuBarEventFilter(QObject*, QEvent* event)
 
169
{
 
170
    if (event->type() == QEvent::WinIdChange) {
 
171
        if (isNativeMenuBar() && m_adapter) {
 
172
            QMetaObject::invokeMethod(this, "registerWindow", Qt::QueuedConnection);
 
173
        }
 
174
    }
 
175
 
 
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) {
 
180
            setAltPressed(true);
 
181
        }
 
182
    }
 
183
    return false;
 
184
}
 
185
 
 
186
bool AppMenuPlatformMenuBar::eventFilter(QObject*, QEvent* event)
 
187
{
 
188
    if (!m_altPressed) {
 
189
        WARN << "called with m_altPressed=false. Should not happen.";
 
190
        return false;
 
191
    }
 
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);
 
201
        break;
 
202
    default:
 
203
        break;
 
204
    }
 
205
    return false;
 
206
}
 
207
 
 
208
void AppMenuPlatformMenuBar::setAltPressed(bool pressed)
 
209
{
 
210
    m_altPressed = pressed;
 
211
    if (pressed) {
 
212
        qApp->installEventFilter(this);
 
213
    } else {
 
214
        qApp->removeEventFilter(this);
 
215
    }
 
216
    if (m_adapter) {
 
217
        m_adapter->m_exporter->setStatus(m_altPressed ? "notice" : "normal");
 
218
    }
 
219
}
 
220
 
 
221
void AppMenuPlatformMenuBar::slotMenuBarServiceChanged(const QString &/*serviceName*/, const QString &/*oldOwner*/, const QString& newOwner)
 
222
{
 
223
    if (m_nativeMenuBar == NMB_DisabledByEnv || m_nativeMenuBar == NMB_Disabled) {
 
224
        return;
 
225
    }
 
226
 
 
227
    if (newOwner.isEmpty()) {
 
228
        destroyMenuBar();
 
229
        QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, true);
 
230
        m_menuBar->updateGeometry();
 
231
        m_menuBar->setVisible(false);
 
232
        m_menuBar->setVisible(true);
 
233
        return;
 
234
    }
 
235
 
 
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);
 
241
    delete m_adapter;
 
242
    m_adapter = 0;
 
243
    createMenuBar();
 
244
}
 
245
 
 
246
void AppMenuPlatformMenuBar::registerWindow()
 
247
{
 
248
    if (m_adapter) {
 
249
        m_adapter->registerWindow();
 
250
    }
 
251
}
 
252
 
 
253
void AppMenuPlatformMenuBar::createMenuBar()
 
254
{
 
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";
 
258
 
 
259
    if (!m_menuBar->parentWidget()) {
 
260
        return;
 
261
    }
 
262
 
 
263
    m_adapter = 0;
 
264
 
 
265
    if (!firstCall && !envSaysBoth && QApplication::testAttribute(Qt::AA_DontUseNativeMenuBar)) {
 
266
        return;
 
267
    }
 
268
 
 
269
    if (envSaysNo) {
 
270
        if (firstCall) {
 
271
            firstCall = false;
 
272
            m_nativeMenuBar = NMB_DisabledByEnv;
 
273
            QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, true);
 
274
        }
 
275
        return;
 
276
    }
 
277
 
 
278
    m_adapter = new MenuBarAdapter(m_menuBar, m_objectPath);
 
279
    if (!m_adapter->registerWindow()) {
 
280
        destroyMenuBar();
 
281
    }
 
282
 
 
283
    if (firstCall) {
 
284
        firstCall = false;
 
285
        bool dontUseNativeMenuBar = !m_adapter;
 
286
        if (envSaysBoth) {
 
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;
 
290
        }
 
291
        QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, dontUseNativeMenuBar);
 
292
    }
 
293
}
 
294
 
 
295
void AppMenuPlatformMenuBar::destroyMenuBar()
 
296
{
 
297
    delete m_adapter;
 
298
    m_adapter = 0;
 
299
}
 
300
 
 
301
///////////////////////////////////////////////////////////
 
302
MenuBarAdapter::MenuBarAdapter(QMenuBar* _menuBar, const QString& _objectPath)
 
303
: m_registeredWinId(0)
 
304
, m_exporter(0)
 
305
, m_rootMenu(new QMenu)
 
306
, m_menuBar(_menuBar)
 
307
, m_objectPath(_objectPath)
 
308
{
 
309
}
 
310
 
 
311
MenuBarAdapter::~MenuBarAdapter()
 
312
{
 
313
    delete m_exporter;
 
314
    m_exporter = 0;
 
315
    delete m_rootMenu;
 
316
    m_rootMenu = 0;
 
317
}
 
318
 
 
319
bool MenuBarAdapter::registerWindow()
 
320
{
 
321
    if (!m_menuBar->window()) {
 
322
        WARN << "No parent for this menubar";
 
323
        return false;
 
324
    }
 
325
 
 
326
    uint winId = m_menuBar->window()->winId();
 
327
    if (winId == m_registeredWinId) {
 
328
        return true;
 
329
    }
 
330
 
 
331
    QDBusInterface host(REGISTRAR_SERVICE, REGISTRAR_PATH, REGISTRAR_IFACE);
 
332
    if (!host.isValid()) {
 
333
        return false;
 
334
    }
 
335
 
 
336
    Q_FOREACH(QAction *action, m_menuBar->actions()) {
 
337
        if (!action->isSeparator()) {
 
338
            m_rootMenu->addAction(action);
 
339
        }
 
340
    }
 
341
 
 
342
    if (m_rootMenu->actions().isEmpty()) {
 
343
        return true;
 
344
    }
 
345
 
 
346
    if (!m_exporter) {
 
347
        m_exporter = new DBusMenuExporter(m_objectPath, m_rootMenu);
 
348
    }
 
349
 
 
350
    m_registeredWinId = winId;
 
351
    QVariant path = QVariant::fromValue<QDBusObjectPath>(QDBusObjectPath(m_objectPath));
 
352
    host.asyncCall(QLatin1String("RegisterWindow"), QVariant(winId), path);
 
353
    return true;
 
354
}
 
355
 
 
356
void MenuBarAdapter::addAction(QAction* action, QAction* before)
 
357
{
 
358
    if (!action->isSeparator()) {
 
359
        m_rootMenu->insertAction(before, action);
 
360
    }
 
361
    if (!m_registeredWinId) {
 
362
        registerWindow();
 
363
    }
 
364
}
 
365
 
 
366
void MenuBarAdapter::removeAction(QAction* action)
 
367
{
 
368
    m_rootMenu->removeAction(action);
 
369
}
 
370
 
 
371
void MenuBarAdapter::popupAction(QAction* action)
 
372
{
 
373
    m_exporter->activateAction(action);
 
374
}
 
375
 
 
376
///////////////////////////////////////////////////////////
 
377
QAbstractPlatformMenuBar* AppMenuPlatformMenuBarFactory::create()
 
378
{
 
379
    return new AppMenuPlatformMenuBar;
 
380
}
 
381
 
 
382
Q_EXPORT_PLUGIN2(appmenuplatformmenubar, AppMenuPlatformMenuBarFactory)
 
383
 
 
384
QT_END_NAMESPACE
 
385
 
 
386
#include <appmenuplatformmenubar.moc>