File indexing completed on 2024-04-21 03:52:25

0001 /**
0002     KStyle for KDE4
0003         SPDX-FileCopyrightText: 2004-2005 Maksim Orlovich <maksim@kde.org>
0004         SPDX-FileCopyrightText: 2005, 2006 Sandro Giessl <giessl@kde.org>
0005 
0006     Based in part on the following software:
0007 
0008     KStyle for KDE3
0009         SPDX-FileCopyrightText: 2001-2002 Karol Szwed <gallium@kde.org>
0010     Portions:
0011         SPDX-FileCopyrightText: 1998-2000 TrollTech AS
0012 
0013     Keramik for KDE3,
0014         SPDX-FileCopyrightText: 2002 Malte Starostik <malte@kde.org>
0015         SPDX-FileCopyrightText: 2002-2003 Maksim Orlovich<maksim@kde.org>
0016     Portions:
0017         SPDX-FileCopyrightText: 2001-2002 Karol Szwed <gallium@kde.org>
0018         SPDX-FileCopyrightText: 2001-2002 Fredrik Höglund <fredrik@kde.org>
0019         SPDX-FileCopyrightText: 2000 Daniel M. Duley <mosfet@kde.org>
0020         SPDX-FileCopyrightText: 2000 Dirk Mueller <mueller@kde.org>
0021         SPDX-FileCopyrightText: 2001 Martijn Klingens <klingens@kde.org>
0022         SPDX-FileCopyrightText: 2003 Sandro Giessl <sandro@giessl.com>
0023 
0024     Many thanks to Bradley T. Hughes for the 3 button scrollbar code.
0025 
0026     SPDX-License-Identifier: LGPL-2.0-or-later
0027 */
0028 
0029 #include "kstyle.h"
0030 
0031 #include <QAbstractItemView>
0032 #include <QApplication>
0033 #include <QDialogButtonBox>
0034 #include <QEvent>
0035 #include <QIcon>
0036 #include <QPushButton>
0037 #include <QShortcut>
0038 #include <QStyleOption>
0039 #include <QToolBar>
0040 
0041 #include <KColorScheme>
0042 #include <KConfigGroup>
0043 #include <KIconLoader>
0044 #include <KMessageWidget>
0045 
0046 // ----------------------------------------------------------------------------
0047 
0048 static const QStyle::StyleHint SH_KCustomStyleElement = (QStyle::StyleHint)0xff000001;
0049 static const int X_KdeBase = 0xff000000;
0050 
0051 class KStylePrivate
0052 {
0053 public:
0054     KStylePrivate();
0055 
0056     QHash<QString, int> styleElements;
0057     int hintCounter, controlCounter, subElementCounter;
0058 };
0059 
0060 KStylePrivate::KStylePrivate()
0061 {
0062     controlCounter = subElementCounter = X_KdeBase;
0063     hintCounter = X_KdeBase + 1; // sic! X_KdeBase is covered by SH_KCustomStyleElement
0064 }
0065 
0066 /*
0067     The functions called by widgets that request custom element support, passed to the effective style.
0068     Collected in a static inline function due to similarity.
0069 */
0070 
0071 static inline int customStyleElement(QStyle::StyleHint type, const QString &element, QWidget *widget)
0072 {
0073     if (!widget || widget->style()->metaObject()->indexOfClassInfo("X-KDE-CustomElements") < 0) {
0074         return 0;
0075     }
0076 
0077     const QString originalName = widget->objectName();
0078     widget->setObjectName(element);
0079     const int id = widget->style()->styleHint(type, nullptr, widget);
0080     widget->setObjectName(originalName);
0081     return id;
0082 }
0083 
0084 QStyle::StyleHint KStyle::customStyleHint(const QString &element, const QWidget *widget)
0085 {
0086     return (StyleHint)customStyleElement(SH_KCustomStyleElement, element, const_cast<QWidget *>(widget));
0087 }
0088 
0089 QStyle::ControlElement KStyle::customControlElement(const QString &element, const QWidget *widget)
0090 {
0091     return (ControlElement)customStyleElement(SH_KCustomStyleElement, element, const_cast<QWidget *>(widget));
0092 }
0093 
0094 QStyle::SubElement KStyle::customSubElement(const QString &element, const QWidget *widget)
0095 {
0096     return (SubElement)customStyleElement(SH_KCustomStyleElement, element, const_cast<QWidget *>(widget));
0097 }
0098 
0099 KStyle::KStyle()
0100     : d(new KStylePrivate)
0101 {
0102 }
0103 
0104 KStyle::~KStyle()
0105 {
0106     delete d;
0107 }
0108 
0109 /*
0110     Custom Style Element runtime extension:
0111     We reserve one StyleHint to let the effective style inform widgets whether it supports certain
0112     string based style elements.
0113     As this could lead to number conflicts (i.e. an app utilizing one of the hints itself for other
0114     purposes) there're various safety mechanisms to rule out such interference.
0115 
0116     1) It's most unlikely that a widget in some 3rd party app will accidentally call a general
0117     QStyle/KStyle styleHint() or draw*() and (unconditionally) expect a valid return, however:
0118     a. The StyleHint is not directly above Qt's custom base, assuming most 3rd party apps would
0119     - in case - make use of such
0120     b. In order to be accepted, the StyleHint query must pass a widget with a perfectly matching
0121     name, containing the typical element prefix ("CE_", etc.) and being supported by the current style
0122     c. Instead using Qt's fragile qstyleoption_cast on the QStyleOption provided to the StyleHint
0123     query, try to dump out a string and hope for the best, we now manipulate the widgets objectName().
0124     Plain Qt dependent widgets can do that themselves and if a widget uses KStyle's convenience access
0125     functions, it won't notice this at all
0126 
0127     2) The key problem is that a common KDE widget will run into an apps custom style which will then
0128     falsely respond to the styleHint() call with an invalid value.
0129     To prevent this, supporting styles *must* set a Q_CLASSINFO "X-KDE-CustomElements".
0130 
0131     3) If any of the above traps snaps, the returned id is 0 - the QStyle default, indicating
0132     that this element is not supported by the current style.
0133 
0134     Obviously, this contains the "diminished clean" action to (temporarily) manipulate the
0135     objectName() of a const QWidget* - but this happens completely inside KStyle or the widget, if
0136     it does not make use of KStyles static convenience functions.
0137     My biggest worry here would be, that in a multithreaded environment a thread (usually not being
0138     owner of the widget) does something crucially relying on the widgets name property...
0139     This however would also have to happen during the widget construction or stylechanges, when
0140     the functions in doubt will typically be called.
0141     So this is imho unlikely causing any trouble, ever.
0142 */
0143 
0144 /*
0145     The functions called by the real style implementation to add support for a certain element.
0146     Checks for well-formed string (containing the element prefix) and returns 0 otherwise.
0147     Checks whether the element is already supported or inserts it otherwise; Returns the proper id
0148     NOTICE: We could check for "X-KDE-CustomElements", but this would bloat style start up times
0149     (if they e.g. register 100 elements or so)
0150 */
0151 
0152 static inline int newStyleElement(const QString &element, const char *check, int &counter, QHash<QString, int> *elements)
0153 {
0154     if (!element.contains(QLatin1String(check))) {
0155         return 0;
0156     }
0157     int id = elements->value(element, 0);
0158     if (!id) {
0159         ++counter;
0160         id = counter;
0161         elements->insert(element, id);
0162     }
0163     return id;
0164 }
0165 
0166 QStyle::StyleHint KStyle::newStyleHint(const QString &element)
0167 {
0168     return (StyleHint)newStyleElement(element, "SH_", d->hintCounter, &d->styleElements);
0169 }
0170 
0171 QStyle::ControlElement KStyle::newControlElement(const QString &element)
0172 {
0173     return (ControlElement)newStyleElement(element, "CE_", d->controlCounter, &d->styleElements);
0174 }
0175 
0176 KStyle::SubElement KStyle::newSubElement(const QString &element)
0177 {
0178     return (SubElement)newStyleElement(element, "SE_", d->subElementCounter, &d->styleElements);
0179 }
0180 
0181 void KStyle::polish(QWidget *w)
0182 {
0183     // Enable hover effects in all itemviews
0184     if (QAbstractItemView *itemView = qobject_cast<QAbstractItemView *>(w)) {
0185         itemView->viewport()->setAttribute(Qt::WA_Hover);
0186     }
0187 
0188     if (QDialogButtonBox *box = qobject_cast<QDialogButtonBox *>(w)) {
0189         QPushButton *button = box->button(QDialogButtonBox::Ok);
0190 
0191         if (button) {
0192             auto shortcut = new QShortcut(Qt::CTRL | Qt::Key_Return, button);
0193             QObject::connect(shortcut, &QShortcut::activated, button, &QPushButton::click);
0194         }
0195     }
0196     if (auto messageWidget = qobject_cast<KMessageWidget *>(w)) {
0197         KColorScheme scheme;
0198         QColor color;
0199         QPalette palette = messageWidget->palette();
0200         switch (messageWidget->messageType()) {
0201         case KMessageWidget::Positive:
0202             color = scheme.foreground(KColorScheme::PositiveText).color();
0203             break;
0204         case KMessageWidget::Information:
0205             color = scheme.foreground(KColorScheme::ActiveText).color();
0206             break;
0207         case KMessageWidget::Warning:
0208             color = scheme.foreground(KColorScheme::NeutralText).color();
0209             break;
0210         case KMessageWidget::Error:
0211             color = scheme.foreground(KColorScheme::NegativeText).color();
0212             break;
0213         }
0214         palette.setColor(QPalette::Window, color);
0215         messageWidget->setPalette(palette);
0216     }
0217     QCommonStyle::polish(w);
0218 }
0219 
0220 QPalette KStyle::standardPalette() const
0221 {
0222     return KColorScheme::createApplicationPalette(KSharedConfig::openConfig());
0223 }
0224 
0225 QIcon KStyle::standardIcon(StandardPixmap standardIcon, const QStyleOption *option, const QWidget *widget) const
0226 {
0227     switch (standardIcon) {
0228     case QStyle::SP_DesktopIcon:
0229         return QIcon::fromTheme(QStringLiteral("user-desktop"));
0230     case QStyle::SP_TrashIcon:
0231         return QIcon::fromTheme(QStringLiteral("user-trash"));
0232     case QStyle::SP_ComputerIcon:
0233         return QIcon::fromTheme(QStringLiteral("computer"));
0234     case QStyle::SP_DriveFDIcon:
0235         return QIcon::fromTheme(QStringLiteral("media-floppy"));
0236     case QStyle::SP_DriveHDIcon:
0237         return QIcon::fromTheme(QStringLiteral("drive-harddisk"));
0238     case QStyle::SP_DriveCDIcon:
0239     case QStyle::SP_DriveDVDIcon:
0240         return QIcon::fromTheme(QStringLiteral("drive-optical"));
0241     case QStyle::SP_DriveNetIcon:
0242         return QIcon::fromTheme(QStringLiteral("folder-remote"));
0243     case QStyle::SP_DirHomeIcon:
0244         return QIcon::fromTheme(QStringLiteral("user-home"));
0245     case QStyle::SP_DirOpenIcon:
0246         return QIcon::fromTheme(QStringLiteral("document-open-folder"));
0247     case QStyle::SP_DirClosedIcon:
0248         return QIcon::fromTheme(QStringLiteral("folder"));
0249     case QStyle::SP_DirIcon:
0250         return QIcon::fromTheme(QStringLiteral("folder"));
0251     case QStyle::SP_DirLinkIcon:
0252         return QIcon::fromTheme(QStringLiteral("folder")); // TODO: generate (!?) folder with link emblem
0253     case QStyle::SP_FileIcon:
0254         return QIcon::fromTheme(QStringLiteral("text-plain")); // TODO: look for a better icon
0255     case QStyle::SP_FileLinkIcon:
0256         return QIcon::fromTheme(QStringLiteral("text-plain")); // TODO: generate (!?) file with link emblem
0257     case QStyle::SP_FileDialogStart:
0258         return QIcon::fromTheme(QStringLiteral("media-playback-start")); // TODO: find correct icon
0259     case QStyle::SP_FileDialogEnd:
0260         return QIcon::fromTheme(QStringLiteral("media-playback-stop")); // TODO: find correct icon
0261     case QStyle::SP_FileDialogToParent:
0262         return QIcon::fromTheme(QStringLiteral("go-up"));
0263     case QStyle::SP_FileDialogNewFolder:
0264         return QIcon::fromTheme(QStringLiteral("folder-new"));
0265     case QStyle::SP_FileDialogDetailedView:
0266         return QIcon::fromTheme(QStringLiteral("view-list-details"));
0267     case QStyle::SP_FileDialogInfoView:
0268         return QIcon::fromTheme(QStringLiteral("document-properties"));
0269     case QStyle::SP_FileDialogContentsView:
0270         return QIcon::fromTheme(QStringLiteral("view-list-icons"));
0271     case QStyle::SP_FileDialogListView:
0272         return QIcon::fromTheme(QStringLiteral("view-list-text"));
0273     case QStyle::SP_FileDialogBack:
0274         return QIcon::fromTheme(QStringLiteral("go-previous"));
0275     case QStyle::SP_MessageBoxInformation:
0276         return QIcon::fromTheme(QStringLiteral("dialog-information"));
0277     case QStyle::SP_MessageBoxWarning:
0278         return QIcon::fromTheme(QStringLiteral("dialog-warning"));
0279     case QStyle::SP_MessageBoxCritical:
0280         return QIcon::fromTheme(QStringLiteral("dialog-error"));
0281     case QStyle::SP_MessageBoxQuestion:
0282         // This used to be dialog-information for a long time, so keep it as a fallback
0283         return QIcon::fromTheme(QStringLiteral("dialog-question"), QIcon::fromTheme(QStringLiteral("dialog-information")));
0284     case QStyle::SP_DialogOkButton:
0285         return QIcon::fromTheme(QStringLiteral("dialog-ok"));
0286     case QStyle::SP_DialogCancelButton:
0287         return QIcon::fromTheme(QStringLiteral("dialog-cancel"));
0288     case QStyle::SP_DialogHelpButton:
0289         return QIcon::fromTheme(QStringLiteral("help-contents"));
0290     case QStyle::SP_DialogOpenButton:
0291         return QIcon::fromTheme(QStringLiteral("document-open"));
0292     case QStyle::SP_DialogSaveButton:
0293         return QIcon::fromTheme(QStringLiteral("document-save"));
0294     case QStyle::SP_DialogCloseButton:
0295         return QIcon::fromTheme(QStringLiteral("dialog-close"));
0296     case QStyle::SP_DialogApplyButton:
0297         return QIcon::fromTheme(QStringLiteral("dialog-ok-apply"));
0298     case QStyle::SP_DialogResetButton:
0299         return QIcon::fromTheme(QStringLiteral("edit-undo"));
0300     case QStyle::SP_DialogDiscardButton:
0301         return QIcon::fromTheme(QStringLiteral("edit-delete"));
0302     case QStyle::SP_DialogYesButton:
0303         return QIcon::fromTheme(QStringLiteral("dialog-ok-apply"));
0304     case QStyle::SP_DialogNoButton:
0305         return QIcon::fromTheme(QStringLiteral("dialog-cancel"));
0306     case QStyle::SP_ArrowUp:
0307         return QIcon::fromTheme(QStringLiteral("go-up"));
0308     case QStyle::SP_ArrowDown:
0309         return QIcon::fromTheme(QStringLiteral("go-down"));
0310     case QStyle::SP_ArrowLeft:
0311         return QIcon::fromTheme(QStringLiteral("go-previous-view"));
0312     case QStyle::SP_ArrowRight:
0313         return QIcon::fromTheme(QStringLiteral("go-next-view"));
0314     case QStyle::SP_ArrowBack:
0315         return QIcon::fromTheme(QStringLiteral("go-previous"));
0316     case QStyle::SP_ArrowForward:
0317         return QIcon::fromTheme(QStringLiteral("go-next"));
0318     case QStyle::SP_BrowserReload:
0319         return QIcon::fromTheme(QStringLiteral("view-refresh"));
0320     case QStyle::SP_BrowserStop:
0321         return QIcon::fromTheme(QStringLiteral("process-stop"));
0322     case QStyle::SP_MediaPlay:
0323         return QIcon::fromTheme(QStringLiteral("media-playback-start"));
0324     case QStyle::SP_MediaStop:
0325         return QIcon::fromTheme(QStringLiteral("media-playback-stop"));
0326     case QStyle::SP_MediaPause:
0327         return QIcon::fromTheme(QStringLiteral("media-playback-pause"));
0328     case QStyle::SP_MediaSkipForward:
0329         return QIcon::fromTheme(QStringLiteral("media-skip-forward"));
0330     case QStyle::SP_MediaSkipBackward:
0331         return QIcon::fromTheme(QStringLiteral("media-skip-backward"));
0332     case QStyle::SP_MediaSeekForward:
0333         return QIcon::fromTheme(QStringLiteral("media-seek-forward"));
0334     case QStyle::SP_MediaSeekBackward:
0335         return QIcon::fromTheme(QStringLiteral("media-seek-backward"));
0336     case QStyle::SP_MediaVolume:
0337         return QIcon::fromTheme(QStringLiteral("audio-volume-medium"));
0338     case QStyle::SP_MediaVolumeMuted:
0339         return QIcon::fromTheme(QStringLiteral("audio-volume-muted"));
0340     case SP_LineEditClearButton: {
0341         const bool rtl = (option && option->direction == Qt::RightToLeft) || (!option && QApplication::isRightToLeft());
0342 
0343         const QString directionalThemeName = rtl ? QStringLiteral("edit-clear-locationbar-ltr") : QStringLiteral("edit-clear-locationbar-rtl");
0344 
0345         return QIcon::fromTheme(directionalThemeName, QIcon::fromTheme(QStringLiteral("edit-clear")));
0346     }
0347     case QStyle::SP_DialogYesToAllButton:
0348         return QIcon::fromTheme(QStringLiteral("dialog-ok"));
0349     case QStyle::SP_DialogNoToAllButton:
0350         return QIcon::fromTheme(QStringLiteral("dialog-cancel"));
0351     case QStyle::SP_DialogSaveAllButton:
0352         return QIcon::fromTheme(QStringLiteral("document-save-all"));
0353     case QStyle::SP_DialogAbortButton:
0354         return QIcon::fromTheme(QStringLiteral("dialog-cancel"));
0355     case QStyle::SP_DialogRetryButton:
0356         return QIcon::fromTheme(QStringLiteral("view-refresh"));
0357     case QStyle::SP_DialogIgnoreButton:
0358         return QIcon::fromTheme(QStringLiteral("dialog-cancel"));
0359     case QStyle::SP_RestoreDefaultsButton:
0360         return QIcon::fromTheme(QStringLiteral("document-revert"));
0361 
0362     default:
0363         break;
0364     }
0365 
0366     return QCommonStyle::standardIcon(standardIcon, option, widget);
0367 }
0368 
0369 int KStyle::styleHint(StyleHint hint, const QStyleOption *option, const QWidget *widget, QStyleHintReturn *returnData) const
0370 {
0371     switch (hint) {
0372     case SH_DialogButtonBox_ButtonsHaveIcons: {
0373         // was KGlobalSettings::showIconsOnPushButtons() :
0374         KConfigGroup g(KSharedConfig::openConfig(), QStringLiteral("KDE"));
0375         return g.readEntry("ShowIconsOnPushButtons", true);
0376     }
0377 
0378     case SH_ItemView_ArrowKeysNavigateIntoChildren:
0379         return true;
0380 
0381     case SH_Widget_Animate: {
0382         KConfigGroup g(KSharedConfig::openConfig(), QStringLiteral("KDE-Global GUI Settings"));
0383         return g.readEntry("GraphicEffectsLevel", true);
0384     }
0385 
0386     case QStyle::SH_Menu_SubMenuSloppyCloseTimeout:
0387         return 300;
0388 
0389     case SH_ToolButtonStyle: {
0390         KConfigGroup g(KSharedConfig::openConfig(), QStringLiteral("Toolbar style"));
0391 
0392         bool useOthertoolbars = false;
0393         const QWidget *parent = widget ? widget->parentWidget() : nullptr;
0394 
0395         // If the widget parent is a QToolBar and the magic property is set
0396         if (parent && qobject_cast<const QToolBar *>(parent)) {
0397             if (parent->property("otherToolbar").isValid()) {
0398                 useOthertoolbars = true;
0399             }
0400         }
0401 
0402         QString buttonStyle;
0403         if (useOthertoolbars) {
0404             buttonStyle = g.readEntry("ToolButtonStyleOtherToolbars", "NoText").toLower();
0405         } else {
0406             buttonStyle = g.readEntry("ToolButtonStyle", "TextBesideIcon").toLower();
0407         }
0408 
0409         return buttonStyle == QLatin1String("textbesideicon") ? Qt::ToolButtonTextBesideIcon
0410             : buttonStyle == QLatin1String("icontextright")   ? Qt::ToolButtonTextBesideIcon
0411             : buttonStyle == QLatin1String("textundericon")   ? Qt::ToolButtonTextUnderIcon
0412             : buttonStyle == QLatin1String("icontextbottom")  ? Qt::ToolButtonTextUnderIcon
0413             : buttonStyle == QLatin1String("textonly")        ? Qt::ToolButtonTextOnly
0414                                                               : Qt::ToolButtonIconOnly;
0415     }
0416 
0417     case SH_KCustomStyleElement:
0418         if (!widget) {
0419             return 0;
0420         }
0421 
0422         return d->styleElements.value(widget->objectName(), 0);
0423 
0424     case SH_ScrollBar_LeftClickAbsolutePosition: {
0425         KConfigGroup g(KSharedConfig::openConfig(), QStringLiteral("KDE"));
0426         return !g.readEntry("ScrollbarLeftClickNavigatesByPage", false);
0427     }
0428 
0429     default:
0430         break;
0431     };
0432 
0433     return QCommonStyle::styleHint(hint, option, widget, returnData);
0434 }
0435 
0436 int KStyle::pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const
0437 {
0438     switch (metric) {
0439     case PM_SmallIconSize:
0440     case PM_ButtonIconSize:
0441         return KIconLoader::global()->currentSize(KIconLoader::Small);
0442 
0443     case PM_ToolBarIconSize:
0444         return KIconLoader::global()->currentSize(KIconLoader::Toolbar);
0445 
0446     case PM_LargeIconSize:
0447         return KIconLoader::global()->currentSize(KIconLoader::Dialog);
0448 
0449     case PM_MessageBoxIconSize:
0450         // TODO return KIconLoader::global()->currentSize(KIconLoader::MessageBox);
0451         return KIconLoader::SizeHuge;
0452     default:
0453         break;
0454     }
0455 
0456     return QCommonStyle::pixelMetric(metric, option, widget);
0457 }
0458 
0459 #include "moc_kstyle.cpp"