Warning, file /system/qtcurve/qt4/style/macmenu.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*****************************************************************************
0002  *   Copyright 2007 Thomas Luebking <thomas.luebking@web.de>                 *
0003  *   Copyright 2007 - 2010 Craig Drummond <craig.p.drummond@gmail.com>       *
0004  *   Copyright 2013 - 2015 Yichao Yu <yyc1992@gmail.com>                     *
0005  *                                                                           *
0006  *   This program is free software; you can redistribute it and/or modify    *
0007  *   it under the terms of the GNU Lesser General Public License as          *
0008  *   published by the Free Software Foundation; either version 2.1 of the    *
0009  *   License, or (at your option) version 3, or any later version accepted   *
0010  *   by the membership of KDE e.V. (or its successor approved by the         *
0011  *   membership of KDE e.V.), which shall act as a proxy defined in          *
0012  *   Section 6 of version 3 of the license.                                  *
0013  *                                                                           *
0014  *   This program is distributed in the hope that it will be useful,         *
0015  *   but WITHOUT ANY WARRANTY; without even the implied warranty of          *
0016  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *
0017  *   Lesser General Public License for more details.                         *
0018  *                                                                           *
0019  *   You should have received a copy of the GNU Lesser General Public        *
0020  *   License along with this library. If not,                                *
0021  *   see <http://www.gnu.org/licenses/>.                                     *
0022  *****************************************************************************/
0023 
0024 #include <qtcurve-utils/utils.h>
0025 #include <qtcurve-utils/qtutils.h>
0026 
0027 #include <QActionEvent>
0028 #include <QApplication>
0029 #include <QDBusConnectionInterface>
0030 #include <QDBusMessage>
0031 #include <QLayout>
0032 #include <QMenuBar>
0033 #include <QWindowStateChangeEvent>
0034 
0035 #include "macmenu.h"
0036 #include "macmenu-dbus.h"
0037 
0038 #include <QtDebug>
0039 
0040 using namespace Bespin;
0041 
0042 static MacMenu *instance = 0;
0043 #define MSG(_FNC_) QDBusMessage::createMethodCall( "org.kde.XBar", "/XBar", "org.kde.XBar", _FNC_ )
0044 #define XBAR_SEND( _MSG_ ) QDBusConnection::sessionBus().send( _MSG_ )
0045 
0046 bool
0047 FullscreenWatcher::eventFilter(QObject *o, QEvent *ev)
0048 {
0049     QWidget *window = QtCurve::qtcToWidget(o);
0050     if (!(window && ev->type() == QEvent::WindowStateChange))
0051         return false;
0052     if (window->windowState() & Qt::WindowFullScreen)
0053         instance->deactivate(window);
0054     else
0055         instance->activate(window);
0056     return false;
0057 }
0058 
0059 static FullscreenWatcher *fullscreenWatcher = 0;
0060 
0061 MacMenu::MacMenu() : QObject()
0062 {
0063     usingMacMenu = QDBusConnection::sessionBus().interface()->isServiceRegistered("org.kde.XBar");
0064     service = QString("org.kde.XBar-%1").arg(QCoreApplication::applicationPid());
0065     // register me
0066     QDBusConnection::sessionBus().registerService(service);
0067     QDBusConnection::sessionBus().registerObject("/XBarClient", this);
0068 
0069     connect (qApp, SIGNAL(aboutToQuit()), this, SLOT(deactivate()));
0070 }
0071 
0072 
0073 void
0074 MacMenu::manage(QMenuBar *menu)
0075 {
0076     if (!menu) // ...
0077         return;
0078 
0079     // we only accept menus that are placed on a QMainWindow - for the moment, and probably ever
0080     QWidget *dad = menu->parentWidget();
0081     if (!(dad && dad->isWindow() && dad->inherits("QMainWindow") && dad->layout() && dad->layout()->menuBar() == menu))
0082         return;
0083 
0084 //     if ((dad = dad->parentWidget()) && dad->inherits("QMdiSubWindow"))
0085 //         return;
0086 
0087 
0088     if (!instance)
0089     {
0090         instance = new MacMenu;
0091         /*MacMenuAdaptor *adapt = */new MacMenuAdaptor(instance);
0092         fullscreenWatcher = new FullscreenWatcher;
0093     }
0094     else if (instance->items.contains(menu))
0095         return; // no double adds please!
0096 
0097     if (instance->usingMacMenu)
0098         instance->activate(menu);
0099 
0100     connect (menu, SIGNAL(destroyed(QObject *)), instance, SLOT(_release(QObject *)));
0101 
0102     instance->items.append(menu);
0103 }
0104 
0105 void
0106 MacMenu::release(QMenuBar *menu)
0107 {
0108     if (!instance)
0109         return;
0110     instance->_release(menu);
0111 }
0112 
0113 bool
0114 MacMenu::isActive()
0115 {
0116     return instance && instance->usingMacMenu;
0117 }
0118 
0119 void
0120 MacMenu::_release(QObject *o)
0121 {
0122     XBAR_SEND( MSG("unregisterMenu") << (qlonglong)o );
0123 
0124     QMenuBar *menu = qobject_cast<QMenuBar*>(o);
0125     if (!menu) return;
0126 
0127     items.removeAll(menu);
0128     menu->removeEventFilter(this);
0129     QWidget *dad = menu->parentWidget();
0130     if (dad && dad->layout())
0131         dad->layout()->setMenuBar(menu);
0132     menu->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
0133     menu->adjustSize();
0134 //    menu->updateGeometry();
0135 }
0136 
0137 void
0138 MacMenu::activate()
0139 {
0140     MenuList::iterator menu = items.begin();
0141     while (menu != items.end())
0142     {
0143         if (*menu)
0144             { activate(*menu); ++menu; }
0145         else
0146             { actions.remove(*menu); menu = items.erase(menu); }
0147     }
0148     usingMacMenu = true;
0149 }
0150 
0151 void
0152 MacMenu::activate(QMenuBar *menu)
0153 {
0154     menu->removeEventFilter(this);
0155 
0156     // and WOWWWW - no more per window menubars...
0157     menu->setFixedSize(0,0);
0158     //NOTICE i used to set the menu's parent->layout()->setMenuBar(0) to get rid of the free space
0159     // but this leeds to side effects (e.g. kcalc won't come up anymore...)
0160     // so now the stylehint for the free space below checks the menubar height and returns
0161     // a negative value so that final result will be 1 px heigh...
0162     menu->updateGeometry();
0163 
0164     // we need to hold a copy of this list to handle action removes
0165     // (as we get the event after the action has been removed from the widget...)
0166     actions[menu] = menu->actions();
0167 
0168     // find a nice header
0169     QString title = menu->window()->windowTitle();
0170     const QStringList appArgs = QCoreApplication::arguments();
0171     QString name = appArgs.isEmpty() ? "" : appArgs.at(0).section('/', -1);
0172     if (title.isEmpty())
0173         title = name;
0174     else
0175     {
0176         int i = title.indexOf(name, 0, Qt::CaseInsensitive);
0177         if (i > -1)
0178             title = title.mid(i, name.length());
0179     }
0180     title = title.section(" - ", -1);
0181     if (title.isEmpty())
0182     {
0183         if (!menu->actions().isEmpty())
0184             title = menu->actions().at(0)->text();
0185         if (title.isEmpty())
0186             title = "QApplication";
0187     }
0188     // register the menu via dbus
0189     QStringList entries;
0190     foreach (QAction *action, menu->actions()) {
0191         if (action->isSeparator()) {
0192             entries << "<XBAR_SEPARATOR/>";
0193         } else {
0194             entries << action->text();
0195         }
0196     }
0197     XBAR_SEND( MSG("registerMenu") << service << (qlonglong)menu << title << entries );
0198     // TODO cause of now async call, the following should - maybe - attached to the above?!!
0199     if (menu->isActiveWindow())
0200         XBAR_SEND( MSG("requestFocus") << (qlonglong)menu );
0201     // take care of several widget events!
0202     menu->installEventFilter(this);
0203     if (menu->window())
0204     {
0205         menu->window()->removeEventFilter(fullscreenWatcher);
0206         menu->window()->installEventFilter(fullscreenWatcher);
0207     }
0208 }
0209 
0210 void
0211 MacMenu::activate(QWidget *window)
0212 {
0213     MenuList::iterator menu = items.begin();
0214     while (menu != items.end())
0215     {
0216         if (*menu)
0217         {
0218             if ((*menu)->window() == window)
0219                 { activate(*menu); return; }
0220             ++menu;
0221         }
0222         else
0223             { actions.remove(*menu); menu = items.erase(menu); }
0224     }
0225 }
0226 
0227 void
0228 MacMenu::deactivate()
0229 {
0230     usingMacMenu = false;
0231 
0232     MenuList::iterator i = items.begin();
0233     QMenuBar *menu = 0;
0234     while (i != items.end())
0235     {
0236         actions.remove(*i);
0237         if ((menu = *i))
0238         {
0239             deactivate(menu);
0240             ++i;
0241         }
0242         else
0243             i = items.erase(i);
0244     }
0245 }
0246 
0247 void
0248 MacMenu::deactivate(QMenuBar *menu)
0249 {
0250     menu->removeEventFilter(this);
0251     QWidget *dad = menu->parentWidget();
0252     if (dad && dad->layout())
0253         dad->layout()->setMenuBar(menu);
0254     menu->setMaximumSize(QWIDGETSIZE_MAX, QWIDGETSIZE_MAX);
0255     menu->adjustSize();
0256     //             menu->updateGeometry();
0257 }
0258 
0259 void
0260 MacMenu::deactivate(QWidget *window)
0261 {
0262     MenuList::iterator menu = items.begin();
0263     while (menu != items.end())
0264     {
0265         if (*menu)
0266         {
0267             if ((*menu)->window() == window)
0268                 { deactivate(*menu); return; }
0269             ++menu;
0270         }
0271         else
0272         { actions.remove(*menu); menu = items.erase(menu); }
0273     }
0274 }
0275 
0276 QMenuBar *
0277 MacMenu::menuBar(qlonglong key)
0278 {
0279     MenuList::iterator i = items.begin();
0280     QMenuBar *menu;
0281     while (i != items.end())
0282     {
0283         if (!(menu = *i))
0284         {
0285             actions.remove(menu);
0286             i = items.erase(i);
0287         }
0288         else
0289         {
0290             if ((qlonglong)menu == key)
0291                 return menu;
0292             else
0293                 ++i;
0294         }
0295     }
0296     return nullptr;
0297 }
0298 
0299 void
0300 MacMenu::popup(qlonglong key, int idx, int x, int y)
0301 {
0302     QMenuBar *menu = menuBar(key);
0303     if (!menu) return;
0304 
0305     QMenu *pop;
0306     for (int i = 0; i < menu->actions().count(); ++i)
0307     {
0308         if (!(pop = menu->actions().at(i)->menu()))
0309             continue;
0310 
0311         if (i == idx) {
0312             if (!pop->isVisible())
0313             {
0314                 connect (pop, SIGNAL(aboutToHide()), this, SLOT(menuClosed()));
0315                 XBAR_SEND( MSG("setOpenPopup") << idx );
0316                 pop->popup(QPoint(x,y));
0317             }
0318             else
0319             {
0320                 XBAR_SEND( MSG("setOpenPopup") << -1000 );
0321                 pop->hide();
0322             }
0323         }
0324         else
0325             pop->hide();
0326     }
0327 }
0328 
0329 void
0330 MacMenu::popDown(qlonglong key)
0331 {
0332     QMenuBar *menu = menuBar(key);
0333     if (!menu) return;
0334 
0335     QWidget *pop;
0336     for (int i = 0; i < menu->actions().count(); ++i)
0337     {
0338         if (!(pop = menu->actions().at(i)->menu()))
0339             continue;
0340         disconnect (pop, SIGNAL(aboutToHide()), this, SLOT(menuClosed()));
0341         pop->hide();
0342 //         menu->activateWindow();
0343         break;
0344     }
0345 }
0346 
0347 static bool inHover = false;
0348 
0349 void
0350 MacMenu::hover(qlonglong key, int idx,  int x, int y)
0351 {
0352     QMenuBar *menu = menuBar(key);
0353     if (!menu) return;
0354 
0355     QWidget *pop;
0356     for (int i = 0; i < menu->actions().count(); ++i)
0357     {
0358         if ((i == idx) || !(pop = menu->actions().at(i)->menu()))
0359             continue;
0360         if (pop->isVisible())
0361         {
0362             inHover = true;
0363             popup(key, idx, x, y); // TODO: this means a useless second pass above...
0364             inHover = false;
0365             break;
0366         }
0367     }
0368 }
0369 
0370 static QMenuBar*
0371 bar4menu(QMenu *menu)
0372 {
0373     if (!menu->menuAction())
0374         return 0;
0375     if (menu->menuAction()->associatedWidgets().isEmpty())
0376         return 0;
0377     foreach (QWidget *w, menu->menuAction()->associatedWidgets()) {
0378         if (qobject_cast<QMenuBar*>(w)) {
0379             return static_cast<QMenuBar*>(w);
0380         }
0381     }
0382     return 0;
0383 }
0384 
0385 void
0386 MacMenu::menuClosed()
0387 {
0388     QObject * _sender = sender();
0389     if (!_sender)
0390         return;
0391 
0392     disconnect (sender(), SIGNAL(aboutToHide()), this, SLOT(menuClosed()));
0393     if (!inHover)
0394     {
0395         XBAR_SEND( MSG("setOpenPopup") << -500 );
0396 
0397         if (QMenu *menu = qobject_cast<QMenu*>(_sender))
0398         if (QMenuBar *bar = bar4menu(menu))
0399             bar->activateWindow();
0400     }
0401 }
0402 
0403 void
0404 MacMenu::changeAction(QMenuBar *menu, QActionEvent *ev)
0405 {
0406     int idx;
0407     const QString title = ev->action()->isSeparator() ? "<XBAR_SEPARATOR/>" : ev->action()->text();
0408     if (ev->type() == QEvent::ActionAdded)
0409     {
0410         idx = ev->before() ? menu->actions().indexOf(ev->before())-1 : -1;
0411         XBAR_SEND( MSG("addEntry") << (qlonglong)menu << idx << title );
0412         actions[menu].insert(idx, ev->action());
0413         return;
0414     }
0415     if (ev->type() == QEvent::ActionChanged)
0416     {
0417         idx = menu->actions().indexOf(ev->action());
0418         XBAR_SEND( MSG("changeEntry") << (qlonglong)menu << idx << title );
0419     }
0420     else
0421     { // remove
0422         idx = actions[menu].indexOf(ev->action());
0423         actions[menu].removeAt(idx);
0424         XBAR_SEND( MSG("removeEntry") << (qlonglong)menu << idx );
0425     }
0426 }
0427 
0428 void
0429 MacMenu::raise(qlonglong key)
0430 {
0431     if (QMenuBar *menu = menuBar(key))
0432     {
0433         if (QWidget *win = menu->window())
0434         {
0435             win->showNormal();
0436             win->activateWindow();
0437             win->raise();
0438         }
0439     }
0440 }
0441 
0442 bool
0443 MacMenu::eventFilter(QObject *o, QEvent *ev)
0444 {
0445     QMenuBar *menu = qobject_cast<QMenuBar*>(o);
0446     if (!menu)
0447         return false;
0448 
0449     if (!usingMacMenu)
0450         return false;
0451 
0452     QString func;
0453     switch (ev->type())
0454     {
0455     case QEvent::Resize:
0456 //         menu->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored));
0457         if (menu->size() != QSize(0,0))
0458         {
0459             menu->setFixedSize(0,0);
0460             menu->updateGeometry();
0461         }
0462         break;
0463     case QEvent::ActionAdded:
0464     case QEvent::ActionChanged:
0465     case QEvent::ActionRemoved:
0466         changeAction(menu, static_cast<QActionEvent*>(ev));
0467         break;
0468 //     case QEvent::ParentChange:
0469 //         qDebug() << o << ev;
0470 //         return false;
0471     case QEvent::EnabledChange:
0472         if (static_cast<QWidget*>(o)->isEnabled())
0473             XBAR_SEND( MSG("requestFocus") << (qlonglong)menu );
0474         else
0475             XBAR_SEND( MSG("releaseFocus") << (qlonglong)menu );
0476         break;
0477 
0478     // TODO: test whether this is the only one and show it? (e.g. what about dialogs...?!)
0479     case QEvent::ApplicationActivate:
0480 //         if (items.count() > 1)
0481 //             break;
0482     case QEvent::WindowActivate:
0483         XBAR_SEND( MSG("requestFocus") << (qlonglong)menu );
0484         break;
0485 
0486     case QEvent::WindowDeactivate:
0487 //         if (items.count() == 1)
0488 //             break;
0489     case QEvent::WindowBlocked:
0490     case QEvent::ApplicationDeactivate:
0491         XBAR_SEND( MSG("releaseFocus") << (qlonglong)menu );
0492         break;
0493     default:
0494         return false;
0495 
0496 // maybe these need to be passed through...?!
0497 //       QEvent::GrabKeyboard
0498 //       QEvent::GrabMouse
0499 //       QEvent::KeyPress
0500 //       QEvent::KeyRelease
0501 //       QEvent::UngrabKeyboard
0502 //       QEvent::UngrabMouse
0503 // --- and what about these ---
0504 //       QEvent::MenubarUpdated
0505 //       QEvent::ParentChange
0506 // -------------------
0507     }
0508     return false;
0509 }
0510 
0511 #undef MSG
0512 #undef XBAR_SEND