File indexing completed on 2024-11-24 05:00:56

0001 /*
0002 
0003     SPDX-FileCopyrightText: 2016 Dmitry Shachnev <mitya57@gmail.com>
0004     SPDX-FileContributor: The Qt Company <https://www.qt.io/licensing/>
0005 
0006     SPDX-License-Identifier: LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KFQF-Accepted-GPL OR LicenseRef-Qt-Commercial
0007 
0008 */
0009 
0010 #include "qdbusmenubar_p.h"
0011 
0012 QT_BEGIN_NAMESPACE
0013 
0014 /* note: do not change these to QStringLiteral;
0015    we are unloaded before QtDBus is done using the strings.
0016  */
0017 #define REGISTRAR_SERVICE QLatin1String("com.canonical.AppMenu.Registrar")
0018 #define REGISTRAR_PATH QLatin1String("/com/canonical/AppMenu/Registrar")
0019 
0020 QDBusMenuBar::QDBusMenuBar(KdePlatformTheme *platformTheme)
0021     : QPlatformMenuBar()
0022     , m_menu(new QDBusPlatformMenu())
0023     , m_menuAdaptor(new QDBusMenuAdaptor(m_menu))
0024     , m_platformTheme(platformTheme)
0025 {
0026     QDBusMenuItem::registerDBusTypes();
0027     connect(m_menu, &QDBusPlatformMenu::propertiesUpdated, m_menuAdaptor, &QDBusMenuAdaptor::ItemsPropertiesUpdated);
0028     connect(m_menu, &QDBusPlatformMenu::updated, m_menuAdaptor, &QDBusMenuAdaptor::LayoutUpdated);
0029     connect(m_menu, SIGNAL(popupRequested(int, uint)), m_menuAdaptor, SIGNAL(ItemActivationRequested(int, uint)));
0030 }
0031 
0032 QDBusMenuBar::~QDBusMenuBar()
0033 {
0034     if (s_globalMenuBar == this) {
0035         s_globalMenuBar = nullptr;
0036         m_platformTheme->globalMenuBarNoLongerExists();
0037     }
0038 
0039     if (this == s_menuBars.value(m_window))
0040         s_menuBars.remove(m_window);
0041 
0042     unregisterMenuBarX11(m_window);
0043     delete m_menuAdaptor;
0044     delete m_menu;
0045     qDeleteAll(m_menuItems);
0046 }
0047 
0048 QDBusPlatformMenuItem *QDBusMenuBar::menuItemForMenu(QPlatformMenu *menu)
0049 {
0050     if (!menu)
0051         return nullptr;
0052     quintptr tag = menu->tag();
0053     const auto it = m_menuItems.constFind(tag);
0054     if (it != m_menuItems.cend()) {
0055         return *it;
0056     } else {
0057         QDBusPlatformMenuItem *item = new QDBusPlatformMenuItem;
0058         updateMenuItem(item, menu);
0059         m_menuItems.insert(tag, item);
0060         return item;
0061     }
0062 }
0063 
0064 void QDBusMenuBar::updateMenuItem(QDBusPlatformMenuItem *item, QPlatformMenu *menu)
0065 {
0066     const QDBusPlatformMenu *ourMenu = qobject_cast<const QDBusPlatformMenu *>(menu);
0067     item->setText(ourMenu->text());
0068     item->setIcon(ourMenu->icon());
0069     item->setEnabled(ourMenu->isEnabled());
0070     item->setVisible(ourMenu->isVisible());
0071     item->setMenu(menu);
0072 }
0073 
0074 void QDBusMenuBar::insertMenu(QPlatformMenu *menu, QPlatformMenu *before)
0075 {
0076     QDBusPlatformMenuItem *menuItem = menuItemForMenu(menu);
0077     QDBusPlatformMenuItem *beforeItem = menuItemForMenu(before);
0078     m_menu->insertMenuItem(menuItem, beforeItem);
0079     m_menu->emitUpdated();
0080 }
0081 
0082 void QDBusMenuBar::removeMenu(QPlatformMenu *menu)
0083 {
0084     QDBusPlatformMenuItem *menuItem = menuItemForMenu(menu);
0085     m_menu->removeMenuItem(menuItem);
0086     m_menu->emitUpdated();
0087 }
0088 
0089 void QDBusMenuBar::syncMenu(QPlatformMenu *menu)
0090 {
0091     QDBusPlatformMenuItem *menuItem = menuItemForMenu(menu);
0092     updateMenuItem(menuItem, menu);
0093 }
0094 
0095 void QDBusMenuBar::handleReparent(QWindow *newParentWindow)
0096 {
0097     // if the parent is set to nullptr on our first time around,
0098     // this is supposed to be an app-wide menu bar
0099     // so we only care if nullptr == nullptr after our first round
0100     if (m_initted && newParentWindow == m_window) {
0101         return;
0102     }
0103     m_initted = true;
0104 
0105     QWindow *oldWindow = m_window;
0106 
0107     if (this == s_menuBars.value(oldWindow))
0108         s_menuBars.remove(oldWindow);
0109 
0110     unregisterMenuBarX11(m_window);
0111     m_window = newParentWindow;
0112     s_menuBars[newParentWindow] = this;
0113 
0114     if (newParentWindow) {
0115         if (s_globalMenuBar == this) {
0116             s_globalMenuBar = nullptr;
0117             m_platformTheme->globalMenuBarNoLongerExists();
0118         }
0119         if (createDBusMenuBar()) {
0120             registerMenuBarX11(m_window, m_objectPath);
0121         }
0122     } else if (!s_globalMenuBar) {
0123         s_globalMenuBar = this;
0124         createDBusMenuBar();
0125         m_platformTheme->globalMenuBarExistsNow();
0126     } else {
0127         qWarning() << "There's already a global menu bar...";
0128     }
0129 
0130     Q_EMIT windowChanged(newParentWindow, oldWindow);
0131 }
0132 
0133 QDBusMenuBar *QDBusMenuBar::globalMenuBar()
0134 {
0135     return s_globalMenuBar;
0136 }
0137 
0138 QPlatformMenu *QDBusMenuBar::menuForTag(quintptr tag) const
0139 {
0140     QDBusPlatformMenuItem *menuItem = m_menuItems.value(tag);
0141     if (menuItem)
0142         return const_cast<QPlatformMenu *>(menuItem->menu());
0143     return nullptr;
0144 }
0145 
0146 QPlatformMenu *QDBusMenuBar::createMenu() const
0147 {
0148     return new QDBusPlatformMenu;
0149 }
0150 
0151 bool QDBusMenuBar::createDBusMenuBar()
0152 {
0153     static uint menuBarId = 0;
0154 
0155     QDBusConnection connection = QDBusConnection::sessionBus();
0156     m_objectPath = QStringLiteral("/MenuBar/%1").arg(++menuBarId);
0157     if (!connection.registerObject(m_objectPath, m_menu))
0158         return false;
0159 
0160     return true;
0161 }
0162 void QDBusMenuBar::uncreateDBusMenuBar()
0163 {
0164     QDBusConnection connection = QDBusConnection::sessionBus();
0165 
0166     if (!m_objectPath.isEmpty())
0167         connection.unregisterObject(m_objectPath);
0168 }
0169 
0170 QDBusMenuBar *QDBusMenuBar::menuBarForWindow(QWindow *window)
0171 {
0172     return s_menuBars.value(window);
0173 }
0174 
0175 void QDBusMenuBar::registerMenuBarX11(QWindow *window, const QString &objectPath)
0176 {
0177     if (!window) {
0178         qWarning("Cannot register window menu without window");
0179         return;
0180     }
0181 
0182     QDBusConnection connection = QDBusConnection::sessionBus();
0183     QDBusMenuRegistrarInterface registrar(REGISTRAR_SERVICE, REGISTRAR_PATH, connection, window);
0184     QDBusPendingReply<> r = registrar.RegisterWindow(static_cast<uint>(window->winId()), QDBusObjectPath(objectPath));
0185     r.waitForFinished();
0186     if (r.isError()) {
0187         qWarning("Failed to register window menu, reason: %s (\"%s\")", qUtf8Printable(r.error().name()), qUtf8Printable(r.error().message()));
0188         connection.unregisterObject(objectPath);
0189     }
0190 }
0191 
0192 QDBusMenuBar *QDBusMenuBar::s_globalMenuBar = nullptr;
0193 QMap<QWindow *, QDBusMenuBar *> QDBusMenuBar::s_menuBars;
0194 
0195 void QDBusMenuBar::unregisterMenuBarX11(QWindow *window)
0196 {
0197     if (!window) {
0198         return;
0199     }
0200 
0201     QDBusConnection connection = QDBusConnection::sessionBus();
0202     QDBusMenuRegistrarInterface registrar(REGISTRAR_SERVICE, REGISTRAR_PATH, connection, window);
0203     QDBusPendingReply<> r = registrar.UnregisterWindow(static_cast<uint>(window->winId()));
0204     r.waitForFinished();
0205     if (r.isError())
0206         qWarning("Failed to unregister window menu, reason: %s (\"%s\")", qUtf8Printable(r.error().name()), qUtf8Printable(r.error().message()));
0207 }
0208 
0209 QT_END_NAMESPACE