File indexing completed on 2024-11-17 05:01:37

0001 /*  This file is part of the KDE libraries
0002     SPDX-FileCopyrightText: 2013 Kevin Ottens <ervin+bluesystems@kde.org>
0003     SPDX-FileCopyrightText: 2013 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
0004     SPDX-FileCopyrightText: 2014 Lukáš Tinkl <ltinkl@redhat.com>
0005     SPDX-FileCopyrightText: 2022 Harald Sitter <sitter@kde.org>
0006 
0007     SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0008 */
0009 
0010 #include <config-platformtheme.h>
0011 
0012 #include "kdeplatformfiledialoghelper.h"
0013 #include "kdeplatformsystemtrayicon.h"
0014 #include "kdeplatformtheme.h"
0015 #include "kfontsettingsdata.h"
0016 #include "khintssettings.h"
0017 #include "kwaylandintegration.h"
0018 #include "x11integration.h"
0019 
0020 #include <QApplication>
0021 #include <QDBusConnection>
0022 #include <QDBusConnectionInterface>
0023 #include <QDebug>
0024 #include <QFont>
0025 #include <QPalette>
0026 #include <QString>
0027 #include <QVariant>
0028 #include <QtQuickControls2/QQuickStyle>
0029 
0030 #include <KConfigGroup>
0031 #include <KIO/Global>
0032 #include <KIO/JobUiDelegate>
0033 #include <KIO/JobUiDelegateFactory>
0034 #include <KIO/OpenWithHandlerInterface>
0035 #include <KJobWidgets>
0036 #include <KLocalizedString>
0037 #include <KStandardGuiItem>
0038 #include <KWayland/Client/connection_thread.h>
0039 #include <KWayland/Client/registry.h>
0040 #include <KWayland/Client/xdgforeign.h>
0041 #include <KWindowSystem>
0042 #include <kiconengine.h>
0043 #include <kiconloader.h>
0044 #include <kstandardshortcut.h>
0045 
0046 #include "qdbusmenubar_p.h"
0047 #include "qxdgdesktopportalfiledialog_p.h"
0048 
0049 static const QByteArray s_x11AppMenuServiceNamePropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_SERVICE_NAME");
0050 static const QByteArray s_x11AppMenuObjectPathPropertyName = QByteArrayLiteral("_KDE_NET_WM_APPMENU_OBJECT_PATH");
0051 
0052 static bool checkDBusGlobalMenuAvailable()
0053 {
0054     if (qEnvironmentVariableIsSet("KDE_NO_GLOBAL_MENU")) {
0055         return false;
0056     }
0057 
0058     QDBusConnection connection = QDBusConnection::sessionBus();
0059     QString registrarService = QStringLiteral("com.canonical.AppMenu.Registrar");
0060     return connection.interface()->isServiceRegistered(registrarService);
0061 }
0062 
0063 static bool isDBusGlobalMenuAvailable()
0064 {
0065     static bool dbusGlobalMenuAvailable = checkDBusGlobalMenuAvailable();
0066     return dbusGlobalMenuAvailable;
0067 }
0068 
0069 static QString desktopPortalService()
0070 {
0071     return QStringLiteral("org.freedesktop.impl.portal.desktop.kde");
0072 }
0073 
0074 static QString desktopPortalPath()
0075 {
0076     return QStringLiteral("/org/freedesktop/portal/desktop");
0077 }
0078 
0079 class XdgWindowExporter : public QObject
0080 {
0081     Q_OBJECT
0082 public:
0083     using QObject::QObject;
0084     virtual void run(QWidget *widget) = 0;
0085 Q_SIGNALS:
0086     void exported(const QString &id);
0087 };
0088 
0089 class XdgWindowExporterWayland : public XdgWindowExporter
0090 {
0091 public:
0092     using XdgWindowExporter::XdgWindowExporter;
0093 
0094     void run(QWidget *widget) override
0095     {
0096         Q_ASSERT(widget);
0097 
0098         auto connection = KWayland::Client::ConnectionThread::fromApplication(QGuiApplication::instance());
0099         if (!connection) {
0100             Q_EMIT exported({});
0101             return;
0102         }
0103 
0104         auto registry = new KWayland::Client::Registry(this);
0105         QPointer<QWidget> maybeWidget(widget);
0106         connect(registry, &KWayland::Client::Registry::exporterUnstableV2Announced, this, [this, registry, maybeWidget](quint32 name, quint32 version) {
0107             auto exporter = registry->createXdgExporter(name, std::min(version, quint32(1)), this);
0108             if (!maybeWidget) {
0109                 qWarning() << "widget was invalid, not exporting any window as transient parent";
0110                 Q_EMIT exported({});
0111                 return;
0112             }
0113             auto surface = KWayland::Client::Surface::fromWindow(maybeWidget->windowHandle());
0114             if (!surface) {
0115                 qWarning() << "wayland surface was unexpectedly null, not exporting any window as transient parent";
0116                 Q_EMIT exported({});
0117                 return;
0118             }
0119             auto xdgExported = exporter->exportTopLevel(surface, this);
0120 
0121             connect(xdgExported, &KWayland::Client::XdgExported::done, this, [this, xdgExported] {
0122                 Q_EMIT exported(QLatin1String("wayland:") + xdgExported->handle());
0123             });
0124         });
0125 
0126         registry->create(connection);
0127         registry->setup();
0128     }
0129 };
0130 
0131 class XdgWindowExporterX11 : public XdgWindowExporter
0132 {
0133 public:
0134     using XdgWindowExporter::XdgWindowExporter;
0135 
0136     void run(QWidget *widget) override
0137     {
0138         Q_ASSERT(widget);
0139         Q_EMIT exported(QLatin1String("x11:") + QString::number(widget->winId(), 16));
0140     }
0141 };
0142 
0143 class KIOOpenWith : public KIO::OpenWithHandlerInterface
0144 {
0145     Q_OBJECT
0146 public:
0147     explicit KIOOpenWith(QWidget *parentWidget, QObject *parent = nullptr)
0148         : KIO::OpenWithHandlerInterface(parent)
0149         , m_parentWidget(parentWidget)
0150     {
0151     }
0152 
0153     void promptUserForApplication(KJob *job, const QList<QUrl> &urls, const QString &mimeType) override
0154     {
0155         QWidget *widget = nullptr;
0156         if (job) {
0157             widget = KJobWidgets::window(job);
0158         }
0159 
0160         if (!widget) {
0161             widget = m_parentWidget;
0162         }
0163 
0164         if (!widget) {
0165             promptInternal({}, urls, mimeType);
0166             return;
0167         }
0168 
0169         widget->winId(); // ensure we have a handle so we can export a window (without this windowHandle() may be null)
0170 
0171         XdgWindowExporter *exporter = nullptr;
0172         switch (KWindowSystem::platform()) {
0173         case KWindowSystem::Platform::X11:
0174             exporter = new XdgWindowExporterX11(this);
0175             break;
0176         case KWindowSystem::Platform::Wayland:
0177             exporter = new XdgWindowExporterWayland(this);
0178             break;
0179         case KWindowSystem::Platform::Unknown:
0180             break;
0181         }
0182         if (exporter) {
0183             connect(exporter, &XdgWindowExporter::exported, this, [this, urls, exporter, mimeType](const QString &windowId) {
0184                 exporter->deleteLater();
0185                 promptInternal(windowId, urls, mimeType);
0186             });
0187             exporter->run(widget);
0188         } else {
0189             promptInternal({}, urls, mimeType);
0190         }
0191     }
0192 
0193     void promptInternal(const QString &windowId, const QList<QUrl> &urls, const QString &mimeType)
0194     {
0195         QDBusMessage message = QDBusMessage::createMethodCall(desktopPortalService(),
0196                                                               desktopPortalPath(),
0197                                                               QStringLiteral("org.freedesktop.impl.portal.AppChooser"),
0198                                                               QStringLiteral("ChooseApplicationPrivate"));
0199 
0200         QStringList urlStrings;
0201         for (const auto &url : urls) {
0202             urlStrings << url.toString();
0203         }
0204 
0205         QString lastChoice;
0206         KSharedConfig::Ptr xdgConfig;
0207         if (!mimeType.isEmpty()) {
0208             xdgConfig = KSharedConfig::openConfig(QStringLiteral("xdg-desktop-portal-kderc"), KConfig::NoGlobals);
0209             KConfigGroup appChooserGroup = xdgConfig->group("org.freedesktop.impl.portal.AppChooser");
0210             if (appChooserGroup.isValid()) {
0211                 KConfigGroup lastUsedGroup = appChooserGroup.group("LastChoice");
0212                 if (lastUsedGroup.isValid()) {
0213                     lastChoice = lastUsedGroup.readEntry(mimeType);
0214                 }
0215             }
0216         }
0217 
0218         message << windowId << urlStrings
0219                 << QVariantMap{
0220                        {QStringLiteral("ask"), true},
0221                        {QStringLiteral("last_choice"), lastChoice},
0222                    };
0223 
0224         QDBusPendingCall pendingCall = QDBusConnection::sessionBus().asyncCall(message, std::numeric_limits<int>::max());
0225         auto watcher = new QDBusPendingCallWatcher(pendingCall, this);
0226         connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, mimeType](QDBusPendingCallWatcher *watcher) {
0227             watcher->deleteLater();
0228 
0229             QDBusPendingReply<uint, QVariantMap> reply = *watcher;
0230             if (reply.isError()) {
0231                 qWarning() << "Couldn't get reply";
0232                 qWarning() << "Error: " << reply.error().message();
0233                 Q_EMIT canceled();
0234             } else {
0235                 if (reply.argumentAt<0>() == 0) {
0236                     const QString choice = reply.argumentAt<1>().value(QStringLiteral("choice")).toString();
0237                     Q_EMIT serviceSelected(KService::serviceByDesktopName(choice));
0238 
0239                     if (!mimeType.isEmpty()) {
0240                         KSharedConfig::Ptr xdgConfig = KSharedConfig::openConfig(QStringLiteral("xdg-desktop-portal-kderc"), KConfig::NoGlobals);
0241                         KConfigGroup appChooserGroup = xdgConfig->group("org.freedesktop.impl.portal.AppChooser");
0242                         KConfigGroup lastUsedGroup = appChooserGroup.group("LastChoice");
0243                         lastUsedGroup.writeEntry(mimeType, choice);
0244                     }
0245                 } else {
0246                     Q_EMIT canceled();
0247                 }
0248             }
0249         });
0250     }
0251 
0252 private:
0253     QWidget *const m_parentWidget;
0254 };
0255 
0256 class KIOUiDelegate : public KIO::JobUiDelegate
0257 {
0258 public:
0259     explicit KIOUiDelegate(KJobUiDelegate::Flags flags = AutoHandlingDisabled, QWidget *window = nullptr)
0260         : KIO::JobUiDelegate(KIO::JobUiDelegate::Version::V2, flags, window, {new KIOOpenWith(window, nullptr)})
0261     {
0262     }
0263 };
0264 
0265 class KIOUiFactory : public KIO::JobUiDelegateFactoryV2
0266 {
0267 public:
0268     KIOUiFactory() = default;
0269 
0270     KJobUiDelegate *createDelegate() const override
0271     {
0272         return new KIOUiDelegate;
0273     }
0274 
0275     KJobUiDelegate *createDelegate(KJobUiDelegate::Flags flags, QWidget *window) const override
0276     {
0277         return new KIOUiDelegate(flags, window);
0278     }
0279 };
0280 
0281 KdePlatformTheme::KdePlatformTheme()
0282 {
0283     loadSettings();
0284 
0285     // explicitly not KWindowSystem::isPlatformWayland to not include the kwin process
0286     if (QGuiApplication::platformName() == QLatin1String("wayland")) {
0287         m_kwaylandIntegration.reset(new KWaylandIntegration(this));
0288     }
0289 
0290 #if HAVE_X11
0291     if (KWindowSystem::isPlatformX11()) {
0292         m_x11Integration.reset(new X11Integration(this));
0293         m_x11Integration->init();
0294     }
0295 #endif
0296 
0297     // Don't show the titlebar "What's This?" help button for dialogs that
0298     // have any UI elements with help text, unless the window specifically
0299     // requests it. This is because KDE apps with help text will use the nice
0300     // contextual tooltips instead; we only want to ever see the titlebar button
0301     // to invoke the "What's This?" feature in 3rd-party Qt apps that have set
0302     // "What's This" help text and requested the button
0303     QCoreApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton, true);
0304 
0305     QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, false);
0306     setQtQuickControlsTheme();
0307 
0308     static KIOUiFactory factory;
0309     KIO::setDefaultJobUiDelegateFactoryV2(&factory);
0310 
0311     static KIOUiDelegate delegateExtension;
0312     KIO::setDefaultJobUiDelegateExtension(&delegateExtension);
0313 }
0314 
0315 KdePlatformTheme::~KdePlatformTheme()
0316 {
0317     delete m_fontsData;
0318     delete m_hints;
0319 }
0320 
0321 QVariant KdePlatformTheme::themeHint(QPlatformTheme::ThemeHint hintType) const
0322 {
0323     QVariant hint = m_hints->hint(hintType);
0324     if (hint.isValid()) {
0325         return hint;
0326     } else {
0327         return QPlatformTheme::themeHint(hintType);
0328     }
0329 }
0330 
0331 QIcon KdePlatformTheme::fileIcon(const QFileInfo &fileInfo, QPlatformTheme::IconOptions iconOptions) const
0332 {
0333     if (iconOptions.testFlag(DontUseCustomDirectoryIcons) && fileInfo.isDir()) {
0334         return QIcon::fromTheme(QLatin1String("inode-directory"));
0335     }
0336 
0337     return QIcon::fromTheme(KIO::iconNameForUrl(QUrl::fromLocalFile(fileInfo.absoluteFilePath())));
0338 }
0339 
0340 const QPalette *KdePlatformTheme::palette(Palette type) const
0341 {
0342     QPalette *palette = m_hints->palette(type);
0343     if (palette) {
0344         return palette;
0345     } else {
0346         return QPlatformTheme::palette(type);
0347     }
0348 }
0349 
0350 const QFont *KdePlatformTheme::font(Font type) const
0351 {
0352     KFontSettingsData::FontTypes fdtype;
0353     switch (type) {
0354     case SystemFont:
0355         fdtype = KFontSettingsData::GeneralFont;
0356         break;
0357     case MenuFont:
0358     case MenuBarFont:
0359     case MenuItemFont:
0360         fdtype = KFontSettingsData::MenuFont;
0361         break;
0362     case MessageBoxFont:
0363     case LabelFont:
0364     case TipLabelFont:
0365     case StatusBarFont:
0366     case PushButtonFont:
0367     case ItemViewFont:
0368     case ListViewFont:
0369     case HeaderViewFont:
0370     case ListBoxFont:
0371     case ComboMenuItemFont:
0372     case ComboLineEditFont:
0373         fdtype = KFontSettingsData::GeneralFont;
0374         break;
0375     case TitleBarFont:
0376     case MdiSubWindowTitleFont:
0377     case DockWidgetTitleFont:
0378         fdtype = KFontSettingsData::WindowTitleFont;
0379         break;
0380     case SmallFont:
0381     case MiniFont:
0382         fdtype = KFontSettingsData::SmallestReadableFont;
0383         break;
0384     case FixedFont:
0385         fdtype = KFontSettingsData::FixedFont;
0386         break;
0387     case ToolButtonFont:
0388         fdtype = KFontSettingsData::ToolbarFont;
0389         break;
0390     default:
0391         fdtype = KFontSettingsData::GeneralFont;
0392         break;
0393     }
0394 
0395     return m_fontsData->font(fdtype);
0396 }
0397 
0398 QIconEngine *KdePlatformTheme::createIconEngine(const QString &iconName) const
0399 {
0400     return new KIconEngine(iconName, KIconLoader::global());
0401 }
0402 
0403 void KdePlatformTheme::loadSettings()
0404 {
0405     m_fontsData = new KFontSettingsData;
0406     m_hints = new KHintsSettings;
0407 }
0408 
0409 QList<QKeySequence> KdePlatformTheme::keyBindings(QKeySequence::StandardKey key) const
0410 {
0411     switch (key) {
0412     case QKeySequence::HelpContents:
0413         return KStandardShortcut::shortcut(KStandardShortcut::Help);
0414     case QKeySequence::WhatsThis:
0415         return KStandardShortcut::shortcut(KStandardShortcut::WhatsThis);
0416     case QKeySequence::Open:
0417         return KStandardShortcut::shortcut(KStandardShortcut::Open);
0418     case QKeySequence::Close:
0419         return KStandardShortcut::shortcut(KStandardShortcut::Close);
0420     case QKeySequence::Save:
0421         return KStandardShortcut::shortcut(KStandardShortcut::Save);
0422     case QKeySequence::New:
0423         return KStandardShortcut::shortcut(KStandardShortcut::New);
0424     case QKeySequence::Cut:
0425         return KStandardShortcut::shortcut(KStandardShortcut::Cut);
0426     case QKeySequence::Copy:
0427         return KStandardShortcut::shortcut(KStandardShortcut::Copy);
0428     case QKeySequence::Paste:
0429         return KStandardShortcut::shortcut(KStandardShortcut::Paste);
0430     case QKeySequence::Undo:
0431         return KStandardShortcut::shortcut(KStandardShortcut::Undo);
0432     case QKeySequence::Redo:
0433         return KStandardShortcut::shortcut(KStandardShortcut::Redo);
0434     case QKeySequence::Back:
0435         return KStandardShortcut::shortcut(KStandardShortcut::Back);
0436     case QKeySequence::Forward:
0437         return KStandardShortcut::shortcut(KStandardShortcut::Forward);
0438     case QKeySequence::Refresh:
0439         return KStandardShortcut::shortcut(KStandardShortcut::Reload);
0440     case QKeySequence::ZoomIn:
0441         return KStandardShortcut::shortcut(KStandardShortcut::ZoomIn);
0442     case QKeySequence::ZoomOut:
0443         return KStandardShortcut::shortcut(KStandardShortcut::ZoomOut);
0444     case QKeySequence::Print:
0445         return KStandardShortcut::shortcut(KStandardShortcut::Print);
0446     case QKeySequence::Find:
0447         return KStandardShortcut::shortcut(KStandardShortcut::Find);
0448     case QKeySequence::FindNext:
0449         return KStandardShortcut::shortcut(KStandardShortcut::FindNext);
0450     case QKeySequence::FindPrevious:
0451         return KStandardShortcut::shortcut(KStandardShortcut::FindPrev);
0452     case QKeySequence::Replace:
0453         return KStandardShortcut::shortcut(KStandardShortcut::Replace);
0454     case QKeySequence::SelectAll:
0455         return KStandardShortcut::shortcut(KStandardShortcut::SelectAll);
0456     case QKeySequence::MoveToNextWord:
0457         return KStandardShortcut::shortcut(KStandardShortcut::ForwardWord);
0458     case QKeySequence::MoveToPreviousWord:
0459         return KStandardShortcut::shortcut(KStandardShortcut::BackwardWord);
0460     case QKeySequence::MoveToNextPage:
0461         return KStandardShortcut::shortcut(KStandardShortcut::Next);
0462     case QKeySequence::MoveToPreviousPage:
0463         return KStandardShortcut::shortcut(KStandardShortcut::Prior);
0464     case QKeySequence::MoveToStartOfLine:
0465         return KStandardShortcut::shortcut(KStandardShortcut::BeginningOfLine);
0466     case QKeySequence::MoveToEndOfLine:
0467         return KStandardShortcut::shortcut(KStandardShortcut::EndOfLine);
0468     case QKeySequence::MoveToStartOfDocument:
0469         return KStandardShortcut::shortcut(KStandardShortcut::Begin);
0470     case QKeySequence::MoveToEndOfDocument:
0471         return KStandardShortcut::shortcut(KStandardShortcut::End);
0472     case QKeySequence::SaveAs:
0473         return KStandardShortcut::shortcut(KStandardShortcut::SaveAs);
0474     case QKeySequence::Preferences:
0475         return KStandardShortcut::shortcut(KStandardShortcut::Preferences);
0476     case QKeySequence::Quit:
0477         return KStandardShortcut::shortcut(KStandardShortcut::Quit);
0478     case QKeySequence::FullScreen:
0479         return KStandardShortcut::shortcut(KStandardShortcut::FullScreen);
0480     case QKeySequence::Deselect:
0481         return KStandardShortcut::shortcut(KStandardShortcut::Deselect);
0482     case QKeySequence::DeleteStartOfWord:
0483         return KStandardShortcut::shortcut(KStandardShortcut::DeleteWordBack);
0484     case QKeySequence::DeleteEndOfWord:
0485         return KStandardShortcut::shortcut(KStandardShortcut::DeleteWordForward);
0486     case QKeySequence::NextChild:
0487         return KStandardShortcut::shortcut(KStandardShortcut::TabNext);
0488     case QKeySequence::PreviousChild:
0489         return KStandardShortcut::shortcut(KStandardShortcut::TabPrev);
0490     case QKeySequence::Delete:
0491         return KStandardShortcut::shortcut(KStandardShortcut::MoveToTrash);
0492     default:
0493         return QPlatformTheme::keyBindings(key);
0494     }
0495 }
0496 
0497 bool KdePlatformTheme::usePlatformNativeDialog(QPlatformTheme::DialogType type) const
0498 {
0499     return type == QPlatformTheme::FileDialog && qobject_cast<QApplication *>(QCoreApplication::instance());
0500 }
0501 
0502 QString KdePlatformTheme::standardButtonText(int button) const
0503 {
0504     switch (static_cast<QPlatformDialogHelper::StandardButton>(button)) {
0505     case QPlatformDialogHelper::NoButton:
0506         qWarning() << Q_FUNC_INFO << "Unsupported standard button:" << button;
0507         return QString();
0508     case QPlatformDialogHelper::Ok:
0509         return KStandardGuiItem::ok().text();
0510     case QPlatformDialogHelper::Save:
0511         return KStandardGuiItem::save().text();
0512     case QPlatformDialogHelper::SaveAll:
0513         return i18nc("@action:button", "Save All");
0514     case QPlatformDialogHelper::Open:
0515         return KStandardGuiItem::open().text();
0516     case QPlatformDialogHelper::Yes:
0517         return i18nc("@action:button", "&Yes");
0518     case QPlatformDialogHelper::YesToAll:
0519         return i18nc("@action:button", "Yes to All");
0520     case QPlatformDialogHelper::No:
0521         return i18nc("@action:button", "&No");
0522     case QPlatformDialogHelper::NoToAll:
0523         return i18nc("@action:button", "No to All");
0524     case QPlatformDialogHelper::Abort:
0525         // FIXME KStandardGuiItem::stop() doesn't seem right here
0526         return i18nc("@action:button", "Abort");
0527     case QPlatformDialogHelper::Retry:
0528         return i18nc("@action:button", "Retry");
0529     case QPlatformDialogHelper::Ignore:
0530         return i18nc("@action:button", "Ignore");
0531     case QPlatformDialogHelper::Close:
0532         return KStandardGuiItem::close().text();
0533     case QPlatformDialogHelper::Cancel:
0534         return KStandardGuiItem::cancel().text();
0535     case QPlatformDialogHelper::Discard:
0536         return KStandardGuiItem::discard().text();
0537     case QPlatformDialogHelper::Help:
0538         return KStandardGuiItem::help().text();
0539     case QPlatformDialogHelper::Apply:
0540         return KStandardGuiItem::apply().text();
0541     case QPlatformDialogHelper::Reset:
0542         return KStandardGuiItem::reset().text();
0543     case QPlatformDialogHelper::RestoreDefaults:
0544         return KStandardGuiItem::defaults().text();
0545     default:
0546         return QPlatformTheme::defaultStandardButtonText(button);
0547     }
0548 }
0549 
0550 QPlatformDialogHelper *KdePlatformTheme::createPlatformDialogHelper(QPlatformTheme::DialogType type) const
0551 {
0552     switch (type) {
0553     case QPlatformTheme::FileDialog:
0554         if (useXdgDesktopPortal()) {
0555             return new QXdgDesktopPortalFileDialog;
0556         }
0557         return new KDEPlatformFileDialogHelper;
0558     case QPlatformTheme::FontDialog:
0559     case QPlatformTheme::ColorDialog:
0560     case QPlatformTheme::MessageDialog:
0561     default:
0562         return nullptr;
0563     }
0564 }
0565 
0566 QPlatformSystemTrayIcon *KdePlatformTheme::createPlatformSystemTrayIcon() const
0567 {
0568     return new KDEPlatformSystemTrayIcon;
0569 }
0570 
0571 QPlatformMenuBar *KdePlatformTheme::createPlatformMenuBar() const
0572 {
0573     if (isDBusGlobalMenuAvailable()) {
0574 #ifndef KF6_TODO_DBUS_MENUBAR
0575         auto *menu = new QDBusMenuBar(const_cast<KdePlatformTheme *>(this));
0576 
0577         QObject::connect(menu, &QDBusMenuBar::windowChanged, menu, [this, menu](QWindow *newWindow, QWindow *oldWindow) {
0578             const QString &serviceName = QDBusConnection::sessionBus().baseService();
0579             const QString &objectPath = menu->objectPath();
0580 
0581             setMenuBarForWindow(oldWindow, {}, {});
0582             setMenuBarForWindow(newWindow, serviceName, objectPath);
0583         });
0584 
0585         return menu;
0586 #endif
0587     }
0588 
0589     return nullptr;
0590 }
0591 
0592 // Force QtQuickControls to use org.kde.desktop theme for QApplication,
0593 // and org.kde.breeze for QGuiApplication unless it's otherwise set.
0594 void KdePlatformTheme::setQtQuickControlsTheme()
0595 {
0596     // if the user is running only a QGuiApplication, explicitly unset the QQC1 desktop style and use
0597     // org.kde.breeze style if it's installed.
0598     if (!qobject_cast<QApplication *>(qApp)) {
0599         if (qgetenv("QT_QUICK_CONTROLS_1_STYLE").right(7) == "Desktop") {
0600             qunsetenv("QT_QUICK_CONTROLS_1_STYLE");
0601         }
0602         QQuickStyle::setStyle(QLatin1String("org.kde.breeze"));
0603         return;
0604     }
0605     // if the user has explicitly set something else, don't meddle
0606     // Also ignore the default Fusion style
0607     if (!QQuickStyle::name().isEmpty() && QQuickStyle::name() != QLatin1String("Fusion")) {
0608         return;
0609     }
0610     QQuickStyle::setStyle(QLatin1String("org.kde.desktop"));
0611 }
0612 
0613 bool KdePlatformTheme::useXdgDesktopPortal()
0614 {
0615     static bool usePortal = qEnvironmentVariableIntValue("PLASMA_INTEGRATION_USE_PORTAL") == 1;
0616     return usePortal;
0617 }
0618 
0619 inline bool windowRelevantForGlobalMenu(QWindow *window)
0620 {
0621     return !(window->type() & Qt::WindowType::Popup);
0622 }
0623 
0624 void KdePlatformTheme::globalMenuBarExistsNow()
0625 {
0626 #ifndef KF6_TODO_DBUS_MENUBAR
0627     const QString &serviceName = QDBusConnection::sessionBus().baseService();
0628     const QString &objectPath = QDBusMenuBar::globalMenuBar()->objectPath();
0629 
0630     for (auto *window : qApp->topLevelWindows()) {
0631         if (QDBusMenuBar::menuBarForWindow(window))
0632             continue;
0633         if (!windowRelevantForGlobalMenu(window))
0634             return;
0635 
0636         setMenuBarForWindow(window, serviceName, objectPath);
0637     }
0638 #endif
0639 }
0640 
0641 void KdePlatformTheme::windowCreated(QWindow *window)
0642 {
0643 #ifndef KF6_TODO_DBUS_MENUBAR
0644     if (!QDBusMenuBar::globalMenuBar())
0645         return;
0646 
0647     if (QDBusMenuBar::menuBarForWindow(window))
0648         return;
0649 
0650     const QString &serviceName = QDBusConnection::sessionBus().baseService();
0651     const QString &objectPath = QDBusMenuBar::globalMenuBar()->objectPath();
0652 
0653     setMenuBarForWindow(window, serviceName, objectPath);
0654 #endif
0655 }
0656 
0657 void KdePlatformTheme::globalMenuBarNoLongerExists()
0658 {
0659 #ifndef KF6_TODO_DBUS_MENUBAR
0660     for (auto *window : qApp->topLevelWindows()) {
0661         if (QDBusMenuBar::menuBarForWindow(window))
0662             continue;
0663         if (!windowRelevantForGlobalMenu(window))
0664             return;
0665 
0666         setMenuBarForWindow(window, {}, {});
0667     }
0668 #endif
0669 }
0670 
0671 void KdePlatformTheme::setMenuBarForWindow(QWindow *window, const QString &serviceName, const QString &objectPath) const
0672 {
0673     if (!window)
0674         return;
0675 
0676     if (m_x11Integration) {
0677         m_x11Integration->setWindowProperty(window, s_x11AppMenuServiceNamePropertyName, serviceName.toUtf8());
0678         m_x11Integration->setWindowProperty(window, s_x11AppMenuObjectPathPropertyName, objectPath.toUtf8());
0679     }
0680 
0681     if (m_kwaylandIntegration) {
0682         m_kwaylandIntegration->setAppMenu(window, serviceName, objectPath);
0683     }
0684 }
0685 #include "kdeplatformtheme.moc"