File indexing completed on 2024-09-08 03:36:39
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"