diff --git a/src/platformtheme/kdemactheme.h b/src/platformtheme/kdemactheme.h index 08fafa4..126bfe5 100644 --- a/src/platformtheme/kdemactheme.h +++ b/src/platformtheme/kdemactheme.h @@ -1,73 +1,75 @@ /* This file is part of the KDE libraries * Copyright 2013 Kevin Ottens * Copyright 2015 René J.V. Bertin * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License or ( at * your option ) version 3 or, at the discretion of KDE e.V. ( which shall * act as a proxy as in section 14 of the GPLv3 ), any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KDEMACTHEME_H #define KDEMACTHEME_H #include "kdeplatformtheme.h" #include "kfontsettingsdatamac.h" #ifdef ADD_MENU_KEY #include #endif class KHintsSettingsMac; class QIconEngine; class KdeMacThemeEventFilter; class KdeMacTheme : public KdePlatformTheme { public: KdeMacTheme(); ~KdeMacTheme(); // KdeMacTheme must provide platform menu methods or else there will be no menus QPlatformMenuItem* createPlatformMenuItem() const override; QPlatformMenu* createPlatformMenu() const override; QPlatformMenuBar* createPlatformMenuBar() const override; QVariant themeHint(ThemeHint hint) const override; const QPalette *palette(Palette type = SystemPalette) const override; const QFont *font(Font type) const override; QList keyBindings(QKeySequence::StandardKey key) const override; QPlatformDialogHelper *createPlatformDialogHelper(DialogType type) const override; bool usePlatformNativeDialog(DialogType type) const override; QString standardButtonText(int button) const override; QPlatformSystemTrayIcon *createPlatformSystemTrayIcon() const override; + bool verbose; + protected: void loadSettings(); KFontSettingsDataMac::FontTypes fontType(Font type) const; private: KHintsSettingsMac *m_hints; KFontSettingsDataMac *m_fontsData; // this will hold the instance of the native theme that will be used as a fallback QPlatformTheme *nativeTheme; // this will hold an instance of a class with Qt and/or native event filters: KdeMacThemeEventFilter *m_eventFilter; }; #endif // KDEMACTHEME_H diff --git a/src/platformtheme/kdemactheme.mm b/src/platformtheme/kdemactheme.mm index ee9b908..7cdbe2c 100644 --- a/src/platformtheme/kdemactheme.mm +++ b/src/platformtheme/kdemactheme.mm @@ -1,718 +1,725 @@ /* This file is part of the KDE libraries * Copyright 2013 Kevin Ottens * Copyright 2013 Aleix Pol Gonzalez * Copyright 2014 Lukáš Tinkl * Copyright 2015 René J.V. Bertin * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License or ( at * your option ) version 3 or, at the discretion of KDE e.V. ( which shall * act as a proxy as in section 14 of the GPLv3 ), any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ // #define ADD_MENU_KEY #include "config-platformtheme.h" #include "kdemactheme.h" #include "kfontsettingsdatamac.h" #include "khintssettingsmac.h" #include "kdeplatformfiledialoghelper.h" #include "kdeplatformsystemtrayicon.h" #include "platformtheme_logging.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifndef QT_NO_GESTURES #include #include #include #include #include #include #include #include #include #include #include #include #endif // instantiating the native platform theme requires the use of private APIs #include #include #include #include #include #include #include #include #include #ifdef USE_PLCRASHREPORTER #include #endif // [NSEvent modifierFlags] keycodes: // LeftShift=131330 // RightShift=131332 // LeftAlt=524576 // RightAlt=524608 // LeftCommand=1048840 // RightCommand=1048848 // RightCommand+RightAlt=1573200 static QString platformName = QStringLiteral(""); // #define TAPANDHOLD_DEBUG class KdeMacThemeEventFilter : public QObject { Q_OBJECT public: KdeMacThemeEventFilter(QObject *parent=nullptr) : QObject(parent) { qtNativeFilter = new QNativeEventFilter; } virtual ~KdeMacThemeEventFilter() { delete qtNativeFilter; qtNativeFilter = nullptr; } class QNativeEventFilter : public QAbstractNativeEventFilter { public: virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) override; }; QNativeEventFilter *qtNativeFilter; #ifdef ADD_MENU_KEY const static int keyboardMonitorMask = NSKeyDownMask | NSKeyUpMask | NSFlagsChangedMask; NSEvent *nativeEventHandler(void *message); id m_keyboardMonitor; bool enabled; NSTimeInterval disableTime; #endif #ifndef QT_NO_GESTURES inline bool handleGestureForObject(const QObject *obj) const { // this function is called with an that is or inherits a QWidget const QPushButton *btn = qobject_cast(obj); const QToolButton *tbtn = qobject_cast(obj); if (tbtn) { return !tbtn->menu(); } else if (btn) { return !btn->menu(); } else { return (qobject_cast(obj) || qobject_cast(obj) // || obj->inherits("QTabBar") || obj->inherits("QTabWidget") || qobject_cast(obj) || qobject_cast(obj) || qobject_cast(obj) // this catches items in directory lists and the like || obj->objectName() == QStringLiteral("qt_scrollarea_viewport") || obj->inherits("KateViewInternal")); // Konsole windows can be found as obj->inherits("Konsole::TerminalDisplay") but // for some reason Konsole doesn't respond to synthetic ContextMenu events } } #endif int pressedMouseButtons() { return [NSEvent pressedMouseButtons]; } bool eventFilter(QObject *obj, QEvent *event) override { #ifndef QT_NO_GESTURES static QVariant qTrue(true), qFalse(false); // #ifdef TAPANDHOLD_DEBUG // if (qEnvironmentVariableIsSet("TAPANDHOLD_CONTEXTMENU_DEBUG")) { // QVariant isGrabbed = obj->property("OurTaHGestureActive"); // if (isGrabbed.isValid() && isGrabbed.toBool()) { // qCWarning(PLATFORMTHEME) << "event=" << event << "grabbed obj=" << obj; // } // } // #endif switch (event->type()) { case QEvent::MouseButtonPress: { QMouseEvent *me = dynamic_cast(event); if (me->button() == Qt::LeftButton && me->modifiers() == Qt::NoModifier) { QWidget *w = qobject_cast(obj); if (w && handleGestureForObject(obj)) { QVariant isGrabbed = obj->property("OurTaHGestureActive"); if (!(isGrabbed.isValid() && isGrabbed.toBool())) { // ideally we'd check first - if we could. // storing all grabbed QObjects is potentially dangerous since we won't // know when they go stale. w->grabGesture(Qt::TapAndHoldGesture); // accept this event but resend it so that the 1st mousepress // can also trigger a tap-and-hold! obj->setProperty("OurTaHGestureActive", qTrue); #ifdef TAPANDHOLD_DEBUG if (qEnvironmentVariableIsSet("TAPANDHOLD_CONTEXTMENU_DEBUG")) { qCWarning(PLATFORMTHEME) << "event=" << event << "grabbing obj=" << obj << "parent=" << obj->parent(); } #endif if (!m_grabbing.contains(obj)) { QMouseEvent relay(*me); me->accept(); m_grabbing.insert(obj); int ret = QCoreApplication::sendEvent(obj, &relay); m_grabbing.remove(obj); return ret; } } } #ifdef TAPANDHOLD_DEBUG else if (w && qEnvironmentVariableIsSet("TAPANDHOLD_CONTEXTMENU_DEBUG")) { qCWarning(PLATFORMTHEME) << "event=" << event << "obj=" << obj << "parent=" << obj->parent(); } #endif } // NB: don't "eat" the event if no action was taken! break; } // case QEvent::Paint: // if (pressedMouseButtons() == 1) { // // ignore QPaintEvents when the left mouse button (1<<0) is being held // break; // } else { // // not holding the left mouse button; fall through to check if // // maybe we should cancel a click-and-hold-opens-contextmenu process. // } case QEvent::MouseMove: case QEvent::MouseButtonRelease: { QVariant isGrabbed = obj->property("OurTaHGestureActive"); if (isGrabbed.isValid() && isGrabbed.toBool()) { #ifdef TAPANDHOLD_DEBUG qCWarning(PLATFORMTHEME) << "event=" << event << "obj=" << obj << "parent=" << obj->parent() << "grabbed=" << obj->property("OurTaHGestureActive"); #endif obj->setProperty("OurTaHGestureActive", qFalse); } break; } case QEvent::Gesture: { QGestureEvent *gEvent = static_cast(event); if (QTapAndHoldGesture *heldTap = static_cast(gEvent->gesture(Qt::TapAndHoldGesture))) { if (heldTap->state() == Qt::GestureFinished) { QVariant isGrabbed = obj->property("OurTaHGestureActive"); if (isGrabbed.isValid() && isGrabbed.toBool() && pressedMouseButtons() == 1) { QWidget *w = qobject_cast(obj); // user clicked and held a button, send it a simulated ContextMenuEvent // but send a simulated buttonrelease event first. QPoint localPos = w->mapFromGlobal(heldTap->position().toPoint()); QContextMenuEvent ce(QContextMenuEvent::Mouse, localPos, heldTap->hotSpot().toPoint()); // don't send a ButtonRelease event to Q*Buttons because we don't want to trigger them if (QPushButton *btn = qobject_cast(obj)) { btn->setDown(false); obj->setProperty("OurTaHGestureActive", qFalse); } else if (QToolButton *tbtn = qobject_cast(obj)) { tbtn->setDown(false); obj->setProperty("OurTaHGestureActive", qFalse); } else { QMouseEvent me(QEvent::MouseButtonRelease, localPos, Qt::LeftButton, Qt::LeftButton, Qt::NoModifier); #ifdef TAPANDHOLD_DEBUG qCWarning(PLATFORMTHEME) << "Sending" << &me; #endif // we'll be unsetting OurTaHGestureActive in the MouseButtonRelease handler above QCoreApplication::sendEvent(obj, &me); } qCWarning(PLATFORMTHEME) << "Sending" << &ce << "to" << obj << "because of" << gEvent << "isGrabbed=" << isGrabbed; bool ret = QCoreApplication::sendEvent(obj, &ce); gEvent->accept(); qCWarning(PLATFORMTHEME) << "\tsendEvent" << &ce << "returned" << ret; return true; } } } break; } #ifdef TAPANDHOLD_DEBUG case QEvent::ContextMenu: if (qEnvironmentVariableIsSet("TAPANDHOLD_CONTEXTMENU_DEBUG")) { qCWarning(PLATFORMTHEME) << "event=" << event << "obj=" << obj << "parent=" << obj->parent() << "grabbed=" << obj->property("OurTaHGestureActive"); } break; #endif default: break; } #endif return false; } #ifndef QT_NO_GESTURES QSet m_grabbing; #endif }; bool KdeMacThemeEventFilter::QNativeEventFilter::nativeEventFilter(const QByteArray&, void *message, long *) { NSEvent *event = static_cast(message); switch ([event type]) { #if defined(MAC_OS_X_VERSION_10_12) && (MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_12) case NSEventTypeSystemDefined: #else case NSSystemDefined: #endif { // borrowed with thanks from QMPlay2 const int keyCode = ([event data1] & 0xFFFF0000) >> 16; const int keyFlags = ([event data1] & 0x0000FFFF); const int keyState = (((keyFlags & 0xFF00) >> 8) == 0xA); // qCWarning(PLATFORMTHEME) << QStringLiteral("NSSystemDefined event keyCode=%1 keyFlags=%2 keyState=%3").arg(keyCode).arg(keyFlags).arg(keyState); if (keyState == 1) { int qtKey = 0; switch (keyCode) { case NX_KEYTYPE_PLAY: qtKey = Qt::Key_MediaTogglePlayPause; break; case NX_KEYTYPE_NEXT: case NX_KEYTYPE_FAST: qtKey = Qt::Key_MediaNext; break; case NX_KEYTYPE_PREVIOUS: case NX_KEYTYPE_REWIND: qtKey = Qt::Key_MediaPrevious; break; } if (qtKey) { QKeyEvent mediaKeyEvent(QEvent::KeyPress, qtKey, Qt::NoModifier); qCWarning(PLATFORMTHEME) << "Sending mediaKeyEvent" << &mediaKeyEvent; QCoreApplication::sendEvent(qApp, &mediaKeyEvent); return false; } } break; } } return false; } #ifdef ADD_MENU_KEY NSEvent *KdeMacThemeEventFilter::nativeEventHandler(void *message) { NSEvent *event = static_cast(message); switch ([event type]) { case NSFlagsChanged: { switch ([event modifierFlags]) { case 524608: case 1048848: enabled = false; disableTime = [event timestamp]; break; case 1573200: // simultaneous press (i.e. within <= 0.1s) of just the right Command and Option keys: if (enabled || [event timestamp] - disableTime <= 0.1) { enabled = true; // qCWarning(PLATFORMTHEME) << Q_FUNC_INFO << "event=" << QString::fromNSString([event description]) // << "modifierFlags=" << [event modifierFlags] << "keyCode=" << [event keyCode]; const unichar menuKeyCode = static_cast(NSMenuFunctionKey); NSString *menuKeyString = [NSString stringWithCharacters:&menuKeyCode length:1]; NSEvent *menuKeyEvent = [NSEvent keyEventWithType:NSKeyDown location:[event locationInWindow] modifierFlags:([event modifierFlags] & ~(NSCommandKeyMask|NSAlternateKeyMask)) timestamp:[event timestamp] windowNumber:[event windowNumber] context:nil characters:menuKeyString charactersIgnoringModifiers:menuKeyString isARepeat:NO // the keyCode must be an 8-bit value so not to be confounded with the Unicode value. // Judging from Carbon/Events.h 0x7f is unused. keyCode:0x7f]; // qCWarning(PLATFORMTHEME) << "new event:" << QString::fromNSString([menuKeyEvent description]); return menuKeyEvent; } // fall through! default: // any other flag change reenables the menukey emulation. enabled = true; break; } break; } // case NSKeyDown: { // qCWarning(PLATFORMTHEME) << Q_FUNC_INFO << "event=" << QString::fromNSString([event description]) // << "key=" << [event keyCode] // << "modifierFlags=" << [event modifierFlags] << "chars=" << QString::fromNSString([event characters]) // << "charsIgnMods=" << QString::fromNSString([event charactersIgnoringModifiers]); // break; // } } // standard event processing return event; } #endif //ADD_MENU_KEY static void warnNoNativeTheme() { // Make sure the warning appears somewhere. qCWarning(PLATFORMTHEME) isn't guaranteed to be of use when we're // not called from a terminal session and it's probably too early to try an alert dialog. // NSLog() will log to system.log, but also to the terminal. if (platformName.contains(QLatin1String("cocoa"))) { NSLog(@"The %s platform theme plugin is being used and the native theme for the %@ platform failed to load.\n" "Applications will function but lack functionality available only through the native theme,\n" "including the menu bar at the top of the screen(s).", PLATFORM_PLUGIN_THEME_NAME, platformName.toNSString()); } else { NSLog(@"The %s platform theme plugin is being used and the native theme for the %@ platform failed to load.\n" "Applications will function but lack functionality available only through the native theme.", PLATFORM_PLUGIN_THEME_NAME, platformName.toNSString()); } } /* ============ How we get here: (lldb) bt * thread #1: tid = 0x2e3a6be, 0x000000010a481454 KDEPlatformTheme.so`KdeMacTheme::KdeMacTheme(this=0x0000000103a3d830) + 4 at kdemactheme.mm:72, queue = 'com.apple.main-thread', stop reason = breakpoint 1.2 * frame #0: 0x000000010a481454 KDEPlatformTheme.so`KdeMacTheme::KdeMacTheme(this=0x0000000103a3d830) + 4 at kdemactheme.mm:72 frame #1: 0x000000010a48686b KDEPlatformTheme.so`CocoaPlatformThemePlugin::create(this=, key=, paramList=) + 27 at main_mac.cpp:53 frame #2: 0x00000001008c85b8 QtGui`QPlatformThemeFactory::create(QString const&, QString const&) [inlined] QPlatformTheme* qLoadPlugin(loader=, key=0x0000000103a406b0, args=0x0000000103a3d710) + 60 at qfactoryloader_p.h:103 frame #3: 0x00000001008c857c QtGui`QPlatformThemeFactory::create(key=, platformPluginPath=) + 396 at qplatformthemefactory.cpp:73 frame #4: 0x00000001008d31bb QtGui`QGuiApplicationPrivate::createPlatformIntegration() [inlined] QLatin1String::QLatin1String(this=0x0000000103b17a00, pluginArgument=0x0000000103b17a00, this=0x0000000103b17a00, platformPluginPath=0x000000010134fa90, s=0x0000000103b19e50, platformThemeName=0x000000010134fa90, argc=, argv=) + 1357 at qguiapplication.cpp:1135 frame #5: 0x00000001008d2c6e QtGui`QGuiApplicationPrivate::createPlatformIntegration(this=0x0000000103c0a6a0) + 1950 at qguiapplication.cpp:1257 frame #6: 0x00000001008d3adb QtGui`QGuiApplicationPrivate::createEventDispatcher(this=) + 27 at qguiapplication.cpp:1274 frame #7: 0x00000001010e0098 QtCore`QCoreApplicationPrivate::init(this=0x0000000103c0a6a0) + 1832 at qcoreapplication.cpp:794 frame #8: 0x00000001008cfce1 QtGui`QGuiApplicationPrivate::init(this=0x0000000103c0a6a0) + 49 at qguiapplication.cpp:1297 frame #9: 0x000000010001e90e QtWidgets`QApplicationPrivate::init(this=0x0000000103c0a6a0) + 14 at qapplication.cpp:583 ============ */ KdeMacTheme::KdeMacTheme() { if (strcasecmp(QT_VERSION_STR, qVersion())) { NSLog(@"Warning: the %s platform theme plugin for Mac was built against Qt %s but is running with Qt %s!", PLATFORM_PLUGIN_THEME_NAME, QT_VERSION_STR, qVersion()); } // first things first: instruct Qt not to use the Mac-style toplevel menubar // if we are not using the Cocoa QPA plugin (but the XCB QPA instead). platformName = QGuiApplication::platformName(); if (!platformName.contains(QLatin1String("cocoa"))) { QCoreApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, true); QCoreApplication::setAttribute(Qt::AA_MacDontSwapCtrlAndMeta, true); } QPlatformIntegration *pi = QGuiApplicationPrivate::platformIntegration(); if (pi) { nativeTheme = pi->createPlatformTheme(platformName); } else { nativeTheme = Q_NULLPTR; } + verbose = qEnvironmentVariableIsSet("QT_QPA_PLATFORMTHEME_VERBOSE"); if (!nativeTheme) { warnNoNativeTheme(); - } else if (qEnvironmentVariableIsSet("QT_QPA_PLATFORMTHEME_VERBOSE")) { + } else if (verbose) { qCWarning(PLATFORMTHEME) << Q_FUNC_INFO << "loading platform theme plugin" << QLatin1String(PLATFORM_PLUGIN_THEME_NAME) << "for platform" << platformName; } m_fontsData = Q_NULLPTR; m_hints = Q_NULLPTR; loadSettings(); m_eventFilter = new KdeMacThemeEventFilter; #ifndef QT_NO_GESTURES qApp->installEventFilter(m_eventFilter); #endif #ifdef ADD_MENU_KEY m_eventFilter->m_keyboardMonitor = 0; @autoreleasepool { // set up a keyboard event monitor m_eventFilter->m_keyboardMonitor = [NSEvent addLocalMonitorForEventsMatchingMask:KdeMacThemeEventFilter::keyboardMonitorMask handler:^(NSEvent* event) { return m_eventFilter->nativeEventHandler(event); }]; } if (m_eventFilter->m_keyboardMonitor) { m_eventFilter->enabled = true; } else { qCWarning(PLATFORMTHEME) << Q_FUNC_INFO << "Could not create a global keyboard monitor"; } #endif // for some reason our Qt native event filter is apparently never called. qApp->installNativeEventFilter(m_eventFilter->qtNativeFilter); #ifdef USE_PLCRASHREPORTER static PLCrashReporter *crashReporter = nil; if (!crashReporter) { crashReporter = [[PLCrashReporter alloc] initWithConfiguration:[PLCrashReporterConfig defaultConfiguration]]; } NSError *error; if ([crashReporter hasPendingCrashReport]) @autoreleasepool { NSData *crashData; PLCrashReport *report = nil; crashData = [crashReporter loadPendingCrashReportDataAndReturnError: &error]; if (crashData) { report = [[[PLCrashReport alloc] initWithData:crashData error:&error] autorelease]; } if (report) { // report could be sent to KAboutData::applicationData().bugAddress() // using QDesktopServices::openUrl("mailto:") qCWarning(PLATFORMTHEME) << qApp->applicationName() << "crashed on" << QString::fromNSString([report.systemInfo.timestamp description]); qCWarning(PLATFORMTHEME) << "\twith signal" << QString::fromNSString(report.signalInfo.name) << "code" << QString::fromNSString(report.signalInfo.code) << "at address" << report.signalInfo.address; } [crashReporter purgePendingCrashReport]; } if (![crashReporter enableCrashReporterAndReturnError: &error]) { NSLog(@"Warning: Could not enable crash reporter: %@", error); } #endif } KdeMacTheme::~KdeMacTheme() { delete nativeTheme; if (m_eventFilter) { qApp->removeNativeEventFilter(m_eventFilter->qtNativeFilter); #ifdef ADD_MENU_KEY m_eventFilter->enabled = false; if (m_eventFilter->m_keyboardMonitor) { @autoreleasepool { [NSEvent removeMonitor:m_eventFilter->m_keyboardMonitor]; } } #endif } delete m_eventFilter; m_eventFilter = 0; } QPlatformMenuItem* KdeMacTheme::createPlatformMenuItem() const { if (nativeTheme) { return nativeTheme->createPlatformMenuItem(); } else { warnNoNativeTheme(); return QPlatformTheme::createPlatformMenuItem(); } } QPlatformMenu* KdeMacTheme::createPlatformMenu() const { if (nativeTheme) { return nativeTheme->createPlatformMenu(); } else { warnNoNativeTheme(); return QPlatformTheme::createPlatformMenu(); } } QPlatformMenuBar* KdeMacTheme::createPlatformMenuBar() const { if (nativeTheme) { return nativeTheme->createPlatformMenuBar(); } else { warnNoNativeTheme(); return QPlatformTheme::createPlatformMenuBar(); } } QVariant KdeMacTheme::themeHint(QPlatformTheme::ThemeHint hintType) const { QVariant hint = m_hints->hint(hintType); if (hint.isValid()) { + if (verbose) { + qCWarning(PLATFORMTHEME) << "themeHint" << hintType << ":" << hint; + } return hint; } else { if (nativeTheme) { + if (verbose) { + qCWarning(PLATFORMTHEME) << "Using native theme for themeHint" << hintType << ":" << nativeTheme->themeHint(hintType); + } return nativeTheme->themeHint(hintType); } return QPlatformTheme::themeHint(hintType); } } const QPalette *KdeMacTheme::palette(Palette type) const { QPalette *palette = m_hints->palette(type); if (palette) { return palette; } else { if (nativeTheme) { return nativeTheme->palette(type); } return QPlatformTheme::palette(type); } } KFontSettingsDataMac::FontTypes KdeMacTheme::fontType(QPlatformTheme::Font type) const { KFontSettingsDataMac::FontTypes ftype; switch (type) { default: ftype = KFontSettingsDataMac::FontTypes(KdePlatformTheme::fontType(type)); break; case MessageBoxFont: ftype = KFontSettingsDataMac::MessageBoxFont; break; } return ftype; } const QFont *KdeMacTheme::font(Font type) const { // when using the platform-default fonts, try returning a bold version of the // standard system font; it's the only one where Qt/OS X really deviates. const QFont *qf = m_fontsData->font(fontType(type)); if (!qf && nativeTheme) { qf = nativeTheme->font(type); // if (qf) { // qCWarning(PLATFORMTHEME) << "native font for type" << type << "=role" << fontType(type) << ":" << *qf; // } else { // qCWarning(PLATFORMTHEME) << "native font for type" << type << "=role" << fontType(type) << ": NULL"; // } } return qf; } void KdeMacTheme::loadSettings() { if (!m_fontsData) { - m_fontsData = new KFontSettingsDataMac; + m_fontsData = new KFontSettingsDataMac(this); } if (!m_hints) { - m_hints = new KHintsSettingsMac; + m_hints = new KHintsSettingsMac(this); } } QList KdeMacTheme::keyBindings(QKeySequence::StandardKey key) const { // return a native keybinding if we can determine what that is if (nativeTheme) { return nativeTheme->keyBindings(key); } // or else we return whatever KDE applications expect elsewhere return KdePlatformTheme::keyBindings(key); } bool KdeMacTheme::usePlatformNativeDialog(QPlatformTheme::DialogType type) const { #ifdef KDEMACTHEME_PREFER_NATIVE_DIALOGS if (nativeTheme) { return nativeTheme->usePlatformNativeDialog(type); } #endif #ifndef KDEMACTHEME_NEVER_NATIVE_DIALOGS return type == QPlatformTheme::FileDialog && qobject_cast(QCoreApplication::instance()); #else return false; #endif } QString KdeMacTheme::standardButtonText(int button) const { // assume that button text is a domain where cross-platform application // coherence primes over native platform look and feel. IOW, function over form. // It's impossible to use the parent's method since we use // the nativeTheme in the default case switch (static_cast(button)) { case QPlatformDialogHelper::NoButton: qCWarning(PLATFORMTHEME) << Q_FUNC_INFO << "Unsupported standard button:" << button; return QString(); case QPlatformDialogHelper::Ok: return KStandardGuiItem::ok().text(); case QPlatformDialogHelper::Save: return KStandardGuiItem::save().text(); case QPlatformDialogHelper::SaveAll: return i18nc("@action:button", "Save All"); case QPlatformDialogHelper::Open: return KStandardGuiItem::open().text(); case QPlatformDialogHelper::Yes: return KStandardGuiItem::yes().text(); case QPlatformDialogHelper::YesToAll: return i18nc("@action:button", "Yes to All"); case QPlatformDialogHelper::No: return KStandardGuiItem::no().text(); case QPlatformDialogHelper::NoToAll: return i18nc("@action:button", "No to All"); case QPlatformDialogHelper::Abort: // FIXME KStandardGuiItem::stop() doesn't seem right here return i18nc("@action:button", "Abort"); case QPlatformDialogHelper::Retry: return i18nc("@action:button", "Retry"); case QPlatformDialogHelper::Ignore: return i18nc("@action:button", "Ignore"); case QPlatformDialogHelper::Close: return KStandardGuiItem::close().text(); case QPlatformDialogHelper::Cancel: return KStandardGuiItem::cancel().text(); case QPlatformDialogHelper::Discard: return KStandardGuiItem::discard().text(); case QPlatformDialogHelper::Help: return KStandardGuiItem::help().text(); case QPlatformDialogHelper::Apply: return KStandardGuiItem::apply().text(); case QPlatformDialogHelper::Reset: return KStandardGuiItem::reset().text(); case QPlatformDialogHelper::RestoreDefaults: return KStandardGuiItem::defaults().text(); default: if (nativeTheme) { // something not foreseen by Qt/KDE: now see if OS X // has an opinion about the text. return nativeTheme->standardButtonText(button); } return QPlatformTheme::defaultStandardButtonText(button); } } QPlatformDialogHelper *KdeMacTheme::createPlatformDialogHelper(QPlatformTheme::DialogType type) const { #ifdef KDEMACTHEME_PREFER_NATIVE_DIALOGS // always prefer native dialogs // NOTE: somehow, the "don't use native dialog" option that Qt's example "standarddialogs" // provides does not modify our usePlatformNativeDialog() return value, but *does* cause // a Qt dialog to be created instead of the native one. Weird. if (nativeTheme && !qEnvironmentVariableIsSet("PREFER_KDE_DIALOGS")) { return nativeTheme->createPlatformDialogHelper(type); } #endif QPlatformDialogHelper *helper = KdePlatformTheme::createPlatformDialogHelper(type); if (helper) { return helper; } else { if (nativeTheme) { helper = nativeTheme->createPlatformDialogHelper(type); } return helper ? helper : QPlatformTheme::createPlatformDialogHelper(type); } } QPlatformSystemTrayIcon *KdeMacTheme::createPlatformSystemTrayIcon() const { if (nativeTheme) { return nativeTheme->createPlatformSystemTrayIcon(); } // TODO: figure out if it makes sense to return something other than // nativeTheme->createPlatformSystemTrayIcon() or even NULL return KdePlatformTheme::createPlatformSystemTrayIcon(); } #include "kdemactheme.moc" diff --git a/src/platformtheme/kfontsettingsdata.cpp b/src/platformtheme/kfontsettingsdata.cpp index c2eec44..6e43edd 100644 --- a/src/platformtheme/kfontsettingsdata.cpp +++ b/src/platformtheme/kfontsettingsdata.cpp @@ -1,133 +1,141 @@ /* This file is part of the KDE libraries Copyright (C) 2000, 2006 David Faure Copyright 2008 Friedrich W. H. Kossebau Copyright 2013 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #undef QT_NO_CAST_FROM_ASCII #include "kfontsettingsdata.h" +#include "platformtheme_logging.h" + #include #include #include #include #ifdef DBUS_SUPPORT_ENABLED #include #include #endif #include #include #include KFontSettingsData::KFontSettingsData() - : QObject(0) + : QObject(nullptr) { #ifdef DBUS_SUPPORT_ENABLED QMetaObject::invokeMethod(this, "delayedDBusConnects", Qt::QueuedConnection); #endif for (int i = 0; i < FontTypesCount; ++i) { mFonts[i] = 0; } } KFontSettingsData::~KFontSettingsData() { for (int i = 0; i < FontTypesCount; ++i) { delete mFonts[i]; } } // NOTE: keep in sync with plasma-desktop/kcms/fonts/fonts.cpp static const char GeneralId[] = "General"; static const char DefaultFont[] = "Lucida Grande"; static const KFontData DefaultFontData[KFontSettingsData::FontTypesCount] = { { GeneralId, "font", DefaultFont, 10, -1, QFont::SansSerif, "Regular" }, { GeneralId, "fixed", "Monaco", 9, -1, QFont::Monospace, "Regular" }, { GeneralId, "toolBarFont", DefaultFont, 9, -1, QFont::SansSerif, "Regular" }, { GeneralId, "menuFont", DefaultFont, 10, -1, QFont::SansSerif, "Regular" }, { "WM", "activeFont", DefaultFont, 10, -1, QFont::SansSerif, "Regular" }, { GeneralId, "taskbarFont", DefaultFont, 10, -1, QFont::SansSerif, "Regular" }, { GeneralId, "smallestReadableFont", DefaultFont, 8, -1, QFont::SansSerif, "Regular" } }; KSharedConfigPtr &KFontSettingsData::kdeGlobals() { if (!mKdeGlobals) { if (qEnvironmentVariableIsSet("QT_QPA_PLATFORMTHEME_CONFIG_FILE")) { - mKdeGlobals = KSharedConfig::openConfig(qgetenv("QT_QPA_PLATFORMTHEME_CONFIG_FILE"), KConfig::NoGlobals); + const auto fname(qgetenv("QT_QPA_PLATFORMTHEME_CONFIG_FILE")); + mKdeGlobals = KSharedConfig::openConfig(fname, KConfig::NoGlobals); + const auto foundFile = QStandardPaths::locate(mKdeGlobals->locationType(), mKdeGlobals->name()); + if (foundFile.isEmpty()) { + qCWarning(PLATFORMTHEME) << "WARNING: could not open config file" << fname + << "in" << QStandardPaths::standardLocations(mKdeGlobals->locationType()); + } } else { mKdeGlobals = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::NoGlobals); } } return mKdeGlobals; } QFont *KFontSettingsData::font(FontTypes fontType) { QFont *cachedFont = mFonts[fontType]; if (!cachedFont) { const KFontData &fontData = DefaultFontData[fontType]; cachedFont = new QFont(QLatin1String(fontData.FontName), fontData.Size, fontData.Weight); cachedFont->setStyleHint(fontData.StyleHint); const KConfigGroup configGroup(kdeGlobals(), fontData.ConfigGroupKey); QString fontInfo = configGroup.readEntry(fontData.ConfigKey, QString()); //If we have serialized information for this font, restore it //NOTE: We are not using KConfig directly because we can't call QFont::QFont from here if (!fontInfo.isEmpty()) { cachedFont->fromString(fontInfo); } else { cachedFont->setStyleName(QLatin1String(fontData.StyleName)); } mFonts[fontType] = cachedFont; } return cachedFont; } void KFontSettingsData::dropFontSettingsCache() { if (mKdeGlobals) { mKdeGlobals->reparseConfiguration(); } for (int i = 0; i < FontTypesCount; ++i) { delete mFonts[i]; mFonts[i] = 0; } QWindowSystemInterface::handleThemeChange(0); if (qobject_cast(QCoreApplication::instance())) { QApplication::setFont(*font(KFontSettingsData::GeneralFont)); } else { QGuiApplication::setFont(*font(KFontSettingsData::GeneralFont)); } } void KFontSettingsData::delayedDBusConnects() { #ifdef DBUS_SUPPORT_ENABLED QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KDEPlatformTheme"), QStringLiteral("org.kde.KDEPlatformTheme"), QStringLiteral("refreshFonts"), this, SLOT(dropFontSettingsCache())); #endif } diff --git a/src/platformtheme/kfontsettingsdatamac.h b/src/platformtheme/kfontsettingsdatamac.h index ee99919..6013326 100644 --- a/src/platformtheme/kfontsettingsdatamac.h +++ b/src/platformtheme/kfontsettingsdatamac.h @@ -1,59 +1,67 @@ /* This file is part of the KDE libraries Copyright (C) 2000, 2006 David Faure Copyright 2008 Friedrich W. H. Kossebau Copyright 2013 Aleix Pol Gonzalez Copyright 2015 René J.V. Bertin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KFONTSETTINGSDATAMAC_H #define KFONTSETTINGSDATAMAC_H #include "kfontsettingsdata.h" +#include + +class KdeMacTheme; class KFontSettingsDataMac : public KFontSettingsData { Q_OBJECT public: // if adding a new type here also add an entry to DefaultFontData enum FontTypes { GeneralFont = 0, FixedFont, ToolbarFont, MenuFont, WindowTitleFont, TaskbarFont, SmallestReadableFont, MessageBoxFont, FontTypesCount }; - KFontSettingsDataMac(); + KFontSettingsDataMac(KdeMacTheme *theme); ~KFontSettingsDataMac(); + const char *fontNameFor(QFontDatabase::SystemFont role) const; + public Q_SLOTS: void dropFontSettingsCache(); protected Q_SLOTS: void delayedDBusConnects(); public: QFont *font(FontTypes fontType); private: + KFontSettingsDataMac(); + QFont *mFonts[FontTypesCount]; + KdeMacTheme *mTheme; }; #endif // KFONTSETTINGSDATAMAC_H diff --git a/src/platformtheme/kfontsettingsdatamac.mm b/src/platformtheme/kfontsettingsdatamac.mm index ebbd1c1..18c9f85 100644 --- a/src/platformtheme/kfontsettingsdatamac.mm +++ b/src/platformtheme/kfontsettingsdatamac.mm @@ -1,253 +1,255 @@ /* This file is part of the KDE libraries Copyright (C) 2000, 2006 David Faure Copyright 2008 Friedrich W. H. Kossebau Copyright 2013 Aleix Pol Gonzalez Copyright 2015 René J.V. Bertin This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kfontsettingsdatamac.h" +#include "kdemactheme.h" #include "platformtheme_logging.h" #include #include #include #include #include #include #include #ifdef DBUS_SUPPORT_ENABLED #include #include #endif #include #include #include // NOTE: keep in sync with plasma-desktop/kcms/fonts/fonts.cpp static const char GeneralId[] = "General"; // NOTE: the default system font changed with OS X 10.11, from Lucida Grande to // San Francisco. With luck this will be caught by QFontDatabase::GeneralFont const char DefaultFont[] = "Lucida Grande"; static const char DefaultFixedFont[] = "Monaco"; static const char *LocalDefaultFont = NULL; // See README.fonts.txt for information and thoughts about native/default fonts KFontData DefaultFontData[KFontSettingsDataMac::FontTypesCount] = { { GeneralId, "font", DefaultFont, 12, -1, QFont::SansSerif, "Medium" }, { GeneralId, "fixed", DefaultFixedFont, 10, -1, QFont::Monospace, "Regular" }, { GeneralId, "toolBarFont", DefaultFont, 10, -1, QFont::SansSerif, "Medium" }, { GeneralId, "menuFont", DefaultFont, 14, -1, QFont::SansSerif, "Medium" }, // applications don't control the window titlebar fonts { "WM", "activeFont", DefaultFont, 13, -1, QFont::SansSerif, "Medium" }, { GeneralId, "taskbarFont", DefaultFont, 9, -1, QFont::SansSerif, "Medium" }, { GeneralId, "smallestReadableFont", DefaultFont, 9, -1, QFont::SansSerif, "Medium" }, // this one is to accomodate for the MessageBoxFont which should be bold on OS X // when using the native theme fonts. { GeneralId, "messageBoxFont", DefaultFont, 13, QFont::Bold, QFont::SansSerif, "Bold" } }; -static const char *fontNameFor(QFontDatabase::SystemFont role) +const char *KFontSettingsDataMac::fontNameFor(QFontDatabase::SystemFont role) const { QFont qf = QFontDatabase::systemFont(role); if (!qf.defaultFamily().isEmpty()) { char *fn; if (role == QFontDatabase::FixedFont && !qf.fixedPitch()) { fn = strdup("Monaco"); } else if (qf.defaultFamily() == QStringLiteral(".Lucida Grande UI")) { fn = strdup("Lucida Grande"); } else { fn = strdup(qf.defaultFamily().toLocal8Bit().data()); } - if (qEnvironmentVariableIsSet("QT_QPA_PLATFORMTHEME_VERBOSE")) { + if (mTheme->verbose) { qCWarning(PLATFORMTHEME) << "fontNameFor" << role << "font:" << qf << "name:" << fn; } return fn; } else { return NULL; } } -void initDefaultFonts() +void initDefaultFonts(KFontSettingsDataMac *instance) { const char *fn; static bool active = false; // we must protect ourselves from being called recursively if (active) { return; } active = true; if (!LocalDefaultFont) { - fn = fontNameFor(QFontDatabase::GeneralFont); + fn = instance->fontNameFor(QFontDatabase::GeneralFont); LocalDefaultFont = fn; } for (int i = 0 ; i < KFontSettingsDataMac::FontTypesCount ; ++i) { switch(i) { case KFontSettingsDataMac::FixedFont: - fn = fontNameFor(QFontDatabase::FixedFont); + fn = instance->fontNameFor(QFontDatabase::FixedFont); break; case KFontSettingsDataMac::WindowTitleFont: - fn = fontNameFor(QFontDatabase::TitleFont); + fn = instance->fontNameFor(QFontDatabase::TitleFont); break; case KFontSettingsDataMac::SmallestReadableFont: - fn = fontNameFor(QFontDatabase::SmallestReadableFont); + fn = instance->fontNameFor(QFontDatabase::SmallestReadableFont); break; default: fn = LocalDefaultFont; break; } if (qEnvironmentVariableIsSet("QT_QPA_PLATFORMTHEME_VERBOSE")) { qCWarning(PLATFORMTHEME) << "Default font for type" << i << ":" << fn << "; currently:" << DefaultFontData[i].FontName; } if (fn) { if (DefaultFontData[i].FontName != DefaultFont && DefaultFontData[i].FontName != DefaultFixedFont && DefaultFontData[i].FontName != LocalDefaultFont) { free((void*)DefaultFontData[i].FontName); } DefaultFontData[i].FontName = fn; } } active = false; } -KFontSettingsDataMac::KFontSettingsDataMac() +KFontSettingsDataMac::KFontSettingsDataMac(KdeMacTheme *theme) + : mTheme(theme) { #ifdef DBUS_SUPPORT_ENABLED QMetaObject::invokeMethod(this, "delayedDBusConnects", Qt::QueuedConnection); #endif for (int i = 0; i < FontTypesCount; ++i) { // remove any information that already have been cached by our parent // IFF we don't have our own mFonts copy // delete mFonts[i]; mFonts[i] = 0; } } KFontSettingsDataMac::~KFontSettingsDataMac() { for (int i = 0 ; i < KFontSettingsDataMac::FontTypesCount ; ++i) { if (DefaultFontData[i].FontName != DefaultFont && DefaultFontData[i].FontName != DefaultFixedFont) { if (DefaultFontData[i].FontName && DefaultFontData[i].FontName != LocalDefaultFont) { free((void*)(DefaultFontData[i].FontName)); } DefaultFontData[i].FontName = (i == FixedFont)? DefaultFixedFont : DefaultFont; } } if (LocalDefaultFont) { free((void*)(LocalDefaultFont)); } LocalDefaultFont = NULL; } QFont *KFontSettingsDataMac::font(FontTypes fontType) { QFont *cachedFont = mFonts[fontType]; if (!cachedFont) { // check if we have already initialised our local database mapping font types to fonts // if not, we do it here, at the latest possible moment. Doing it in the KFontSettingsDataMac // ctor is bound for failure as our instance is likely to be created before Qt's own // font database has been populated. That's expectable: the font database also represents // platform (theme) specific fonts for various roles, and our ctor is called as part of the // platform theme creation procedure. if (!LocalDefaultFont) { static bool active = false; // NB: initDefaultFonts() queries Qt's font database which in turn can call us // again. Protection against this is built into initDefaultFonts(), but in practice // we prefer to return NULL if called through recursively. if (!active) { active = true; - initDefaultFonts(); + initDefaultFonts(this); active = false; } else { // our caller must handle NULL, preferably by relaying the font request // to the native platform theme (see KdeMacTheme::font()). return NULL; } } const KConfigGroup configGroup(kdeGlobals(), DefaultFontData[fontType].ConfigGroupKey); QString fontInfo; bool forceBold = false; if (fontType == MessageBoxFont) { // OS X special: the MessageBoxFont is by default a bold version of the GeneralFont // and that's what is cached in DefaultFontData[MessageBoxFont]. // NB: we can use a single configGroup for this hack as long as MessageBoxFont and // GeneralFont share the same ConfigGroupKey (or MessageBoxFont cannot be configured). fontInfo = configGroup.readEntry(DefaultFontData[GeneralFont].ConfigKey, QString()); if (!fontInfo.isEmpty()) { // However, if the user has configured a GeneralFont (MessageBoxFont cannot be configured), // we respect his/her choice but maintain the bold aspect dictated by the platform. fontType = GeneralFont; forceBold = true; } } const KFontData &fontData = DefaultFontData[fontType]; cachedFont = new QFont(QLatin1String(fontData.FontName), fontData.Size, forceBold? QFont::Bold : fontData.Weight); cachedFont->setStyleHint(fontData.StyleHint); // ignore the default stylehint; works better converting medium -> bold // cachedFont->setStyleName(QLatin1String(fontData.StyleName)); // if (qEnvironmentVariableIsSet("QT_QPA_PLATFORMTHEME_VERBOSE")) { // qCWarning(PLATFORMTHEME) << "Requested font type" << fontType << "name=" << fontData.FontName << "forceBold=" << forceBold << "styleHint=" << fontData.StyleHint; // qCWarning(PLATFORMTHEME) << "\t->" << *cachedFont; // } fontInfo = configGroup.readEntry(fontData.ConfigKey, QString()); if (!fontInfo.isEmpty()) { cachedFont->fromString(fontInfo); // if (qEnvironmentVariableIsSet("QT_QPA_PLATFORMTHEME_VERBOSE")) { // qCWarning(PLATFORMTHEME) << "\tfontInfo=" << fontInfo << "->" << *cachedFont; // } } else { QString fName = cachedFont->toString(); cachedFont->setStyleName(QLatin1String(fontData.StyleName)); if (qEnvironmentVariableIsSet("QT_QPA_PLATFORMTHEME_VERBOSE")) { qCWarning(PLATFORMTHEME) << "\t" << fName << "+ styleName" << fontData.StyleName << "->" << *cachedFont; } } mFonts[fontType] = cachedFont; } return cachedFont; } void KFontSettingsDataMac::dropFontSettingsCache() { if (qobject_cast(QCoreApplication::instance())) { QApplication::setFont(*font(KFontSettingsDataMac::GeneralFont)); } else { QGuiApplication::setFont(*font(KFontSettingsDataMac::GeneralFont)); } } void KFontSettingsDataMac::delayedDBusConnects() { #ifdef DBUS_SUPPORT_ENABLED QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KDEPlatformTheme"), QStringLiteral("org.kde.KDEPlatformTheme"), QStringLiteral("refreshFonts"), this, SLOT(dropFontSettingsCache())); #endif } diff --git a/src/platformtheme/khintssettings.cpp b/src/platformtheme/khintssettings.cpp index ae63e83..d450476 100644 --- a/src/platformtheme/khintssettings.cpp +++ b/src/platformtheme/khintssettings.cpp @@ -1,434 +1,457 @@ /* This file is part of the KDE libraries * Copyright 2013 Kevin Ottens * Copyright 2013 Aleix Pol Gonzalez * Copyright 2013 Alejandro Fiestas Olivares * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License or ( at * your option ) version 3 or, at the discretion of KDE e.V. ( which shall * act as a proxy as in section 14 of the GPLv3 ), any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #undef QT_NO_CAST_FROM_ASCII #include "khintssettings.h" #include "platformtheme_logging.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DBUS_SUPPORT_ENABLED #include #include #endif #include #include #include #include #include #ifdef UNIT_TEST #undef HAVE_X11 #define HAVE_X11 0 #endif #if HAVE_X11 #include #include #endif -static const QString defaultLookAndFeelPackage = QStringLiteral("org.kde.breeze.desktop"); +static const QString defaultLookAndFeelPackage = QStringLiteral("com.apple.macintosh.desktop"); KSharedConfigPtr &KHintsSettings::kdeGlobals() { if (!mKdeGlobals) { + verbose = qEnvironmentVariableIsSet("QT_QPA_PLATFORMTHEME_VERBOSE"); if (qEnvironmentVariableIsSet("QT_QPA_PLATFORMTHEME_CONFIG_FILE")) { - mKdeGlobals = KSharedConfig::openConfig(qgetenv("QT_QPA_PLATFORMTHEME_CONFIG_FILE"), KConfig::NoGlobals); + const auto fname(qgetenv("QT_QPA_PLATFORMTHEME_CONFIG_FILE")); + mKdeGlobals = KSharedConfig::openConfig(fname, KConfig::NoGlobals); + const auto foundFile = QStandardPaths::locate(mKdeGlobals->locationType(), mKdeGlobals->name()); + if (foundFile.isEmpty()) { + qCWarning(PLATFORMTHEME) << "WARNING: could not open config file" << fname + << "in" << QStandardPaths::standardLocations(mKdeGlobals->locationType()); + } } else { mKdeGlobals = KSharedConfig::openConfig(QStringLiteral("kdeglobals"), KConfig::NoGlobals); } } return mKdeGlobals; } +KSharedConfigPtr &KHintsSettings::LnfConfig() +{ + return mLnfConfig; +} + +KSharedConfigPtr &KHintsSettings::DefaultLnfConfig() +{ + return mDefaultLnfConfig; +} + KHintsSettings::KHintsSettings() : QObject(0) { kdeGlobals(); KConfigGroup cg(mKdeGlobals, "KDE"); // try to extract the proper defaults file from a lookandfeel package const QString looknfeel = cg.readEntry("LookAndFeelPackage", defaultLookAndFeelPackage); mDefaultLnfConfig = KSharedConfig::openConfig(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("plasma/look-and-feel/") + looknfeel + QStringLiteral("/contents/defaults"))); if (looknfeel != defaultLookAndFeelPackage) { mLnfConfig = KSharedConfig::openConfig(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("plasma/look-and-feel/") + defaultLookAndFeelPackage + QStringLiteral("/contents/defaults"))); + if (verbose) { + qCWarning(PLATFORMTHEME) << "User-selected look-and-feel:" << looknfeel << mDefaultLnfConfig->name(); + qCWarning(PLATFORMTHEME) << "Default look-and-feel:" << defaultLookAndFeelPackage << mLnfConfig->name(); + } } const auto cursorBlinkRate = cg.readEntry("CursorBlinkRate", 1000); m_hints[QPlatformTheme::CursorFlashTime] = cursorBlinkRate > 0 ? qBound(200, cursorBlinkRate, 2000) : 0; // 0 => no blinking m_hints[QPlatformTheme::MouseDoubleClickInterval] = cg.readEntry("DoubleClickInterval", 400); m_hints[QPlatformTheme::StartDragDistance] = cg.readEntry("StartDragDist", 10); m_hints[QPlatformTheme::StartDragTime] = cg.readEntry("StartDragTime", 500); KConfigGroup cgToolbar(mKdeGlobals, "Toolbar style"); m_hints[QPlatformTheme::ToolButtonStyle] = toolButtonStyle(cgToolbar); KConfigGroup cgToolbarIcon(mKdeGlobals, "MainToolbarIcons"); m_hints[QPlatformTheme::ToolBarIconSize] = cgToolbarIcon.readEntry("Size", 22); m_hints[QPlatformTheme::ItemViewActivateItemOnSingleClick] = cg.readEntry("SingleClick", true); m_hints[QPlatformTheme::SystemIconThemeName] = readConfigValue(QStringLiteral("Icons"), QStringLiteral("Theme"), QStringLiteral("oxygen")); m_hints[QPlatformTheme::SystemIconFallbackThemeName] = QStringLiteral("hicolor"); m_hints[QPlatformTheme::IconThemeSearchPaths] = xdgIconThemePaths(); QStringList styleNames; styleNames << QStringLiteral("oxygen") << QStringLiteral("fusion") << QStringLiteral("windows"); const QString configuredStyle = cg.readEntry("widgetStyle", QString()); if (!configuredStyle.isEmpty()) { styleNames.removeOne(configuredStyle); styleNames.prepend(configuredStyle); } const QString lnfStyle = readConfigValue(QStringLiteral("KDE"), QStringLiteral("widgetStyle"), QString()).toString(); if (!lnfStyle.isEmpty()) { styleNames.removeOne(lnfStyle); styleNames.prepend(lnfStyle); } m_hints[QPlatformTheme::StyleNames] = styleNames; m_hints[QPlatformTheme::DialogButtonBoxLayout] = QDialogButtonBox::KdeLayout; m_hints[QPlatformTheme::DialogButtonBoxButtonsHaveIcons] = cg.readEntry("ShowIconsOnPushButtons", true); m_hints[QPlatformTheme::UseFullScreenForPopupMenu] = true; m_hints[QPlatformTheme::KeyboardScheme] = QPlatformTheme::KdeKeyboardScheme; m_hints[QPlatformTheme::UiEffects] = cg.readEntry("GraphicEffectsLevel", 0) != 0 ? QPlatformTheme::GeneralUiEffect : 0; m_hints[QPlatformTheme::IconPixmapSizes] = QVariant::fromValue(QList() << 512 << 256 << 128 << 64 << 32 << 22 << 16 << 8); m_hints[QPlatformTheme::WheelScrollLines] = cg.readEntry("WheelScrollLines", 3); if (qobject_cast(QCoreApplication::instance())) { QApplication::setWheelScrollLines(cg.readEntry("WheelScrollLines", 3)); } updateShowIconsInMenuItems(cg); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) m_hints[QPlatformTheme::ShowShortcutsInContextMenus] = true; #endif #ifdef DBUS_SUPPORT_ENABLED QMetaObject::invokeMethod(this, "delayedDBusConnects", Qt::QueuedConnection); #endif QMetaObject::invokeMethod(this, "setupIconLoader", Qt::QueuedConnection); loadPalettes(); } KHintsSettings::~KHintsSettings() { qDeleteAll(m_palettes); } QVariant KHintsSettings::readConfigValue(const QString &group, const QString &key, const QVariant &defaultValue) { KConfigGroup userCg(mKdeGlobals, group); QVariant value = userCg.readEntry(key, QString()); if (!value.isNull()) { return value; } if (mLnfConfig) { KConfigGroup lnfCg(mLnfConfig, "kdeglobals"); lnfCg = KConfigGroup(&lnfCg, group); if (lnfCg.isValid()) { value = lnfCg.readEntry(key, defaultValue); if (!value.isNull()) { return value; } } } +#ifndef Q_OS_MACOS KConfigGroup lnfCg(mDefaultLnfConfig, group); if (lnfCg.isValid()) { return lnfCg.readEntry(key, defaultValue); } +#endif return defaultValue; } QStringList KHintsSettings::xdgIconThemePaths() const { QStringList paths; // make sure we have ~/.local/share/icons in paths if it exists paths << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("icons"), QStandardPaths::LocateDirectory); const QFileInfo homeIconDir(QDir::homePath() + QStringLiteral("/.icons")); if (homeIconDir.isDir()) { paths << homeIconDir.absoluteFilePath(); } return paths; } void KHintsSettings::delayedDBusConnects() { #ifdef DBUS_SUPPORT_ENABLED QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged"), this, SLOT(toolbarStyleChanged())); QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KGlobalSettings"), QStringLiteral("org.kde.KGlobalSettings"), QStringLiteral("notifyChange"), this, SLOT(slotNotifyChange(int,int))); #endif } void KHintsSettings::setupIconLoader() { connect(KIconLoader::global(), &KIconLoader::iconChanged, this, &KHintsSettings::iconChanged); } void KHintsSettings::toolbarStyleChanged() { mKdeGlobals->reparseConfiguration(); KConfigGroup cg(mKdeGlobals, "Toolbar style"); m_hints[QPlatformTheme::ToolButtonStyle] = toolButtonStyle(cg); //from gtksymbol.cpp QWidgetList widgets = QApplication::allWidgets(); for (int i = 0; i < widgets.size(); ++i) { QWidget *widget = widgets.at(i); if (qobject_cast(widget)) { QEvent event(QEvent::StyleChange); QApplication::sendEvent(widget, &event); } } } void KHintsSettings::slotNotifyChange(int type, int arg) { mKdeGlobals->reparseConfiguration(); KConfigGroup cg(mKdeGlobals, "KDE"); switch (type) { case PaletteChanged: { loadPalettes(); //QApplication::setPalette and QGuiApplication::setPalette are different functions //and non virtual. Call the correct one if (qobject_cast(QCoreApplication::instance())) { QPalette palette = *m_palettes[QPlatformTheme::SystemPalette]; QApplication::setPalette(palette); // QTBUG QGuiApplication::paletteChanged() signal is only emitted by QGuiApplication // so things like SystemPalette QtQuick item that use it won't notice a palette // change when a QApplication which causes e.g. QML System Settings modules to not update emit qApp->paletteChanged(palette); } else if (qobject_cast(QCoreApplication::instance())) { QGuiApplication::setPalette(*m_palettes[QPlatformTheme::SystemPalette]); } break; } case SettingsChanged: { SettingsCategory category = static_cast(arg); if (category == SETTINGS_QT || category == SETTINGS_MOUSE) { updateQtSettings(cg); } else if (category == SETTINGS_STYLE) { m_hints[QPlatformTheme::DialogButtonBoxButtonsHaveIcons] = cg.readEntry("ShowIconsOnPushButtons", true); m_hints[QPlatformTheme::UiEffects] = cg.readEntry("GraphicEffectsLevel", 0) != 0 ? QPlatformTheme::GeneralUiEffect : 0; updateShowIconsInMenuItems(cg); } break; } case ToolbarStyleChanged: { toolbarStyleChanged(); break; } case IconChanged: iconChanged(arg); //Once the KCM is ported to use IconChanged, this should not be needed break; case CursorChanged: updateCursorTheme(); break; case StyleChanged: { QApplication *app = qobject_cast(QCoreApplication::instance()); if (!app) { return; } const QString theme = cg.readEntry("widgetStyle", QString()); if (theme.isEmpty()) { return; } QStringList styleNames; styleNames << cg.readEntry("widgetStyle", QString()) << QStringLiteral("oxygen") << QStringLiteral("fusion") << QStringLiteral("windows"); const QString lnfStyle = readConfigValue(QStringLiteral("KDE"), QStringLiteral("widgetStyle"), QString()).toString(); if (!lnfStyle.isEmpty() && !styleNames.contains(lnfStyle)) { styleNames.prepend(lnfStyle); } m_hints[QPlatformTheme::StyleNames] = styleNames; app->setStyle(theme); loadPalettes(); break; } default: qCWarning(PLATFORMTHEME) << "Unknown type of change in KGlobalSettings::slotNotifyChange: " << type; } } void KHintsSettings::iconChanged(int group) { KIconLoader::Group iconGroup = (KIconLoader::Group) group; if (iconGroup != KIconLoader::MainToolbar) { m_hints[QPlatformTheme::SystemIconThemeName] = readConfigValue(QStringLiteral("Icons"), QStringLiteral("Theme"), QStringLiteral("oxygen")); return; } const int currentSize = KIconLoader::global()->currentSize(KIconLoader::MainToolbar); if (m_hints[QPlatformTheme::ToolBarIconSize] == currentSize) { return; } m_hints[QPlatformTheme::ToolBarIconSize] = currentSize; //If we are not a QApplication, means that we are a QGuiApplication, then we do nothing. if (!qobject_cast(QCoreApplication::instance())) { return; } QWidgetList widgets = QApplication::allWidgets(); Q_FOREACH (QWidget *widget, widgets) { if (qobject_cast(widget) || qobject_cast(widget)) { QEvent event(QEvent::StyleChange); QApplication::sendEvent(widget, &event); } } } void KHintsSettings::updateQtSettings(KConfigGroup &cg) { int flash = qBound(200, cg.readEntry("CursorBlinkRate", 1000), 2000); m_hints[QPlatformTheme::CursorFlashTime] = flash; int doubleClickInterval = cg.readEntry("DoubleClickInterval", 400); m_hints[QPlatformTheme::MouseDoubleClickInterval] = doubleClickInterval; int startDragDistance = cg.readEntry("StartDragDist", 10); m_hints[QPlatformTheme::StartDragDistance] = startDragDistance; int startDragTime = cg.readEntry("StartDragTime", 10); m_hints[QPlatformTheme::StartDragTime] = startDragTime; m_hints[QPlatformTheme::ItemViewActivateItemOnSingleClick] = cg.readEntry("SingleClick", true); updateShowIconsInMenuItems(cg); int wheelScrollLines = cg.readEntry("WheelScrollLines", 3); m_hints[QPlatformTheme::WheelScrollLines] = wheelScrollLines; QApplication *app = qobject_cast(QCoreApplication::instance()); if (app) { QApplication::setWheelScrollLines(cg.readEntry("WheelScrollLines", 3)); } } void KHintsSettings::updateShowIconsInMenuItems(KConfigGroup &cg) { bool showIcons = cg.readEntry("ShowIconsInMenuItems", !QApplication::testAttribute(Qt::AA_DontShowIconsInMenus)); QCoreApplication::setAttribute(Qt::AA_DontShowIconsInMenus, !showIcons); } Qt::ToolButtonStyle KHintsSettings::toolButtonStyle(const KConfigGroup &cg) const { const QString buttonStyle = cg.readEntry("ToolButtonStyle", "TextBesideIcon").toLower(); return buttonStyle == QLatin1String("textbesideicon") ? Qt::ToolButtonTextBesideIcon : buttonStyle == QLatin1String("icontextright") ? Qt::ToolButtonTextBesideIcon : buttonStyle == QLatin1String("textundericon") ? Qt::ToolButtonTextUnderIcon : buttonStyle == QLatin1String("icontextbottom") ? Qt::ToolButtonTextUnderIcon : buttonStyle == QLatin1String("textonly") ? Qt::ToolButtonTextOnly : Qt::ToolButtonIconOnly; } void KHintsSettings::loadPalettes() { qDeleteAll(m_palettes); m_palettes.clear(); if (mKdeGlobals->hasGroup("Colors:View")) { m_palettes[QPlatformTheme::SystemPalette] = new QPalette(KColorScheme::createApplicationPalette(mKdeGlobals)); } else { KConfigGroup cg(mKdeGlobals, "KDE"); const QString looknfeel = cg.readEntry("LookAndFeelPackage", defaultLookAndFeelPackage); QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("plasma/look-and-feel/") + looknfeel + QStringLiteral("/contents/colors")); if (!path.isEmpty()) { m_palettes[QPlatformTheme::SystemPalette] = new QPalette(KColorScheme::createApplicationPalette(KSharedConfig::openConfig(path))); return; } const QString scheme = readConfigValue(QStringLiteral("General"), QStringLiteral("ColorScheme"), QStringLiteral("Oxygen")).toString(); path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes/") + scheme + QStringLiteral(".colors")); if (!path.isEmpty()) { m_palettes[QPlatformTheme::SystemPalette] = new QPalette(KColorScheme::createApplicationPalette(KSharedConfig::openConfig(path))); } } } void KHintsSettings::updateCursorTheme() { KConfig config(QStringLiteral("kcminputrc")); KConfigGroup g(&config, "Mouse"); int size = g.readEntry("cursorSize", -1); // Default cursor size is 16 points if (size == -1) { if (QScreen *s = QGuiApplication::primaryScreen()) { size = s->logicalDotsPerInchY() * 16 / 72; } else { size = 0; } } #if HAVE_X11 if (QX11Info::isPlatformX11()) { const QString theme = g.readEntry("cursorTheme", QString()); // Note that in X11R7.1 and earlier, calling XcursorSetTheme() // with a NULL theme would cause Xcursor to use "default", but // in 7.2 and later it will cause it to revert to the theme that // was configured when the application was started. XcursorSetTheme(QX11Info::display(), theme.isNull() ? "default" : QFile::encodeName(theme).constData()); XcursorSetDefaultSize(QX11Info::display(), size); } #endif } diff --git a/src/platformtheme/khintssettings.h b/src/platformtheme/khintssettings.h index 8cc58dd..bdd2ec6 100644 --- a/src/platformtheme/khintssettings.h +++ b/src/platformtheme/khintssettings.h @@ -1,102 +1,106 @@ /* This file is part of the KDE libraries * Copyright 2013 Alejandro Fiestas Olivares * * This library is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License or ( at * your option ) version 3 or, at the discretion of KDE e.V. ( which shall * act as a proxy as in section 14 of the GPLv3 ), any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef KHINTS_SETTINGS_H #define KHINTS_SETTINGS_H #include #include #include #include class KConfigGroup; class QPalette; class KHintsSettings : public QObject { Q_OBJECT public: /** * An identifier for change signals. * @note Copied from KGlobalSettings */ enum ChangeType { PaletteChanged = 0, FontChanged, StyleChanged, SettingsChanged, IconChanged, CursorChanged, ToolbarStyleChanged, ClipboardConfigChanged, BlockShortcuts, NaturalSortingChanged }; /** * Valid values for the settingsChanged signal * @note Copied from KGlobalSettings */ enum SettingsCategory { SETTINGS_MOUSE, SETTINGS_COMPLETION, SETTINGS_PATHS, SETTINGS_POPUPMENU, SETTINGS_QT, SETTINGS_SHORTCUTS, SETTINGS_LOCALE, SETTINGS_STYLE }; explicit KHintsSettings(); virtual ~KHintsSettings(); inline QVariant hint(QPlatformTheme::ThemeHint hint) const { return m_hints[hint]; } inline QPalette *palette(QPlatformTheme::Palette type) const { return m_palettes[type]; } QStringList xdgIconThemePaths() const; protected Q_SLOTS: void delayedDBusConnects(); void setupIconLoader(); void toolbarStyleChanged(); void slotNotifyChange(int type, int arg); protected: KSharedConfigPtr &kdeGlobals(); + KSharedConfigPtr &LnfConfig(); + KSharedConfigPtr &DefaultLnfConfig(); + QVariant readConfigValue(const QString &group, const QString &key, const QVariant &defaultValue); void loadPalettes(); void iconChanged(int group); void updateQtSettings(KConfigGroup &cg); void updateShowIconsInMenuItems(KConfigGroup &cg); Qt::ToolButtonStyle toolButtonStyle(const KConfigGroup &cg) const; void updateCursorTheme(); inline QHash &palettes() { return m_palettes; } inline QHash &hints() { return m_hints; } private: QHash m_palettes; QHash m_hints; KSharedConfigPtr mKdeGlobals; KSharedConfigPtr mDefaultLnfConfig; KSharedConfigPtr mLnfConfig; + bool verbose; }; #endif //KHINTS_SETTINGS_H diff --git a/src/platformtheme/khintssettingsmac.h b/src/platformtheme/khintssettingsmac.h index 57f00ff..71e17c0 100644 --- a/src/platformtheme/khintssettingsmac.h +++ b/src/platformtheme/khintssettingsmac.h @@ -1,55 +1,59 @@ /* This file is part of the KDE libraries * Copyright 2013 Alejandro Fiestas Olivares * Copyright 2015 René J.V. Bertin * Copyright 2013 Aleix Pol Gonzalez * Copyright 2013 Alejandro Fiestas Olivares * Copyright 2015 René J.V. Bertin #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef DBUS_SUPPORT_ENABLED #include #include #endif #include #include #include #include #include class KdeProxyStyle : public QProxyStyle { public: KdeProxyStyle(const QString &styleName) : QProxyStyle(styleName) { ; } int layoutSpacing(QSizePolicy::ControlType control1, QSizePolicy::ControlType control2, Qt::Orientation orientation, const QStyleOption *option = 0, const QWidget *widget = 0) const { int spacing = QProxyStyle::layoutSpacing(control1, control2, orientation, option, widget); qCWarning(PLATFORMTHEME) << "layoutSpacing=" << spacing; if (spacing > 2) { spacing /= 2; } return spacing; } }; -KHintsSettingsMac::KHintsSettingsMac() - : styleProxy(0) +KHintsSettingsMac::KHintsSettingsMac(KdeMacTheme *theme) + : mTheme(theme) + , styleProxy(0) { KSharedConfigPtr mKdeGlobals = kdeGlobals(); - if (qEnvironmentVariableIsSet("QT_QPA_PLATFORMTHEME_VERBOSE")) { + if (mTheme->verbose) { if (!mKdeGlobals->name().isEmpty()) { qCWarning(PLATFORMTHEME) << Q_FUNC_INFO << "config file:" << mKdeGlobals->name() << "(" << QStandardPaths::locate(mKdeGlobals->locationType(), mKdeGlobals->name()) << ")"; + } else { + qCWarning(PLATFORMTHEME) << Q_FUNC_INFO << "config file:" << mKdeGlobals << "has no known name"; } } KConfigGroup cg(mKdeGlobals, "KDE"); + if (mTheme->verbose) { + qCWarning(PLATFORMTHEME) << "config group" << mKdeGlobals->name() << "." << cg.name() + << "exists=" << cg.exists() + << "valid=" << cg.isValid() + << "groups=" << cg.groupList() + << "keys=" << cg.keyList(); + } // we're overriding whatever the parent class configured hints().clear(); KConfigGroup cgToolbar(mKdeGlobals, "Toolbar style"); + if (mTheme->verbose) { + qCWarning(PLATFORMTHEME) << "config group" << mKdeGlobals->name() << "." << cgToolbar.name() + << "exists=" << cgToolbar.exists() + << "valid=" << cgToolbar.isValid() + << "groups=" << cgToolbar.groupList() + << "keys=" << cgToolbar.keyList(); + } hints()[QPlatformTheme::ToolButtonStyle] = toolButtonStyle(cgToolbar); KConfigGroup cgToolbarIcon(mKdeGlobals, "MainToolbarIcons"); + if (mTheme->verbose) { + qCWarning(PLATFORMTHEME) << "config group" << mKdeGlobals->name() << "." << cgToolbarIcon.name() + << "exists=" << cgToolbarIcon.exists() + << "valid=" << cgToolbarIcon.isValid() + << "groups=" << cgToolbarIcon.groupList() + << "keys=" << cgToolbarIcon.keyList(); + } hints()[QPlatformTheme::ToolBarIconSize] = cgToolbarIcon.readEntry("Size", 22); hints()[QPlatformTheme::ItemViewActivateItemOnSingleClick] = cg.readEntry("SingleClick", true); #ifdef KDEMACTHEME_ADD_ICONTHEMESETTINGS // The new default Breeze icon theme is svg based and looks more out of place than the older Oxygen theme // which is PNG-based, and thus easier to use with/in the Finder. hints()[QPlatformTheme::SystemIconThemeName] = readConfigValue(QStringLiteral("Icons"), QStringLiteral("Theme"), QStringLiteral("oxygen")); hints()[QPlatformTheme::IconThemeSearchPaths] = xdgIconThemePaths(); #endif QStringList styleNames; styleNames << QStringLiteral("aqua") << QStringLiteral("macintosh") << QStringLiteral("fusion") << QStringLiteral("windows"); + if (mTheme->verbose) { + qCWarning(PLATFORMTHEME) << "initial widget style list:" << styleNames; + } const QString configuredStyle = cg.readEntry("widgetStyle", QString()); if (!configuredStyle.isEmpty()) { styleNames.removeOne(configuredStyle); styleNames.prepend(configuredStyle); + if (mTheme->verbose) { + qCWarning(PLATFORMTHEME) << "Found widgetStyle" << configuredStyle << "in config file"; + } } const QString lnfStyle = readConfigValue(QStringLiteral("KDE"), QStringLiteral("widgetStyle"), QString()).toString(); - if (!lnfStyle.isEmpty()) { + if (!lnfStyle.isEmpty() && lnfStyle != configuredStyle) { styleNames.removeOne(lnfStyle); styleNames.prepend(lnfStyle); + if (mTheme->verbose) { + qCWarning(PLATFORMTHEME) << "Found widgetStyle" << lnfStyle << "look-and-feel definition" + << (LnfConfig() ? LnfConfig()->name() : QStringLiteral("???")); + } + } + if (mTheme->verbose) { + qCWarning(PLATFORMTHEME) << "final widget style list:" << styleNames; } hints()[QPlatformTheme::StyleNames] = styleNames; checkNativeTheme(configuredStyle); hints()[QPlatformTheme::DialogButtonBoxLayout] = QDialogButtonBox::MacLayout; hints()[QPlatformTheme::DialogButtonBoxButtonsHaveIcons] = cg.readEntry("ShowIconsOnPushButtons", false); hints()[QPlatformTheme::UseFullScreenForPopupMenu] = true; hints()[QPlatformTheme::KeyboardScheme] = QPlatformTheme::MacKeyboardScheme; hints()[QPlatformTheme::UiEffects] = cg.readEntry("GraphicEffectsLevel", 0) != 0 ? QPlatformTheme::GeneralUiEffect : 0; // this would be what we should return for IconPixmapSizes if we wanted to copy the system defaults: // qreal devicePixelRatio = qGuiApp->devicePixelRatio(); // QList sizes; // sizes << 16 * devicePixelRatio // << 32 * devicePixelRatio // << 64 * devicePixelRatio // << 128 * devicePixelRatio; // hints()[QPlatformTheme::IconPixmapSizes] = QVariant::fromValue(sizes); hints()[QPlatformTheme::WheelScrollLines] = cg.readEntry("WheelScrollLines", 3); if (qobject_cast(QCoreApplication::instance())) { QApplication::setWheelScrollLines(cg.readEntry("WheelScrollLines", 3)); } updateShowIconsInMenuItems(cg); #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) m_hints[QPlatformTheme::ShowShortcutsInContextMenus] = true; #endif #ifdef DBUS_SUPPORT_ENABLED QMetaObject::invokeMethod(this, "delayedDBusConnects", Qt::QueuedConnection); #endif loadPalettes(); } KHintsSettingsMac::~KHintsSettingsMac() { } // adapted from QGenericUnixTheme::xdgIconThemePaths() QStringList KHintsSettingsMac::xdgIconThemePaths() const { QStringList paths; // make sure we have ~/.local/share/icons in paths if it exists paths << QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("icons"), QStandardPaths::LocateDirectory); // Add home directory first in search path const QFileInfo homeIconDir(QDir::homePath() + QStringLiteral("/.icons")); if (homeIconDir.isDir()) { paths.prepend(homeIconDir.absoluteFilePath()); } QStringList xdgDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); if (xdgDirs.isEmpty()) { xdgDirs << QStringLiteral("/opt/local/share") << QStringLiteral("/usr/local/share") << QStringLiteral("/usr/share"); } foreach (const QString &xdgDir, xdgDirs) { const QFileInfo xdgIconsDir(xdgDir + QStringLiteral("/icons")); if (xdgIconsDir.isDir()) { paths.append(xdgIconsDir.absoluteFilePath()); } const QFileInfo pixmapsIconsDir(xdgDir + QStringLiteral("/pixmaps")); if (pixmapsIconsDir.isDir()) { paths.append(pixmapsIconsDir.absoluteFilePath()); } } return paths; } void KHintsSettingsMac::delayedDBusConnects() { #ifdef DBUS_SUPPORT_ENABLED QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KToolBar"), QStringLiteral("org.kde.KToolBar"), QStringLiteral("styleChanged"), this, SLOT(toolbarStyleChanged())); QDBusConnection::sessionBus().connect(QString(), QStringLiteral("/KGlobalSettings"), QStringLiteral("org.kde.KGlobalSettings"), QStringLiteral("notifyChange"), this, SLOT(slotNotifyChange(int,int))); #endif } void KHintsSettingsMac::checkNativeTheme(const QString &theme) { #if 0 // using a QStyleProxy messes up the colour palette for some reason, so this feature is deactivated for now if (theme.isEmpty() || theme.compare(QStringLiteral("macintosh"), Qt::CaseInsensitive) == 0) { if (qApp) { if (!styleProxy) { styleProxy = new KdeProxyStyle(QStringLiteral("macintosh")); } // styleProxy will be owned by QApplication after this, so no point deleting it qApp->setStyle(styleProxy); loadPalettes(); } } #endif // do this only when certain that there's a QApplication instance: // QApplication *app = qobject_cast(QCoreApplication::instance()); // if (app) { // qCWarning(PLATFORMTHEME) << Q_FUNC_INFO << "platform theme:" << app->style()->objectName(); // } } void KHintsSettingsMac::slotNotifyChange(int type, int arg) { KHintsSettings::slotNotifyChange(type,arg); KSharedConfigPtr mKdeGlobals = kdeGlobals(); KConfigGroup cg(mKdeGlobals, "KDE"); switch (type) { case SettingsChanged: { SettingsCategory category = static_cast(arg); if (category == SETTINGS_STYLE) { hints()[QPlatformTheme::DialogButtonBoxButtonsHaveIcons] = cg.readEntry("ShowIconsOnPushButtons", false); updateShowIconsInMenuItems(cg); } break; } case StyleChanged: { QApplication *app = qobject_cast(QCoreApplication::instance()); if (!app) { return; } const QString theme = cg.readEntry("widgetStyle", QString()); checkNativeTheme(theme); if (theme.isEmpty()) { return; } QStringList styleNames; styleNames << cg.readEntry("widgetStyle", QString()) << QStringLiteral("aqua") << QStringLiteral("macintosh") << QStringLiteral("fusion") << QStringLiteral("windows"); + if (mTheme->verbose) { + qCWarning(PLATFORMTHEME) << "initial widget style list:" << styleNames; + } const QString lnfStyle = readConfigValue(QStringLiteral("KDE"), QStringLiteral("widgetStyle"), QString()).toString(); if (!lnfStyle.isEmpty() && !styleNames.contains(lnfStyle)) { styleNames.prepend(lnfStyle); + if (mTheme->verbose) { + qCWarning(PLATFORMTHEME) << "Found widgetStyle" << lnfStyle << "look-and-feel definition" + << (LnfConfig() ? LnfConfig()->name() : QStringLiteral("???")); + } + } + if (mTheme->verbose) { + qCWarning(PLATFORMTHEME) << "final widget style list:" << styleNames; } hints()[QPlatformTheme::StyleNames] = styleNames; break; } } } void KHintsSettingsMac::iconChanged(int group) { KIconLoader::Group iconGroup = (KIconLoader::Group) group; if (iconGroup != KIconLoader::MainToolbar) { hints()[QPlatformTheme::SystemIconThemeName] = readConfigValue(QStringLiteral("Icons"), QStringLiteral("Theme"), QStringLiteral("oxygen")); return; } return KHintsSettings::iconChanged(group); } Qt::ToolButtonStyle KHintsSettingsMac::toolButtonStyle(const KConfigGroup &cg) const { const QString buttonStyle = cg.readEntry("ToolButtonStyle", "TextUnderIcon").toLower(); return buttonStyle == QLatin1String("textbesideicon") ? Qt::ToolButtonTextBesideIcon : buttonStyle == QLatin1String("icontextright") ? Qt::ToolButtonTextBesideIcon : buttonStyle == QLatin1String("textundericon") ? Qt::ToolButtonTextUnderIcon : buttonStyle == QLatin1String("icontextbottom") ? Qt::ToolButtonTextUnderIcon : buttonStyle == QLatin1String("textonly") ? Qt::ToolButtonTextOnly : Qt::ToolButtonIconOnly; } void KHintsSettingsMac::loadPalettes() { qDeleteAll(palettes()); palettes().clear(); KSharedConfigPtr mKdeGlobals = kdeGlobals(); if (mKdeGlobals->hasGroup("Colors:View")) { palettes()[QPlatformTheme::SystemPalette] = new QPalette(KColorScheme::createApplicationPalette(mKdeGlobals)); } else { const QString scheme = readConfigValue(QStringLiteral("General"), QStringLiteral("ColorScheme"), QStringLiteral("Mac OSX Graphite")).toString(); const QString path = QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("color-schemes/") + scheme + QStringLiteral(".colors")); if (!path.isEmpty()) { palettes()[QPlatformTheme::SystemPalette] = new QPalette(KColorScheme::createApplicationPalette(KSharedConfig::openConfig(path))); } } } void KHintsSettingsMac::updateCursorTheme() { }