1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtWidgets module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include <qglobal.h>
41#include "qstylesheetstyle_p.h"
42
43#if QT_CONFIG(style_stylesheet)
44
45#include "private/qcssutil_p.h"
46#include <qdebug.h>
47#include <qdir.h>
48#include <qapplication.h>
49#if QT_CONFIG(menu)
50#include <qmenu.h>
51#endif
52#if QT_CONFIG(menubar)
53#include <qmenubar.h>
54#endif
55#include <qpainter.h>
56#include <qstyleoption.h>
57#if QT_CONFIG(lineedit)
58#include <qlineedit.h>
59#endif
60#include <private/qwindowsstyle_p.h>
61#if QT_CONFIG(combobox)
62#include <qcombobox.h>
63#endif
64#include "private/qcssparser_p.h"
65#include "private/qmath_p.h"
66#include <qabstractscrollarea.h>
67#include "private/qabstractscrollarea_p.h"
68#include <qtooltip.h>
69#include <qshareddata.h>
70#if QT_CONFIG(toolbutton)
71#include <qtoolbutton.h>
72#endif
73#if QT_CONFIG(scrollbar)
74#include <qscrollbar.h>
75#endif
76#if QT_CONFIG(abstractslider)
77#include <qabstractslider.h>
78#endif
79#include <qstring.h>
80#include <qfile.h>
81#if QT_CONFIG(checkbox)
82#include <qcheckbox.h>
83#endif
84#if QT_CONFIG(itemviews)
85#include <qheaderview.h>
86#endif
87#include <private/qwindowsstyle_p_p.h>
88#if QT_CONFIG(animation)
89#include <private/qstyleanimation_p.h>
90#endif
91#if QT_CONFIG(tabbar)
92#include <qtabbar.h>
93#endif
94#include <QMetaProperty>
95#if QT_CONFIG(mainwindow)
96#include <qmainwindow.h>
97#endif
98#if QT_CONFIG(dockwidget)
99#include <qdockwidget.h>
100#endif
101#if QT_CONFIG(mdiarea)
102#include <qmdisubwindow.h>
103#endif
104#if QT_CONFIG(dialog)
105#include <qdialog.h>
106#endif
107#include <private/qwidget_p.h>
108#if QT_CONFIG(spinbox)
109#include <QAbstractSpinBox>
110#endif
111#if QT_CONFIG(label)
112#include <QLabel>
113#endif
114#include "qdrawutil.h"
115
116#include <limits.h>
117#if QT_CONFIG(toolbar)
118#include <QtWidgets/qtoolbar.h>
119#endif
120
121#include <QtGui/qscreen.h>
122
123QT_BEGIN_NAMESPACE
124
125using namespace QCss;
126
127
128class QStyleSheetStylePrivate : public QWindowsStylePrivate
129{
130 Q_DECLARE_PUBLIC(QStyleSheetStyle)
131public:
132 QStyleSheetStylePrivate() { }
133};
134
135
136static QStyleSheetStyleCaches *styleSheetCaches = 0;
137
138/* RECURSION_GUARD:
139 * the QStyleSheetStyle is a proxy. If used with others proxy style, we may end up with something like:
140 * QStyleSheetStyle -> ProxyStyle -> QStyleSheetStyle -> OriginalStyle
141 * Recursion may happen if the style call the widget()->style() again.
142 * Not to mention the performence penalty of having two lookup of rules.
143 *
144 * The first instance of QStyleSheetStyle will set globalStyleSheetStyle to itself. The second one
145 * will notice the globalStyleSheetStyle is not istelf and call its base style directly.
146 */
147static const QStyleSheetStyle *globalStyleSheetStyle = 0;
148class QStyleSheetStyleRecursionGuard
149{
150 public:
151 QStyleSheetStyleRecursionGuard(const QStyleSheetStyle *that)
152 : guarded(globalStyleSheetStyle == 0)
153 {
154 if (guarded) globalStyleSheetStyle = that;
155 }
156 ~QStyleSheetStyleRecursionGuard() { if (guarded) globalStyleSheetStyle = 0; }
157 bool guarded;
158};
159#define RECURSION_GUARD(RETURN) \
160 if (globalStyleSheetStyle != 0 && globalStyleSheetStyle != this) { RETURN; } \
161 QStyleSheetStyleRecursionGuard recursion_guard(this);
162
163#define ceil(x) ((int)(x) + ((x) > 0 && (x) != (int)(x)))
164
165enum PseudoElement {
166 PseudoElement_None,
167 PseudoElement_DownArrow,
168 PseudoElement_UpArrow,
169 PseudoElement_LeftArrow,
170 PseudoElement_RightArrow,
171 PseudoElement_Indicator,
172 PseudoElement_ExclusiveIndicator,
173 PseudoElement_PushButtonMenuIndicator,
174 PseudoElement_ComboBoxDropDown,
175 PseudoElement_ComboBoxArrow,
176 PseudoElement_Item,
177 PseudoElement_SpinBoxUpButton,
178 PseudoElement_SpinBoxUpArrow,
179 PseudoElement_SpinBoxDownButton,
180 PseudoElement_SpinBoxDownArrow,
181 PseudoElement_GroupBoxTitle,
182 PseudoElement_GroupBoxIndicator,
183 PseudoElement_ToolButtonMenu,
184 PseudoElement_ToolButtonMenuArrow,
185 PseudoElement_ToolButtonDownArrow,
186 PseudoElement_ToolBoxTab,
187 PseudoElement_ScrollBarSlider,
188 PseudoElement_ScrollBarAddPage,
189 PseudoElement_ScrollBarSubPage,
190 PseudoElement_ScrollBarAddLine,
191 PseudoElement_ScrollBarSubLine,
192 PseudoElement_ScrollBarFirst,
193 PseudoElement_ScrollBarLast,
194 PseudoElement_ScrollBarUpArrow,
195 PseudoElement_ScrollBarDownArrow,
196 PseudoElement_ScrollBarLeftArrow,
197 PseudoElement_ScrollBarRightArrow,
198 PseudoElement_SplitterHandle,
199 PseudoElement_ToolBarHandle,
200 PseudoElement_ToolBarSeparator,
201 PseudoElement_MenuScroller,
202 PseudoElement_MenuTearoff,
203 PseudoElement_MenuCheckMark,
204 PseudoElement_MenuSeparator,
205 PseudoElement_MenuIcon,
206 PseudoElement_MenuRightArrow,
207 PseudoElement_TreeViewBranch,
208 PseudoElement_HeaderViewSection,
209 PseudoElement_HeaderViewUpArrow,
210 PseudoElement_HeaderViewDownArrow,
211 PseudoElement_ProgressBarChunk,
212 PseudoElement_TabBarTab,
213 PseudoElement_TabBarScroller,
214 PseudoElement_TabBarTear,
215 PseudoElement_SliderGroove,
216 PseudoElement_SliderHandle,
217 PseudoElement_SliderAddPage,
218 PseudoElement_SliderSubPage,
219 PseudoElement_SliderTickmark,
220 PseudoElement_TabWidgetPane,
221 PseudoElement_TabWidgetTabBar,
222 PseudoElement_TabWidgetLeftCorner,
223 PseudoElement_TabWidgetRightCorner,
224 PseudoElement_DockWidgetTitle,
225 PseudoElement_DockWidgetCloseButton,
226 PseudoElement_DockWidgetFloatButton,
227 PseudoElement_DockWidgetSeparator,
228 PseudoElement_MdiCloseButton,
229 PseudoElement_MdiMinButton,
230 PseudoElement_MdiNormalButton,
231 PseudoElement_TitleBar,
232 PseudoElement_TitleBarCloseButton,
233 PseudoElement_TitleBarMinButton,
234 PseudoElement_TitleBarMaxButton,
235 PseudoElement_TitleBarShadeButton,
236 PseudoElement_TitleBarUnshadeButton,
237 PseudoElement_TitleBarNormalButton,
238 PseudoElement_TitleBarContextHelpButton,
239 PseudoElement_TitleBarSysMenu,
240 PseudoElement_ViewItem,
241 PseudoElement_ViewItemIcon,
242 PseudoElement_ViewItemText,
243 PseudoElement_ViewItemIndicator,
244 PseudoElement_ScrollAreaCorner,
245 PseudoElement_TabBarTabCloseButton,
246 NumPseudoElements
247};
248
249struct PseudoElementInfo {
250 QStyle::SubControl subControl;
251 const char name[19];
252};
253
254static const PseudoElementInfo knownPseudoElements[NumPseudoElements] = {
255 { QStyle::SC_None, "" },
256 { QStyle::SC_None, "down-arrow" },
257 { QStyle::SC_None, "up-arrow" },
258 { QStyle::SC_None, "left-arrow" },
259 { QStyle::SC_None, "right-arrow" },
260 { QStyle::SC_None, "indicator" },
261 { QStyle::SC_None, "indicator" },
262 { QStyle::SC_None, "menu-indicator" },
263 { QStyle::SC_ComboBoxArrow, "drop-down" },
264 { QStyle::SC_ComboBoxArrow, "down-arrow" },
265 { QStyle::SC_None, "item" },
266 { QStyle::SC_SpinBoxUp, "up-button" },
267 { QStyle::SC_SpinBoxUp, "up-arrow" },
268 { QStyle::SC_SpinBoxDown, "down-button" },
269 { QStyle::SC_SpinBoxDown, "down-arrow" },
270 { QStyle::SC_GroupBoxLabel, "title" },
271 { QStyle::SC_GroupBoxCheckBox, "indicator" },
272 { QStyle::SC_ToolButtonMenu, "menu-button" },
273 { QStyle::SC_ToolButtonMenu, "menu-arrow" },
274 { QStyle::SC_None, "menu-indicator" },
275 { QStyle::SC_None, "tab" },
276 { QStyle::SC_ScrollBarSlider, "handle" },
277 { QStyle::SC_ScrollBarAddPage, "add-page" },
278 { QStyle::SC_ScrollBarSubPage, "sub-page" },
279 { QStyle::SC_ScrollBarAddLine, "add-line" },
280 { QStyle::SC_ScrollBarSubLine, "sub-line" },
281 { QStyle::SC_ScrollBarFirst, "first" },
282 { QStyle::SC_ScrollBarLast, "last" },
283 { QStyle::SC_ScrollBarSubLine, "up-arrow" },
284 { QStyle::SC_ScrollBarAddLine, "down-arrow" },
285 { QStyle::SC_ScrollBarSubLine, "left-arrow" },
286 { QStyle::SC_ScrollBarAddLine, "right-arrow" },
287 { QStyle::SC_None, "handle" },
288 { QStyle::SC_None, "handle" },
289 { QStyle::SC_None, "separator" },
290 { QStyle::SC_None, "scroller" },
291 { QStyle::SC_None, "tearoff" },
292 { QStyle::SC_None, "indicator" },
293 { QStyle::SC_None, "separator" },
294 { QStyle::SC_None, "icon" },
295 { QStyle::SC_None, "right-arrow" },
296 { QStyle::SC_None, "branch" },
297 { QStyle::SC_None, "section" },
298 { QStyle::SC_None, "down-arrow" },
299 { QStyle::SC_None, "up-arrow" },
300 { QStyle::SC_None, "chunk" },
301 { QStyle::SC_None, "tab" },
302 { QStyle::SC_None, "scroller" },
303 { QStyle::SC_None, "tear" },
304 { QStyle::SC_SliderGroove, "groove" },
305 { QStyle::SC_SliderHandle, "handle" },
306 { QStyle::SC_None, "add-page" },
307 { QStyle::SC_None, "sub-page"},
308 { QStyle::SC_SliderTickmarks, "tick-mark" },
309 { QStyle::SC_None, "pane" },
310 { QStyle::SC_None, "tab-bar" },
311 { QStyle::SC_None, "left-corner" },
312 { QStyle::SC_None, "right-corner" },
313 { QStyle::SC_None, "title" },
314 { QStyle::SC_None, "close-button" },
315 { QStyle::SC_None, "float-button" },
316 { QStyle::SC_None, "separator" },
317 { QStyle::SC_MdiCloseButton, "close-button" },
318 { QStyle::SC_MdiMinButton, "minimize-button" },
319 { QStyle::SC_MdiNormalButton, "normal-button" },
320 { QStyle::SC_TitleBarLabel, "title" },
321 { QStyle::SC_TitleBarCloseButton, "close-button" },
322 { QStyle::SC_TitleBarMinButton, "minimize-button" },
323 { QStyle::SC_TitleBarMaxButton, "maximize-button" },
324 { QStyle::SC_TitleBarShadeButton, "shade-button" },
325 { QStyle::SC_TitleBarUnshadeButton, "unshade-button" },
326 { QStyle::SC_TitleBarNormalButton, "normal-button" },
327 { QStyle::SC_TitleBarContextHelpButton, "contexthelp-button" },
328 { QStyle::SC_TitleBarSysMenu, "sys-menu" },
329 { QStyle::SC_None, "item" },
330 { QStyle::SC_None, "icon" },
331 { QStyle::SC_None, "text" },
332 { QStyle::SC_None, "indicator" },
333 { QStyle::SC_None, "corner" },
334 { QStyle::SC_None, "close-button" },
335};
336
337
338struct QStyleSheetBorderImageData : public QSharedData
339{
340 QStyleSheetBorderImageData()
341 : horizStretch(QCss::TileMode_Unknown), vertStretch(QCss::TileMode_Unknown)
342 {
343 for (int i = 0; i < 4; i++)
344 cuts[i] = -1;
345 }
346 int cuts[4];
347 QPixmap pixmap;
348 QImage image;
349 QCss::TileMode horizStretch, vertStretch;
350};
351
352struct QStyleSheetBackgroundData : public QSharedData
353{
354 QStyleSheetBackgroundData(const QBrush& b, const QPixmap& p, QCss::Repeat r,
355 Qt::Alignment a, QCss::Origin o, Attachment t, QCss::Origin c)
356 : brush(b), pixmap(p), repeat(r), position(a), origin(o), attachment(t), clip(c) { }
357
358 bool isTransparent() const {
359 if (brush.style() != Qt::NoBrush)
360 return !brush.isOpaque();
361 return pixmap.isNull() ? false : pixmap.hasAlpha();
362 }
363 QBrush brush;
364 QPixmap pixmap;
365 QCss::Repeat repeat;
366 Qt::Alignment position;
367 QCss::Origin origin;
368 QCss::Attachment attachment;
369 QCss::Origin clip;
370};
371
372struct QStyleSheetBorderData : public QSharedData
373{
374 QStyleSheetBorderData() : bi(0)
375 {
376 for (int i = 0; i < 4; i++) {
377 borders[i] = 0;
378 styles[i] = QCss::BorderStyle_None;
379 }
380 }
381
382 QStyleSheetBorderData(int *b, QBrush *c, QCss::BorderStyle *s, QSize *r) : bi(0)
383 {
384 for (int i = 0; i < 4; i++) {
385 borders[i] = b[i];
386 styles[i] = s[i];
387 colors[i] = c[i];
388 radii[i] = r[i];
389 }
390 }
391
392 int borders[4];
393 QBrush colors[4];
394 QCss::BorderStyle styles[4];
395 QSize radii[4]; // topleft, topright, bottomleft, bottomright
396
397 const QStyleSheetBorderImageData *borderImage() const
398 { return bi; }
399 bool hasBorderImage() const { return bi!=0; }
400
401 QSharedDataPointer<QStyleSheetBorderImageData> bi;
402
403 bool isOpaque() const
404 {
405 for (int i = 0; i < 4; i++) {
406 if (styles[i] == QCss::BorderStyle_Native || styles[i] == QCss::BorderStyle_None)
407 continue;
408 if (styles[i] >= QCss::BorderStyle_Dotted && styles[i] <= QCss::BorderStyle_DotDotDash
409 && styles[i] != BorderStyle_Solid)
410 return false;
411 if (!colors[i].isOpaque())
412 return false;
413 if (!radii[i].isEmpty())
414 return false;
415 }
416 if (bi != 0 && bi->pixmap.hasAlpha())
417 return false;
418 return true;
419 }
420};
421
422
423struct QStyleSheetOutlineData : public QStyleSheetBorderData
424{
425 QStyleSheetOutlineData()
426 {
427 for (int i = 0; i < 4; i++) {
428 offsets[i] = 0;
429 }
430 }
431
432 QStyleSheetOutlineData(int *b, QBrush *c, QCss::BorderStyle *s, QSize *r, int *o)
433 : QStyleSheetBorderData(b, c, s, r)
434 {
435 for (int i = 0; i < 4; i++) {
436 offsets[i] = o[i];
437 }
438 }
439
440 int offsets[4];
441};
442
443struct QStyleSheetBoxData : public QSharedData
444{
445 QStyleSheetBoxData(int *m, int *p, int s) : spacing(s)
446 {
447 for (int i = 0; i < 4; i++) {
448 margins[i] = m[i];
449 paddings[i] = p[i];
450 }
451 }
452
453 int margins[4];
454 int paddings[4];
455
456 int spacing;
457};
458
459struct QStyleSheetPaletteData : public QSharedData
460{
461 QStyleSheetPaletteData(const QBrush &fg, const QBrush &sfg, const QBrush &sbg,
462 const QBrush &abg)
463 : foreground(fg), selectionForeground(sfg), selectionBackground(sbg),
464 alternateBackground(abg) { }
465
466 QBrush foreground;
467 QBrush selectionForeground;
468 QBrush selectionBackground;
469 QBrush alternateBackground;
470};
471
472struct QStyleSheetGeometryData : public QSharedData
473{
474 QStyleSheetGeometryData(int w, int h, int minw, int minh, int maxw, int maxh)
475 : minWidth(minw), minHeight(minh), width(w), height(h), maxWidth(maxw), maxHeight(maxh) { }
476
477 int minWidth, minHeight, width, height, maxWidth, maxHeight;
478};
479
480struct QStyleSheetPositionData : public QSharedData
481{
482 QStyleSheetPositionData(int l, int t, int r, int b, Origin o, Qt::Alignment p, QCss::PositionMode m, Qt::Alignment a = 0)
483 : left(l), top(t), bottom(b), right(r), origin(o), position(p), mode(m), textAlignment(a) { }
484
485 int left, top, bottom, right;
486 Origin origin;
487 Qt::Alignment position;
488 QCss::PositionMode mode;
489 Qt::Alignment textAlignment;
490};
491
492struct QStyleSheetImageData : public QSharedData
493{
494 QStyleSheetImageData(const QIcon &i, Qt::Alignment a, const QSize &sz)
495 : icon(i), alignment(a), size(sz) { }
496
497 QIcon icon;
498 Qt::Alignment alignment;
499 QSize size;
500};
501
502class QRenderRule
503{
504public:
505 QRenderRule() : features(0), hasFont(false), pal(0), b(0), bg(0), bd(0), ou(0), geo(0), p(0), img(0), clipset(0) { }
506 QRenderRule(const QVector<QCss::Declaration> &, const QObject *);
507
508 QRect borderRect(const QRect &r) const;
509 QRect outlineRect(const QRect &r) const;
510 QRect paddingRect(const QRect &r) const;
511 QRect contentsRect(const QRect &r) const;
512
513 enum { Margin = 1, Border = 2, Padding = 4, All=Margin|Border|Padding };
514 QRect boxRect(const QRect &r, int flags = All) const;
515 QSize boxSize(const QSize &s, int flags = All) const;
516 QRect originRect(const QRect &rect, Origin origin) const;
517
518 QPainterPath borderClip(QRect rect);
519 void drawBorder(QPainter *, const QRect&);
520 void drawOutline(QPainter *, const QRect&);
521 void drawBorderImage(QPainter *, const QRect&);
522 void drawBackground(QPainter *, const QRect&, const QPoint& = QPoint(0, 0));
523 void drawBackgroundImage(QPainter *, const QRect&, QPoint = QPoint(0, 0));
524 void drawFrame(QPainter *, const QRect&);
525 void drawImage(QPainter *p, const QRect &rect);
526 void drawRule(QPainter *, const QRect&);
527 void configurePalette(QPalette *, QPalette::ColorGroup, const QWidget *, bool);
528 void configurePalette(QPalette *p, QPalette::ColorRole fr, QPalette::ColorRole br);
529
530 const QStyleSheetPaletteData *palette() const { return pal; }
531 const QStyleSheetBoxData *box() const { return b; }
532 const QStyleSheetBackgroundData *background() const { return bg; }
533 const QStyleSheetBorderData *border() const { return bd; }
534 const QStyleSheetOutlineData *outline() const { return ou; }
535 const QStyleSheetGeometryData *geometry() const { return geo; }
536 const QStyleSheetPositionData *position() const { return p; }
537
538 bool hasModification() const;
539
540 bool hasPalette() const { return pal != 0; }
541 bool hasBackground() const { return bg != 0 && (!bg->pixmap.isNull() || bg->brush.style() != Qt::NoBrush); }
542 bool hasGradientBackground() const { return bg && bg->brush.style() >= Qt::LinearGradientPattern
543 && bg->brush.style() <= Qt::ConicalGradientPattern; }
544
545 bool hasNativeBorder() const {
546 return bd == 0
547 || (!bd->hasBorderImage() && bd->styles[0] == BorderStyle_Native);
548 }
549
550 bool hasNativeOutline() const {
551 return (ou == 0
552 || (!ou->hasBorderImage() && ou->styles[0] == BorderStyle_Native));
553 }
554
555 bool baseStyleCanDraw() const {
556 if (!hasBackground() || (background()->brush.style() == Qt::NoBrush && bg->pixmap.isNull()))
557 return true;
558 if (bg && !bg->pixmap.isNull())
559 return false;
560 if (hasGradientBackground())
561 return features & StyleFeature_BackgroundGradient;
562 return features & StyleFeature_BackgroundColor;
563 }
564
565 bool hasBox() const { return b != 0; }
566 bool hasBorder() const { return bd != 0; }
567 bool hasOutline() const { return ou != 0; }
568 bool hasPosition() const { return p != 0; }
569 bool hasGeometry() const { return geo != 0; }
570 bool hasDrawable() const { return !hasNativeBorder() || hasBackground() || hasImage(); }
571 bool hasImage() const { return img != 0; }
572
573 QSize minimumContentsSize() const
574 { return geo ? QSize(geo->minWidth, geo->minHeight) : QSize(0, 0); }
575 QSize minimumSize() const
576 { return boxSize(minimumContentsSize()); }
577
578 QSize contentsSize() const
579 { return geo ? QSize(geo->width, geo->height)
580 : ((img && img->size.isValid()) ? img->size : QSize()); }
581 QSize contentsSize(const QSize &sz) const
582 {
583 QSize csz = contentsSize();
584 if (csz.width() == -1) csz.setWidth(sz.width());
585 if (csz.height() == -1) csz.setHeight(sz.height());
586 return csz;
587 }
588 bool hasContentsSize() const
589 { return (geo && (geo->width != -1 || geo->height != -1)) || (img && img->size.isValid()); }
590
591 QSize size() const { return boxSize(contentsSize()); }
592 QSize size(const QSize &sz) const { return boxSize(contentsSize(sz)); }
593 QSize adjustSize(const QSize &sz)
594 {
595 if (!geo)
596 return sz;
597 QSize csz = contentsSize();
598 if (csz.width() == -1) csz.setWidth(sz.width());
599 if (csz.height() == -1) csz.setHeight(sz.height());
600 if (geo->maxWidth != -1 && csz.width() > geo->maxWidth) csz.setWidth(geo->maxWidth);
601 if (geo->maxHeight != -1 && csz.height() > geo->maxHeight) csz.setHeight(geo->maxHeight);
602 csz=csz.expandedTo(QSize(geo->minWidth, geo->minHeight));
603 return csz;
604 }
605
606 bool hasStyleHint(const QString &sh) const { return styleHints.contains(sh); }
607 QVariant styleHint(const QString &sh) const { return styleHints.value(sh); }
608
609 void fixupBorder(int);
610
611 // Shouldn't be here
612 void setClip(QPainter *p, const QRect &rect);
613 void unsetClip(QPainter *);
614
615public:
616 int features;
617 QBrush defaultBackground;
618 QFont font;
619 bool hasFont;
620
621 QHash<QString, QVariant> styleHints;
622
623 QSharedDataPointer<QStyleSheetPaletteData> pal;
624 QSharedDataPointer<QStyleSheetBoxData> b;
625 QSharedDataPointer<QStyleSheetBackgroundData> bg;
626 QSharedDataPointer<QStyleSheetBorderData> bd;
627 QSharedDataPointer<QStyleSheetOutlineData> ou;
628 QSharedDataPointer<QStyleSheetGeometryData> geo;
629 QSharedDataPointer<QStyleSheetPositionData> p;
630 QSharedDataPointer<QStyleSheetImageData> img;
631
632 int clipset;
633 QPainterPath clipPath;
634};
635Q_DECLARE_TYPEINFO(QRenderRule, Q_MOVABLE_TYPE);
636
637///////////////////////////////////////////////////////////////////////////////////////////
638static const char knownStyleHints[][45] = {
639 "activate-on-singleclick",
640 "alignment",
641 "arrow-keys-navigate-into-children",
642 "backward-icon",
643 "button-layout",
644 "cd-icon",
645 "combobox-list-mousetracking",
646 "combobox-popup",
647 "computer-icon",
648 "desktop-icon",
649 "dialog-apply-icon",
650 "dialog-cancel-icon",
651 "dialog-close-icon",
652 "dialog-discard-icon",
653 "dialog-help-icon",
654 "dialog-no-icon",
655 "dialog-ok-icon",
656 "dialog-open-icon",
657 "dialog-reset-icon",
658 "dialog-save-icon",
659 "dialog-yes-icon",
660 "dialogbuttonbox-buttons-have-icons",
661 "directory-closed-icon",
662 "directory-icon",
663 "directory-link-icon",
664 "directory-open-icon",
665 "dither-disable-text",
666 "dockwidget-close-icon",
667 "downarrow-icon",
668 "dvd-icon",
669 "etch-disabled-text",
670 "file-icon",
671 "file-link-icon",
672 "filedialog-backward-icon", // unused
673 "filedialog-contentsview-icon",
674 "filedialog-detailedview-icon",
675 "filedialog-end-icon",
676 "filedialog-infoview-icon",
677 "filedialog-listview-icon",
678 "filedialog-new-directory-icon",
679 "filedialog-parent-directory-icon",
680 "filedialog-start-icon",
681 "floppy-icon",
682 "forward-icon",
683 "gridline-color",
684 "harddisk-icon",
685 "home-icon",
686 "icon-size",
687 "leftarrow-icon",
688 "lineedit-password-character",
689 "lineedit-password-mask-delay",
690 "mdi-fill-space-on-maximize",
691 "menu-scrollable",
692 "menubar-altkey-navigation",
693 "menubar-separator",
694 "messagebox-critical-icon",
695 "messagebox-information-icon",
696 "messagebox-question-icon",
697 "messagebox-text-interaction-flags",
698 "messagebox-warning-icon",
699 "mouse-tracking",
700 "network-icon",
701 "opacity",
702 "paint-alternating-row-colors-for-empty-area",
703 "rightarrow-icon",
704 "scrollbar-contextmenu",
705 "scrollbar-leftclick-absolute-position",
706 "scrollbar-middleclick-absolute-position",
707 "scrollbar-roll-between-buttons",
708 "scrollbar-scroll-when-pointer-leaves-control",
709 "scrollview-frame-around-contents",
710 "show-decoration-selected",
711 "spinbox-click-autorepeat-rate",
712 "spincontrol-disable-on-bounds",
713 "tabbar-elide-mode",
714 "tabbar-prefer-no-arrows",
715 "titlebar-close-icon",
716 "titlebar-contexthelp-icon",
717 "titlebar-maximize-icon",
718 "titlebar-menu-icon",
719 "titlebar-minimize-icon",
720 "titlebar-normal-icon",
721 "titlebar-shade-icon",
722 "titlebar-show-tooltips-on-buttons",
723 "titlebar-unshade-icon",
724 "toolbutton-popup-delay",
725 "trash-icon",
726 "uparrow-icon",
727 "widget-animation-duration"
728};
729
730static const int numKnownStyleHints = sizeof(knownStyleHints)/sizeof(knownStyleHints[0]);
731
732static QList<QVariant> subControlLayout(const QString& layout)
733{
734 QList<QVariant> buttons;
735 for (int i = 0; i < layout.count(); i++) {
736 int button = layout[i].toLatin1();
737 switch (button) {
738 case 'm':
739 buttons.append(PseudoElement_MdiMinButton);
740 buttons.append(PseudoElement_TitleBarMinButton);
741 break;
742 case 'M':
743 buttons.append(PseudoElement_TitleBarMaxButton);
744 break;
745 case 'X':
746 buttons.append(PseudoElement_MdiCloseButton);
747 buttons.append(PseudoElement_TitleBarCloseButton);
748 break;
749 case 'N':
750 buttons.append(PseudoElement_MdiNormalButton);
751 buttons.append(PseudoElement_TitleBarNormalButton);
752 break;
753 case 'I':
754 buttons.append(PseudoElement_TitleBarSysMenu);
755 break;
756 case 'T':
757 buttons.append(PseudoElement_TitleBar);
758 break;
759 case 'H':
760 buttons.append(PseudoElement_TitleBarContextHelpButton);
761 break;
762 case 'S':
763 buttons.append(PseudoElement_TitleBarShadeButton);
764 break;
765 default:
766 buttons.append(button);
767 break;
768 }
769 }
770 return buttons;
771}
772
773namespace {
774 struct ButtonInfo {
775 QRenderRule rule;
776 int element;
777 int offset;
778 int where;
779 int width;
780 };
781}
782template <> class QTypeInfo<ButtonInfo> : public QTypeInfoMerger<ButtonInfo, QRenderRule, int> {};
783
784QHash<QStyle::SubControl, QRect> QStyleSheetStyle::titleBarLayout(const QWidget *w, const QStyleOptionTitleBar *tb) const
785{
786 QHash<QStyle::SubControl, QRect> layoutRects;
787 const bool isMinimized = tb->titleBarState & Qt::WindowMinimized;
788 const bool isMaximized = tb->titleBarState & Qt::WindowMaximized;
789 QRenderRule subRule = renderRule(w, tb);
790 QRect cr = subRule.contentsRect(tb->rect);
791 QList<QVariant> layout = subRule.styleHint(QLatin1String("button-layout")).toList();
792 if (layout.isEmpty())
793 layout = subControlLayout(QLatin1String("I(T)HSmMX"));
794
795 int offsets[3] = { 0, 0, 0 };
796 enum Where { Left, Right, Center, NoWhere } where = Left;
797 QVector<ButtonInfo> infos;
798 const int numLayouts = layout.size();
799 infos.reserve(numLayouts);
800 for (int i = 0; i < numLayouts; i++) {
801 const int element = layout[i].toInt();
802 if (element == '(') {
803 where = Center;
804 } else if (element == ')') {
805 where = Right;
806 } else {
807 ButtonInfo info;
808 info.element = element;
809 switch (element) {
810 case PseudoElement_TitleBar:
811 if (!(tb->titleBarFlags & (Qt::WindowTitleHint | Qt::WindowSystemMenuHint)))
812 continue;
813 break;
814 case PseudoElement_TitleBarContextHelpButton:
815 if (!(tb->titleBarFlags & Qt::WindowContextHelpButtonHint))
816 continue;
817 break;
818 case PseudoElement_TitleBarMinButton:
819 if (!(tb->titleBarFlags & Qt::WindowMinimizeButtonHint))
820 continue;
821 if (isMinimized)
822 info.element = PseudoElement_TitleBarNormalButton;
823 break;
824 case PseudoElement_TitleBarMaxButton:
825 if (!(tb->titleBarFlags & Qt::WindowMaximizeButtonHint))
826 continue;
827 if (isMaximized)
828 info.element = PseudoElement_TitleBarNormalButton;
829 break;
830 case PseudoElement_TitleBarShadeButton:
831 if (!(tb->titleBarFlags & Qt::WindowShadeButtonHint))
832 continue;
833 if (isMinimized)
834 info.element = PseudoElement_TitleBarUnshadeButton;
835 break;
836 case PseudoElement_TitleBarCloseButton:
837 case PseudoElement_TitleBarSysMenu:
838 if (!(tb->titleBarFlags & Qt::WindowSystemMenuHint))
839 continue;
840 break;
841 default:
842 continue;
843 }
844 if (info.element == PseudoElement_TitleBar) {
845 info.width = tb->fontMetrics.horizontalAdvance(tb->text) + 6;
846 subRule.geo = new QStyleSheetGeometryData(info.width, tb->fontMetrics.height(), -1, -1, -1, -1);
847 } else {
848 subRule = renderRule(w, tb, info.element);
849 info.width = subRule.size().width();
850 }
851 info.rule = subRule;
852 info.offset = offsets[where];
853 info.where = where;
854 infos.append(std::move(info));
855
856 offsets[where] += info.width;
857 }
858 }
859
860 for (int i = 0; i < infos.size(); i++) {
861 const ButtonInfo &info = infos[i];
862 QRect lr = cr;
863 switch (info.where) {
864 case Center: {
865 lr.setLeft(cr.left() + offsets[Left]);
866 lr.setRight(cr.right() - offsets[Right]);
867 QRect r(0, 0, offsets[Center], lr.height());
868 r.moveCenter(lr.center());
869 r.setLeft(r.left()+info.offset);
870 r.setWidth(info.width);
871 lr = r;
872 break; }
873 case Left:
874 lr.translate(info.offset, 0);
875 lr.setWidth(info.width);
876 break;
877 case Right:
878 lr.moveLeft(cr.right() + 1 - offsets[Right] + info.offset);
879 lr.setWidth(info.width);
880 break;
881 default:
882 break;
883 }
884 QStyle::SubControl control = knownPseudoElements[info.element].subControl;
885 layoutRects[control] = positionRect(w, info.rule, info.element, lr, tb->direction);
886 }
887
888 return layoutRects;
889}
890
891static QStyle::StandardPixmap subControlIcon(int pe)
892{
893 switch (pe) {
894 case PseudoElement_MdiCloseButton: return QStyle::SP_TitleBarCloseButton;
895 case PseudoElement_MdiMinButton: return QStyle::SP_TitleBarMinButton;
896 case PseudoElement_MdiNormalButton: return QStyle::SP_TitleBarNormalButton;
897 case PseudoElement_TitleBarCloseButton: return QStyle::SP_TitleBarCloseButton;
898 case PseudoElement_TitleBarMinButton: return QStyle::SP_TitleBarMinButton;
899 case PseudoElement_TitleBarMaxButton: return QStyle::SP_TitleBarMaxButton;
900 case PseudoElement_TitleBarShadeButton: return QStyle::SP_TitleBarShadeButton;
901 case PseudoElement_TitleBarUnshadeButton: return QStyle::SP_TitleBarUnshadeButton;
902 case PseudoElement_TitleBarNormalButton: return QStyle::SP_TitleBarNormalButton;
903 case PseudoElement_TitleBarContextHelpButton: return QStyle::SP_TitleBarContextHelpButton;
904 default: break;
905 }
906 return QStyle::SP_CustomBase;
907}
908
909QRenderRule::QRenderRule(const QVector<Declaration> &declarations, const QObject *object)
910: features(0), hasFont(false), pal(0), b(0), bg(0), bd(0), ou(0), geo(0), p(0), img(0), clipset(0)
911{
912 QPalette palette = QGuiApplication::palette(); // ###: ideally widget's palette
913 ValueExtractor v(declarations, palette);
914 features = v.extractStyleFeatures();
915
916 int w = -1, h = -1, minw = -1, minh = -1, maxw = -1, maxh = -1;
917 if (v.extractGeometry(&w, &h, &minw, &minh, &maxw, &maxh))
918 geo = new QStyleSheetGeometryData(w, h, minw, minh, maxw, maxh);
919
920 int left = 0, top = 0, right = 0, bottom = 0;
921 Origin origin = Origin_Unknown;
922 Qt::Alignment position = 0;
923 QCss::PositionMode mode = PositionMode_Unknown;
924 Qt::Alignment textAlignment = 0;
925 if (v.extractPosition(&left, &top, &right, &bottom, &origin, &position, &mode, &textAlignment))
926 p = new QStyleSheetPositionData(left, top, right, bottom, origin, position, mode, textAlignment);
927
928 int margins[4], paddings[4], spacing = -1;
929 for (int i = 0; i < 4; i++)
930 margins[i] = paddings[i] = 0;
931 if (v.extractBox(margins, paddings, &spacing))
932 b = new QStyleSheetBoxData(margins, paddings, spacing);
933
934 int borders[4];
935 QBrush colors[4];
936 QCss::BorderStyle styles[4];
937 QSize radii[4];
938 for (int i = 0; i < 4; i++) {
939 borders[i] = 0;
940 styles[i] = BorderStyle_None;
941 }
942 if (v.extractBorder(borders, colors, styles, radii))
943 bd = new QStyleSheetBorderData(borders, colors, styles, radii);
944
945 int offsets[4];
946 for (int i = 0; i < 4; i++) {
947 borders[i] = offsets[i] = 0;
948 styles[i] = BorderStyle_None;
949 }
950 if (v.extractOutline(borders, colors, styles, radii, offsets))
951 ou = new QStyleSheetOutlineData(borders, colors, styles, radii, offsets);
952
953 QBrush brush;
954 QString uri;
955 Repeat repeat = Repeat_XY;
956 Qt::Alignment alignment = Qt::AlignTop | Qt::AlignLeft;
957 Attachment attachment = Attachment_Scroll;
958 origin = Origin_Padding;
959 Origin clip = Origin_Border;
960 if (v.extractBackground(&brush, &uri, &repeat, &alignment, &origin, &attachment, &clip)) {
961 QPixmap pixmap = QStyleSheetStyle::loadPixmap(uri, object);
962 if (!uri.isEmpty() && pixmap.isNull())
963 qWarning("Could not create pixmap from %s", qPrintable(QDir::toNativeSeparators(uri)));
964 bg = new QStyleSheetBackgroundData(brush, pixmap, repeat, alignment, origin, attachment, clip);
965 }
966
967 QBrush sfg, fg;
968 QBrush sbg, abg;
969 if (v.extractPalette(&fg, &sfg, &sbg, &abg))
970 pal = new QStyleSheetPaletteData(fg, sfg, sbg, abg);
971
972 QIcon icon;
973 alignment = Qt::AlignCenter;
974 QSize size;
975 if (v.extractImage(&icon, &alignment, &size))
976 img = new QStyleSheetImageData(icon, alignment, size);
977
978 int adj = -255;
979 hasFont = v.extractFont(&font, &adj);
980
981#ifndef QT_NO_TOOLTIP
982 if (object && qstrcmp(object->metaObject()->className(), "QTipLabel") == 0)
983 palette = QToolTip::palette();
984#endif
985
986 for (int i = 0; i < declarations.count(); i++) {
987 const Declaration& decl = declarations.at(i);
988 if (decl.d->propertyId == BorderImage) {
989 QString uri;
990 QCss::TileMode horizStretch, vertStretch;
991 int cuts[4];
992
993 decl.borderImageValue(&uri, cuts, &horizStretch, &vertStretch);
994 if (uri.isEmpty() || uri == QLatin1String("none")) {
995 if (bd && bd->bi)
996 bd->bi->pixmap = QPixmap();
997 } else {
998 if (!bd)
999 bd = new QStyleSheetBorderData;
1000 if (!bd->bi)
1001 bd->bi = new QStyleSheetBorderImageData;
1002
1003 QStyleSheetBorderImageData *bi = bd->bi;
1004 bi->pixmap = QStyleSheetStyle::loadPixmap(uri, object);
1005 for (int i = 0; i < 4; i++)
1006 bi->cuts[i] = cuts[i];
1007 bi->horizStretch = horizStretch;
1008 bi->vertStretch = vertStretch;
1009 }
1010 } else if (decl.d->propertyId == QtBackgroundRole) {
1011 if (bg && bg->brush.style() != Qt::NoBrush)
1012 continue;
1013 int role = decl.d->values.at(0).variant.toInt();
1014 if (role >= Value_FirstColorRole && role <= Value_LastColorRole)
1015 defaultBackground = palette.color((QPalette::ColorRole)(role-Value_FirstColorRole));
1016 } else if (decl.d->property.startsWith(QLatin1String("qproperty-"), Qt::CaseInsensitive)) {
1017 // intentionally left blank...
1018 } else if (decl.d->propertyId == UnknownProperty) {
1019 bool knownStyleHint = false;
1020 for (int i = 0; i < numKnownStyleHints; i++) {
1021 QLatin1String styleHint(knownStyleHints[i]);
1022 if (decl.d->property.compare(styleHint) == 0) {
1023 QString hintName = QString(styleHint);
1024 QVariant hintValue;
1025 if (hintName.endsWith(QLatin1String("alignment"))) {
1026 hintValue = (int) decl.alignmentValue();
1027 } else if (hintName.endsWith(QLatin1String("color"))) {
1028 hintValue = (int) decl.colorValue().rgba();
1029 } else if (hintName.endsWith(QLatin1String("size"))) {
1030 hintValue = decl.sizeValue();
1031 } else if (hintName.endsWith(QLatin1String("icon"))) {
1032 hintValue = decl.iconValue();
1033 } else if (hintName == QLatin1String("button-layout")
1034 && decl.d->values.count() != 0 && decl.d->values.at(0).type == Value::String) {
1035 hintValue = subControlLayout(decl.d->values.at(0).variant.toString());
1036 } else {
1037 int integer;
1038 decl.intValue(&integer);
1039 hintValue = integer;
1040 }
1041 styleHints[decl.d->property] = hintValue;
1042 knownStyleHint = true;
1043 break;
1044 }
1045 }
1046 if (!knownStyleHint)
1047 qDebug("Unknown property %s", qPrintable(decl.d->property));
1048 }
1049 }
1050
1051 if (hasBorder()) {
1052 if (const QWidget *widget = qobject_cast<const QWidget *>(object)) {
1053 QStyleSheetStyle *style = const_cast<QStyleSheetStyle *>(globalStyleSheetStyle);
1054 if (!style)
1055 style = qt_styleSheet(widget->style());
1056 if (style)
1057 fixupBorder(style->nativeFrameWidth(widget));
1058 }
1059 if (border()->hasBorderImage())
1060 defaultBackground = QBrush();
1061 }
1062}
1063
1064QRect QRenderRule::borderRect(const QRect& r) const
1065{
1066 if (!hasBox())
1067 return r;
1068 const int* m = box()->margins;
1069 return r.adjusted(m[LeftEdge], m[TopEdge], -m[RightEdge], -m[BottomEdge]);
1070}
1071
1072QRect QRenderRule::outlineRect(const QRect& r) const
1073{
1074 QRect br = borderRect(r);
1075 if (!hasOutline())
1076 return br;
1077 const int *b = outline()->borders;
1078 return r.adjusted(b[LeftEdge], b[TopEdge], -b[RightEdge], -b[BottomEdge]);
1079}
1080
1081QRect QRenderRule::paddingRect(const QRect& r) const
1082{
1083 QRect br = borderRect(r);
1084 if (!hasBorder())
1085 return br;
1086 const int *b = border()->borders;
1087 return br.adjusted(b[LeftEdge], b[TopEdge], -b[RightEdge], -b[BottomEdge]);
1088}
1089
1090QRect QRenderRule::contentsRect(const QRect& r) const
1091{
1092 QRect pr = paddingRect(r);
1093 if (!hasBox())
1094 return pr;
1095 const int *p = box()->paddings;
1096 return pr.adjusted(p[LeftEdge], p[TopEdge], -p[RightEdge], -p[BottomEdge]);
1097}
1098
1099QRect QRenderRule::boxRect(const QRect& cr, int flags) const
1100{
1101 QRect r = cr;
1102 if (hasBox()) {
1103 if (flags & Margin) {
1104 const int *m = box()->margins;
1105 r.adjust(-m[LeftEdge], -m[TopEdge], m[RightEdge], m[BottomEdge]);
1106 }
1107 if (flags & Padding) {
1108 const int *p = box()->paddings;
1109 r.adjust(-p[LeftEdge], -p[TopEdge], p[RightEdge], p[BottomEdge]);
1110 }
1111 }
1112 if (hasBorder() && (flags & Border)) {
1113 const int *b = border()->borders;
1114 r.adjust(-b[LeftEdge], -b[TopEdge], b[RightEdge], b[BottomEdge]);
1115 }
1116 return r;
1117}
1118
1119QSize QRenderRule::boxSize(const QSize &cs, int flags) const
1120{
1121 QSize bs = boxRect(QRect(QPoint(0, 0), cs), flags).size();
1122 if (cs.width() < 0) bs.setWidth(-1);
1123 if (cs.height() < 0) bs.setHeight(-1);
1124 return bs;
1125}
1126
1127void QRenderRule::fixupBorder(int nativeWidth)
1128{
1129 if (bd == 0)
1130 return;
1131
1132 if (!bd->hasBorderImage() || bd->bi->pixmap.isNull()) {
1133 bd->bi = 0;
1134 // ignore the color, border of edges that have none border-style
1135 QBrush color = pal ? pal->foreground : QBrush();
1136 const bool hasRadius = bd->radii[0].isValid() || bd->radii[1].isValid()
1137 || bd->radii[2].isValid() || bd->radii[3].isValid();
1138 for (int i = 0; i < 4; i++) {
1139 if ((bd->styles[i] == BorderStyle_Native) && hasRadius)
1140 bd->styles[i] = BorderStyle_None;
1141
1142 switch (bd->styles[i]) {
1143 case BorderStyle_None:
1144 // border-style: none forces width to be 0
1145 bd->colors[i] = QBrush();
1146 bd->borders[i] = 0;
1147 break;
1148 case BorderStyle_Native:
1149 if (bd->borders[i] == 0)
1150 bd->borders[i] = nativeWidth;
1151 Q_FALLTHROUGH();
1152 default:
1153 if (bd->colors[i].style() == Qt::NoBrush) // auto-acquire 'color'
1154 bd->colors[i] = color;
1155 break;
1156 }
1157 }
1158
1159 return;
1160 }
1161
1162 // inspect the border image
1163 QStyleSheetBorderImageData *bi = bd->bi;
1164 if (bi->cuts[0] == -1) {
1165 for (int i = 0; i < 4; i++) // assume, cut = border
1166 bi->cuts[i] = int(border()->borders[i]);
1167 }
1168}
1169
1170void QRenderRule::drawBorderImage(QPainter *p, const QRect& rect)
1171{
1172 setClip(p, rect);
1173 static const Qt::TileRule tileMode2TileRule[] = {
1174 Qt::StretchTile, Qt::RoundTile, Qt::StretchTile, Qt::RepeatTile, Qt::StretchTile };
1175
1176 const QStyleSheetBorderImageData *borderImageData = border()->borderImage();
1177 const int *targetBorders = border()->borders;
1178 const int *sourceBorders = borderImageData->cuts;
1179 QMargins sourceMargins(sourceBorders[LeftEdge], sourceBorders[TopEdge],
1180 sourceBorders[RightEdge], sourceBorders[BottomEdge]);
1181 QMargins targetMargins(targetBorders[LeftEdge], targetBorders[TopEdge],
1182 targetBorders[RightEdge], targetBorders[BottomEdge]);
1183
1184 bool wasSmoothPixmapTransform = p->renderHints() & QPainter::SmoothPixmapTransform;
1185 p->setRenderHint(QPainter::SmoothPixmapTransform);
1186 qDrawBorderPixmap(p, rect, targetMargins, borderImageData->pixmap,
1187 QRect(QPoint(), borderImageData->pixmap.size()), sourceMargins,
1188 QTileRules(tileMode2TileRule[borderImageData->horizStretch], tileMode2TileRule[borderImageData->vertStretch]));
1189 p->setRenderHint(QPainter::SmoothPixmapTransform, wasSmoothPixmapTransform);
1190 unsetClip(p);
1191}
1192
1193QRect QRenderRule::originRect(const QRect &rect, Origin origin) const
1194{
1195 switch (origin) {
1196 case Origin_Padding:
1197 return paddingRect(rect);
1198 case Origin_Border:
1199 return borderRect(rect);
1200 case Origin_Content:
1201 return contentsRect(rect);
1202 case Origin_Margin:
1203 default:
1204 return rect;
1205 }
1206}
1207
1208void QRenderRule::drawBackgroundImage(QPainter *p, const QRect &rect, QPoint off)
1209{
1210 if (!hasBackground())
1211 return;
1212
1213 const QPixmap& bgp = background()->pixmap;
1214 if (bgp.isNull())
1215 return;
1216
1217 setClip(p, borderRect(rect));
1218
1219 if (background()->origin != background()->clip) {
1220 p->save();
1221 p->setClipRect(originRect(rect, background()->clip), Qt::IntersectClip);
1222 }
1223
1224 if (background()->attachment == Attachment_Fixed)
1225 off = QPoint(0, 0);
1226
1227 QSize bgpSize = bgp.size() / bgp.devicePixelRatio();
1228 int bgpHeight = bgpSize.height();
1229 int bgpWidth = bgpSize.width();
1230 QRect r = originRect(rect, background()->origin);
1231 QRect aligned = QStyle::alignedRect(Qt::LeftToRight, background()->position, bgpSize, r);
1232 QRect inter = aligned.translated(-off).intersected(r);
1233
1234 switch (background()->repeat) {
1235 case Repeat_Y:
1236 p->drawTiledPixmap(inter.x(), r.y(), inter.width(), r.height(), bgp,
1237 inter.x() - aligned.x() + off.x(),
1238 bgpHeight - int(aligned.y() - r.y()) % bgpHeight + off.y());
1239 break;
1240 case Repeat_X:
1241 p->drawTiledPixmap(r.x(), inter.y(), r.width(), inter.height(), bgp,
1242 bgpWidth - int(aligned.x() - r.x())%bgpWidth + off.x(),
1243 inter.y() - aligned.y() + off.y());
1244 break;
1245 case Repeat_XY:
1246 p->drawTiledPixmap(r, bgp,
1247 QPoint(bgpWidth - int(aligned.x() - r.x())% bgpWidth + off.x(),
1248 bgpHeight - int(aligned.y() - r.y())%bgpHeight + off.y()));
1249 break;
1250 case Repeat_None:
1251 default:
1252 p->drawPixmap(inter.x(), inter.y(), bgp, inter.x() - aligned.x() + off.x(),
1253 inter.y() - aligned.y() + off.y(), bgp.width() , bgp.height());
1254 break;
1255 }
1256
1257
1258 if (background()->origin != background()->clip)
1259 p->restore();
1260
1261 unsetClip(p);
1262}
1263
1264void QRenderRule::drawOutline(QPainter *p, const QRect &rect)
1265{
1266 if (!hasOutline())
1267 return;
1268
1269 bool wasAntialiased = p->renderHints() & QPainter::Antialiasing;
1270 p->setRenderHint(QPainter::Antialiasing);
1271 qDrawBorder(p, rect, ou->styles, ou->borders, ou->colors, ou->radii);
1272 p->setRenderHint(QPainter::Antialiasing, wasAntialiased);
1273}
1274
1275void QRenderRule::drawBorder(QPainter *p, const QRect& rect)
1276{
1277 if (!hasBorder())
1278 return;
1279
1280 if (border()->hasBorderImage()) {
1281 drawBorderImage(p, rect);
1282 return;
1283 }
1284
1285 bool wasAntialiased = p->renderHints() & QPainter::Antialiasing;
1286 p->setRenderHint(QPainter::Antialiasing);
1287 qDrawBorder(p, rect, bd->styles, bd->borders, bd->colors, bd->radii);
1288 p->setRenderHint(QPainter::Antialiasing, wasAntialiased);
1289}
1290
1291QPainterPath QRenderRule::borderClip(QRect r)
1292{
1293 if (!hasBorder())
1294 return QPainterPath();
1295
1296 QSize tlr, trr, blr, brr;
1297 qNormalizeRadii(r, bd->radii, &tlr, &trr, &blr, &brr);
1298 if (tlr.isNull() && trr.isNull() && blr.isNull() && brr.isNull())
1299 return QPainterPath();
1300
1301 const QRectF rect(r);
1302 const int *borders = border()->borders;
1303 QPainterPath path;
1304 qreal curY = rect.y() + borders[TopEdge]/2.0;
1305 path.moveTo(rect.x() + tlr.width(), curY);
1306 path.lineTo(rect.right() - trr.width(), curY);
1307 qreal curX = rect.right() - borders[RightEdge]/2.0;
1308 path.arcTo(curX - 2*trr.width() + borders[RightEdge], curY,
1309 trr.width()*2 - borders[RightEdge], trr.height()*2 - borders[TopEdge], 90, -90);
1310
1311 path.lineTo(curX, rect.bottom() - brr.height());
1312 curY = rect.bottom() - borders[BottomEdge]/2.0;
1313 path.arcTo(curX - 2*brr.width() + borders[RightEdge], curY - 2*brr.height() + borders[BottomEdge],
1314 brr.width()*2 - borders[RightEdge], brr.height()*2 - borders[BottomEdge], 0, -90);
1315
1316 path.lineTo(rect.x() + blr.width(), curY);
1317 curX = rect.left() + borders[LeftEdge]/2.0;
1318 path.arcTo(curX, rect.bottom() - 2*blr.height() + borders[BottomEdge]/2,
1319 blr.width()*2 - borders[LeftEdge], blr.height()*2 - borders[BottomEdge], 270, -90);
1320
1321 path.lineTo(curX, rect.top() + tlr.height());
1322 path.arcTo(curX, rect.top() + borders[TopEdge]/2,
1323 tlr.width()*2 - borders[LeftEdge], tlr.height()*2 - borders[TopEdge], 180, -90);
1324
1325 path.closeSubpath();
1326 return path;
1327}
1328
1329/*! \internal
1330 Clip the painter to the border (in case we are using radius border)
1331 */
1332void QRenderRule::setClip(QPainter *p, const QRect &rect)
1333{
1334 if (clipset++)
1335 return;
1336 clipPath = borderClip(rect);
1337 if (!clipPath.isEmpty()) {
1338 p->save();
1339 p->setClipPath(clipPath, Qt::IntersectClip);
1340 }
1341}
1342
1343void QRenderRule::unsetClip(QPainter *p)
1344{
1345 if (--clipset)
1346 return;
1347 if (!clipPath.isEmpty())
1348 p->restore();
1349}
1350
1351void QRenderRule::drawBackground(QPainter *p, const QRect& rect, const QPoint& off)
1352{
1353 QBrush brush = hasBackground() ? background()->brush : QBrush();
1354 if (brush.style() == Qt::NoBrush)
1355 brush = defaultBackground;
1356
1357 if (brush.style() != Qt::NoBrush) {
1358 Origin origin = hasBackground() ? background()->clip : Origin_Border;
1359 // ### fix for gradients
1360 const QPainterPath &borderPath = borderClip(originRect(rect, origin));
1361 if (!borderPath.isEmpty()) {
1362 // Drawn intead of being used as clipping path for better visual quality
1363 bool wasAntialiased = p->renderHints() & QPainter::Antialiasing;
1364 p->setRenderHint(QPainter::Antialiasing);
1365 p->fillPath(borderPath, brush);
1366 p->setRenderHint(QPainter::Antialiasing, wasAntialiased);
1367 } else {
1368 p->fillRect(originRect(rect, origin), brush);
1369 }
1370 }
1371
1372 drawBackgroundImage(p, rect, off);
1373}
1374
1375void QRenderRule::drawFrame(QPainter *p, const QRect& rect)
1376{
1377 drawBackground(p, rect);
1378 if (hasBorder())
1379 drawBorder(p, borderRect(rect));
1380}
1381
1382void QRenderRule::drawImage(QPainter *p, const QRect &rect)
1383{
1384 if (!hasImage())
1385 return;
1386 img->icon.paint(p, rect, img->alignment);
1387}
1388
1389void QRenderRule::drawRule(QPainter *p, const QRect& rect)
1390{
1391 drawFrame(p, rect);
1392 drawImage(p, contentsRect(rect));
1393}
1394
1395// *shudder* , *horror*, *whoa* <-- what you might feel when you see the functions below
1396void QRenderRule::configurePalette(QPalette *p, QPalette::ColorRole fr, QPalette::ColorRole br)
1397{
1398 if (bg && bg->brush.style() != Qt::NoBrush) {
1399 if (br != QPalette::NoRole)
1400 p->setBrush(br, bg->brush);
1401 p->setBrush(QPalette::Window, bg->brush);
1402 if (bg->brush.style() == Qt::SolidPattern) {
1403 p->setBrush(QPalette::Light, bg->brush.color().lighter(115));
1404 p->setBrush(QPalette::Midlight, bg->brush.color().lighter(107));
1405 p->setBrush(QPalette::Dark, bg->brush.color().darker(150));
1406 p->setBrush(QPalette::Shadow, bg->brush.color().darker(300));
1407 }
1408 }
1409
1410 if (!hasPalette())
1411 return;
1412
1413 if (pal->foreground.style() != Qt::NoBrush) {
1414 if (fr != QPalette::NoRole)
1415 p->setBrush(fr, pal->foreground);
1416 p->setBrush(QPalette::WindowText, pal->foreground);
1417 p->setBrush(QPalette::Text, pal->foreground);
1418 }
1419 if (pal->selectionBackground.style() != Qt::NoBrush)
1420 p->setBrush(QPalette::Highlight, pal->selectionBackground);
1421 if (pal->selectionForeground.style() != Qt::NoBrush)
1422 p->setBrush(QPalette::HighlightedText, pal->selectionForeground);
1423 if (pal->alternateBackground.style() != Qt::NoBrush)
1424 p->setBrush(QPalette::AlternateBase, pal->alternateBackground);
1425}
1426
1427void QRenderRule::configurePalette(QPalette *p, QPalette::ColorGroup cg, const QWidget *w, bool embedded)
1428{
1429 if (bg && bg->brush.style() != Qt::NoBrush) {
1430 p->setBrush(cg, QPalette::Base, bg->brush); // for windows, windowxp
1431 p->setBrush(cg, QPalette::Button, bg->brush); // for plastique
1432 p->setBrush(cg, w->backgroundRole(), bg->brush);
1433 p->setBrush(cg, QPalette::Window, bg->brush);
1434 }
1435
1436 if (embedded) {
1437 /* For embedded widgets (ComboBox, SpinBox and ScrollArea) we want the embedded widget
1438 * to be transparent when we have a transparent background or border image */
1439 if ((hasBackground() && background()->isTransparent())
1440 || (hasBorder() && border()->hasBorderImage() && !border()->borderImage()->pixmap.isNull()))
1441 p->setBrush(cg, w->backgroundRole(), Qt::NoBrush);
1442 }
1443
1444 if (!hasPalette())
1445 return;
1446
1447 if (pal->foreground.style() != Qt::NoBrush) {
1448 p->setBrush(cg, QPalette::ButtonText, pal->foreground);
1449 p->setBrush(cg, w->foregroundRole(), pal->foreground);
1450 p->setBrush(cg, QPalette::WindowText, pal->foreground);
1451 p->setBrush(cg, QPalette::Text, pal->foreground);
1452 }
1453 if (pal->selectionBackground.style() != Qt::NoBrush)
1454 p->setBrush(cg, QPalette::Highlight, pal->selectionBackground);
1455 if (pal->selectionForeground.style() != Qt::NoBrush)
1456 p->setBrush(cg, QPalette::HighlightedText, pal->selectionForeground);
1457 if (pal->alternateBackground.style() != Qt::NoBrush)
1458 p->setBrush(cg, QPalette::AlternateBase, pal->alternateBackground);
1459}
1460
1461bool QRenderRule::hasModification() const
1462{
1463 return hasPalette() ||
1464 hasBackground() ||
1465 hasGradientBackground() ||
1466 !hasNativeBorder() ||
1467 !hasNativeOutline() ||
1468 hasBox() ||
1469 hasPosition() ||
1470 hasGeometry() ||
1471 hasImage() ||
1472 hasFont ||
1473 !styleHints.isEmpty();
1474}
1475
1476///////////////////////////////////////////////////////////////////////////////
1477// Style rules
1478#define OBJECT_PTR(x) (static_cast<QObject *>(x.ptr))
1479
1480static inline QObject *parentObject(const QObject *obj)
1481{
1482#if QT_CONFIG(tooltip)
1483 if (qobject_cast<const QLabel *>(obj) && qstrcmp(obj->metaObject()->className(), "QTipLabel") == 0) {
1484 QObject *p = qvariant_cast<QObject *>(obj->property("_q_stylesheet_parent"));
1485 if (p)
1486 return p;
1487 }
1488#endif
1489 return obj->parent();
1490}
1491
1492class QStyleSheetStyleSelector : public StyleSelector
1493{
1494public:
1495 QStyleSheetStyleSelector() { }
1496
1497 QStringList nodeNames(NodePtr node) const override
1498 {
1499 if (isNullNode(node))
1500 return QStringList();
1501 const QMetaObject *metaObject = OBJECT_PTR(node)->metaObject();
1502#ifndef QT_NO_TOOLTIP
1503 if (qstrcmp(metaObject->className(), "QTipLabel") == 0)
1504 return QStringList(QLatin1String("QToolTip"));
1505#endif
1506 QStringList result;
1507 do {
1508 result += QString::fromLatin1(metaObject->className()).replace(QLatin1Char(':'), QLatin1Char('-'));
1509 metaObject = metaObject->superClass();
1510 } while (metaObject != 0);
1511 return result;
1512 }
1513 QString attribute(NodePtr node, const QString& name) const override
1514 {
1515 if (isNullNode(node))
1516 return QString();
1517
1518 QHash<QString, QString> &cache = m_attributeCache[OBJECT_PTR(node)];
1519 QHash<QString, QString>::const_iterator cacheIt = cache.constFind(name);
1520 if (cacheIt != cache.constEnd())
1521 return cacheIt.value();
1522
1523 QObject *obj = OBJECT_PTR(node);
1524 QVariant value = obj->property(name.toLatin1());
1525 if (!value.isValid()) {
1526 if (name == QLatin1String("class")) {
1527 QString className = QString::fromLatin1(obj->metaObject()->className());
1528 if (className.contains(QLatin1Char(':')))
1529 className.replace(QLatin1Char(':'), QLatin1Char('-'));
1530 cache[name] = className;
1531 return className;
1532 } else if (name == QLatin1String("style")) {
1533 QWidget *w = qobject_cast<QWidget *>(obj);
1534 QStyleSheetStyle *proxy = w ? qt_styleSheet(w->style()) : 0;
1535 if (proxy) {
1536 QString styleName = QString::fromLatin1(proxy->baseStyle()->metaObject()->className());
1537 cache[name] = styleName;
1538 return styleName;
1539 }
1540 }
1541 }
1542 QString valueStr;
1543 if(value.type() == QVariant::StringList || value.type() == QVariant::List)
1544 valueStr = value.toStringList().join(QLatin1Char(' '));
1545 else
1546 valueStr = value.toString();
1547 cache[name] = valueStr;
1548 return valueStr;
1549 }
1550 bool nodeNameEquals(NodePtr node, const QString& nodeName) const override
1551 {
1552 if (isNullNode(node))
1553 return false;
1554 const QMetaObject *metaObject = OBJECT_PTR(node)->metaObject();
1555#ifndef QT_NO_TOOLTIP
1556 if (qstrcmp(metaObject->className(), "QTipLabel") == 0)
1557 return nodeName == QLatin1String("QToolTip");
1558#endif
1559 do {
1560 const ushort *uc = (const ushort *)nodeName.constData();
1561 const ushort *e = uc + nodeName.length();
1562 const uchar *c = (const uchar *)metaObject->className();
1563 while (*c && uc != e && (*uc == *c || (*c == ':' && *uc == '-'))) {
1564 ++uc;
1565 ++c;
1566 }
1567 if (uc == e && !*c)
1568 return true;
1569 metaObject = metaObject->superClass();
1570 } while (metaObject != 0);
1571 return false;
1572 }
1573 bool hasAttributes(NodePtr) const override
1574 { return true; }
1575 QStringList nodeIds(NodePtr node) const override
1576 { return isNullNode(node) ? QStringList() : QStringList(OBJECT_PTR(node)->objectName()); }
1577 bool isNullNode(NodePtr node) const override
1578 { return node.ptr == 0; }
1579 NodePtr parentNode(NodePtr node) const override
1580 { NodePtr n; n.ptr = isNullNode(node) ? 0 : parentObject(OBJECT_PTR(node)); return n; }
1581 NodePtr previousSiblingNode(NodePtr) const override
1582 { NodePtr n; n.ptr = 0; return n; }
1583 NodePtr duplicateNode(NodePtr node) const override
1584 { return node; }
1585 void freeNode(NodePtr) const override
1586 { }
1587
1588private:
1589 mutable QHash<const QObject *, QHash<QString, QString> > m_attributeCache;
1590};
1591
1592QVector<QCss::StyleRule> QStyleSheetStyle::styleRules(const QObject *obj) const
1593{
1594 QHash<const QObject *, QVector<StyleRule> >::const_iterator cacheIt = styleSheetCaches->styleRulesCache.constFind(obj);
1595 if (cacheIt != styleSheetCaches->styleRulesCache.constEnd())
1596 return cacheIt.value();
1597
1598 if (!initObject(obj)) {
1599 return QVector<StyleRule>();
1600 }
1601
1602 QStyleSheetStyleSelector styleSelector;
1603
1604 StyleSheet defaultSs;
1605 QHash<const void *, StyleSheet>::const_iterator defaultCacheIt = styleSheetCaches->styleSheetCache.constFind(baseStyle());
1606 if (defaultCacheIt == styleSheetCaches->styleSheetCache.constEnd()) {
1607 defaultSs = getDefaultStyleSheet();
1608 QStyle *bs = baseStyle();
1609 styleSheetCaches->styleSheetCache.insert(bs, defaultSs);
1610 QObject::connect(bs, SIGNAL(destroyed(QObject*)), styleSheetCaches, SLOT(styleDestroyed(QObject*)), Qt::UniqueConnection);
1611 } else {
1612 defaultSs = defaultCacheIt.value();
1613 }
1614 styleSelector.styleSheets += defaultSs;
1615
1616 if (!qApp->styleSheet().isEmpty()) {
1617 StyleSheet appSs;
1618 QHash<const void *, StyleSheet>::const_iterator appCacheIt = styleSheetCaches->styleSheetCache.constFind(qApp);
1619 if (appCacheIt == styleSheetCaches->styleSheetCache.constEnd()) {
1620 QString ss = qApp->styleSheet();
1621 if (ss.startsWith(QLatin1String("file:///")))
1622 ss.remove(0, 8);
1623 parser.init(ss, qApp->styleSheet() != ss);
1624 if (Q_UNLIKELY(!parser.parse(&appSs)))
1625 qWarning("Could not parse application stylesheet");
1626 appSs.origin = StyleSheetOrigin_Inline;
1627 appSs.depth = 1;
1628 styleSheetCaches->styleSheetCache.insert(qApp, appSs);
1629 } else {
1630 appSs = appCacheIt.value();
1631 }
1632 styleSelector.styleSheets += appSs;
1633 }
1634
1635 QVector<QCss::StyleSheet> objectSs;
1636 for (const QObject *o = obj; o; o = parentObject(o)) {
1637 QString styleSheet = o->property("styleSheet").toString();
1638 if (styleSheet.isEmpty())
1639 continue;
1640 StyleSheet ss;
1641 QHash<const void *, StyleSheet>::const_iterator objCacheIt = styleSheetCaches->styleSheetCache.constFind(o);
1642 if (objCacheIt == styleSheetCaches->styleSheetCache.constEnd()) {
1643 parser.init(styleSheet);
1644 if (!parser.parse(&ss)) {
1645 parser.init(QLatin1String("* {") + styleSheet + QLatin1Char('}'));
1646 if (Q_UNLIKELY(!parser.parse(&ss)))
1647 qWarning() << "Could not parse stylesheet of object" << o;
1648 }
1649 ss.origin = StyleSheetOrigin_Inline;
1650 styleSheetCaches->styleSheetCache.insert(o, ss);
1651 } else {
1652 ss = objCacheIt.value();
1653 }
1654 objectSs.append(ss);
1655 }
1656
1657 for (int i = 0; i < objectSs.count(); i++)
1658 objectSs[i].depth = objectSs.count() - i + 2;
1659
1660 styleSelector.styleSheets += objectSs;
1661
1662 StyleSelector::NodePtr n;
1663 n.ptr = const_cast<QObject *>(obj);
1664 QVector<QCss::StyleRule> rules = styleSelector.styleRulesForNode(n);
1665 styleSheetCaches->styleRulesCache.insert(obj, rules);
1666 return rules;
1667}
1668
1669/////////////////////////////////////////////////////////////////////////////////////////
1670// Rendering rules
1671static QVector<Declaration> declarations(const QVector<StyleRule> &styleRules, const QString &part, quint64 pseudoClass = PseudoClass_Unspecified)
1672{
1673 QVector<Declaration> decls;
1674 for (int i = 0; i < styleRules.count(); i++) {
1675 const Selector& selector = styleRules.at(i).selectors.at(0);
1676 // Rules with pseudo elements don't cascade. This is an intentional
1677 // diversion for CSS
1678 if (part.compare(selector.pseudoElement(), Qt::CaseInsensitive) != 0)
1679 continue;
1680 quint64 negated = 0;
1681 quint64 cssClass = selector.pseudoClass(&negated);
1682 if ((pseudoClass == PseudoClass_Any) || (cssClass == PseudoClass_Unspecified)
1683 || ((((cssClass & pseudoClass) == cssClass)) && ((negated & pseudoClass) == 0)))
1684 decls += styleRules.at(i).declarations;
1685 }
1686 return decls;
1687}
1688
1689int QStyleSheetStyle::nativeFrameWidth(const QWidget *w)
1690{
1691 QStyle *base = baseStyle();
1692
1693#if QT_CONFIG(spinbox)
1694 if (qobject_cast<const QAbstractSpinBox *>(w))
1695 return base->pixelMetric(QStyle::PM_SpinBoxFrameWidth, 0, w);
1696#endif
1697
1698#if QT_CONFIG(combobox)
1699 if (qobject_cast<const QComboBox *>(w))
1700 return base->pixelMetric(QStyle::PM_ComboBoxFrameWidth, 0, w);
1701#endif
1702
1703#if QT_CONFIG(menu)
1704 if (qobject_cast<const QMenu *>(w))
1705 return base->pixelMetric(QStyle::PM_MenuPanelWidth, 0, w);
1706#endif
1707
1708#if QT_CONFIG(menubar)
1709 if (qobject_cast<const QMenuBar *>(w))
1710 return base->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, w);
1711#endif
1712#ifndef QT_NO_FRAME
1713 if (const QFrame *frame = qobject_cast<const QFrame *>(w)) {
1714 if (frame->frameShape() == QFrame::NoFrame)
1715 return 0;
1716 }
1717#endif
1718
1719 if (qstrcmp(w->metaObject()->className(), "QTipLabel") == 0)
1720 return base->pixelMetric(QStyle::PM_ToolTipLabelFrameWidth, 0, w);
1721
1722 return base->pixelMetric(QStyle::PM_DefaultFrameWidth, 0, w);
1723}
1724
1725static quint64 pseudoClass(QStyle::State state)
1726{
1727 quint64 pc = 0;
1728 if (state & QStyle::State_Enabled) {
1729 pc |= PseudoClass_Enabled;
1730 if (state & QStyle::State_MouseOver)
1731 pc |= PseudoClass_Hover;
1732 } else {
1733 pc |= PseudoClass_Disabled;
1734 }
1735 if (state & QStyle::State_Active)
1736 pc |= PseudoClass_Active;
1737 if (state & QStyle::State_Window)
1738 pc |= PseudoClass_Window;
1739 if (state & QStyle::State_Sunken)
1740 pc |= PseudoClass_Pressed;
1741 if (state & QStyle::State_HasFocus)
1742 pc |= PseudoClass_Focus;
1743 if (state & QStyle::State_On)
1744 pc |= (PseudoClass_On | PseudoClass_Checked);
1745 if (state & QStyle::State_Off)
1746 pc |= (PseudoClass_Off | PseudoClass_Unchecked);
1747 if (state & QStyle::State_NoChange)
1748 pc |= PseudoClass_Indeterminate;
1749 if (state & QStyle::State_Selected)
1750 pc |= PseudoClass_Selected;
1751 if (state & QStyle::State_Horizontal)
1752 pc |= PseudoClass_Horizontal;
1753 else
1754 pc |= PseudoClass_Vertical;
1755 if (state & (QStyle::State_Open | QStyle::State_On | QStyle::State_Sunken))
1756 pc |= PseudoClass_Open;
1757 else
1758 pc |= PseudoClass_Closed;
1759 if (state & QStyle::State_Children)
1760 pc |= PseudoClass_Children;
1761 if (state & QStyle::State_Sibling)
1762 pc |= PseudoClass_Sibling;
1763 if (state & QStyle::State_ReadOnly)
1764 pc |= PseudoClass_ReadOnly;
1765 if (state & QStyle::State_Item)
1766 pc |= PseudoClass_Item;
1767#ifdef QT_KEYPAD_NAVIGATION
1768 if (state & QStyle::State_HasEditFocus)
1769 pc |= PseudoClass_EditFocus;
1770#endif
1771 return pc;
1772}
1773
1774static void qt_check_if_internal_object(const QObject **obj, int *element)
1775{
1776#if !QT_CONFIG(dockwidget)
1777 Q_UNUSED(obj);
1778 Q_UNUSED(element);
1779#else
1780 if (*obj && qstrcmp((*obj)->metaObject()->className(), "QDockWidgetTitleButton") == 0) {
1781 if ((*obj)->objectName() == QLatin1String("qt_dockwidget_closebutton")) {
1782 *element = PseudoElement_DockWidgetCloseButton;
1783 } else if ((*obj)->objectName() == QLatin1String("qt_dockwidget_floatbutton")) {
1784 *element = PseudoElement_DockWidgetFloatButton;
1785 }
1786 *obj = (*obj)->parent();
1787 }
1788#endif
1789}
1790
1791QRenderRule QStyleSheetStyle::renderRule(const QObject *obj, int element, quint64 state) const
1792{
1793 qt_check_if_internal_object(&obj, &element);
1794 QHash<quint64, QRenderRule> &cache = styleSheetCaches->renderRulesCache[obj][element];
1795 QHash<quint64, QRenderRule>::const_iterator cacheIt = cache.constFind(state);
1796 if (cacheIt != cache.constEnd())
1797 return cacheIt.value();
1798
1799 if (!initObject(obj))
1800 return QRenderRule();
1801
1802 quint64 stateMask = 0;
1803 const QVector<StyleRule> rules = styleRules(obj);
1804 for (int i = 0; i < rules.count(); i++) {
1805 const Selector& selector = rules.at(i).selectors.at(0);
1806 quint64 negated = 0;
1807 stateMask |= selector.pseudoClass(&negated);
1808 stateMask |= negated;
1809 }
1810
1811 cacheIt = cache.constFind(state & stateMask);
1812 if (cacheIt != cache.constEnd()) {
1813 const QRenderRule &newRule = cacheIt.value();
1814 cache[state] = newRule;
1815 return newRule;
1816 }
1817
1818
1819 const QString part = QLatin1String(knownPseudoElements[element].name);
1820 QVector<Declaration> decls = declarations(rules, part, state);
1821 QRenderRule newRule(decls, obj);
1822 cache[state] = newRule;
1823 if ((state & stateMask) != state)
1824 cache[state&stateMask] = newRule;
1825 return newRule;
1826}
1827
1828QRenderRule QStyleSheetStyle::renderRule(const QObject *obj, const QStyleOption *opt, int pseudoElement) const
1829{
1830 quint64 extraClass = 0;
1831 QStyle::State state = opt ? opt->state : QStyle::State(QStyle::State_None);
1832
1833 if (const QStyleOptionComplex *complex = qstyleoption_cast<const QStyleOptionComplex *>(opt)) {
1834 if (pseudoElement != PseudoElement_None) {
1835 // if not an active subcontrol, just pass enabled/disabled
1836 QStyle::SubControl subControl = knownPseudoElements[pseudoElement].subControl;
1837
1838 if (!(complex->activeSubControls & subControl))
1839 state &= (QStyle::State_Enabled | QStyle::State_Horizontal | QStyle::State_HasFocus);
1840 }
1841
1842 switch (pseudoElement) {
1843 case PseudoElement_ComboBoxDropDown:
1844 case PseudoElement_ComboBoxArrow:
1845 state |= (complex->state & (QStyle::State_On|QStyle::State_ReadOnly));
1846 break;
1847 case PseudoElement_SpinBoxUpButton:
1848 case PseudoElement_SpinBoxDownButton:
1849 case PseudoElement_SpinBoxUpArrow:
1850 case PseudoElement_SpinBoxDownArrow:
1851#if QT_CONFIG(spinbox)
1852 if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
1853 bool on = false;
1854 bool up = pseudoElement == PseudoElement_SpinBoxUpButton
1855 || pseudoElement == PseudoElement_SpinBoxUpArrow;
1856 if ((sb->stepEnabled & QAbstractSpinBox::StepUpEnabled) && up)
1857 on = true;
1858 else if ((sb->stepEnabled & QAbstractSpinBox::StepDownEnabled) && !up)
1859 on = true;
1860 state |= (on ? QStyle::State_On : QStyle::State_Off);
1861 }
1862#endif // QT_CONFIG(spinbox)
1863 break;
1864 case PseudoElement_GroupBoxTitle:
1865 state |= (complex->state & (QStyle::State_MouseOver | QStyle::State_Sunken));
1866 break;
1867 case PseudoElement_ToolButtonMenu:
1868 case PseudoElement_ToolButtonMenuArrow:
1869 case PseudoElement_ToolButtonDownArrow:
1870 state |= complex->state & QStyle::State_MouseOver;
1871 if (complex->state & QStyle::State_Sunken ||
1872 complex->activeSubControls & QStyle::SC_ToolButtonMenu)
1873 state |= QStyle::State_Sunken;
1874 break;
1875 case PseudoElement_SliderGroove:
1876 state |= complex->state & QStyle::State_MouseOver;
1877 break;
1878 default:
1879 break;
1880 }
1881
1882 if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
1883 // QStyle::State_On is set when the popup is being shown
1884 // Propagate EditField Pressed state
1885 if (pseudoElement == PseudoElement_None
1886 && (complex->activeSubControls & QStyle::SC_ComboBoxEditField)
1887 && (!(state & QStyle::State_MouseOver))) {
1888 state |= QStyle::State_Sunken;
1889 }
1890
1891 if (!combo->frame)
1892 extraClass |= PseudoClass_Frameless;
1893 if (!combo->editable)
1894 extraClass |= PseudoClass_ReadOnly;
1895 else
1896 extraClass |= PseudoClass_Editable;
1897#if QT_CONFIG(spinbox)
1898 } else if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
1899 if (!spin->frame)
1900 extraClass |= PseudoClass_Frameless;
1901#endif // QT_CONFIG(spinbox)
1902 } else if (const QStyleOptionGroupBox *gb = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
1903 if (gb->features & QStyleOptionFrame::Flat)
1904 extraClass |= PseudoClass_Flat;
1905 if (gb->lineWidth == 0)
1906 extraClass |= PseudoClass_Frameless;
1907 } else if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
1908 if (tb->titleBarState & Qt::WindowMinimized) {
1909 extraClass |= PseudoClass_Minimized;
1910 }
1911 else if (tb->titleBarState & Qt::WindowMaximized)
1912 extraClass |= PseudoClass_Maximized;
1913 }
1914 } else {
1915 // handle simple style options
1916 if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
1917 if (mi->menuItemType == QStyleOptionMenuItem::DefaultItem)
1918 extraClass |= PseudoClass_Default;
1919 if (mi->checkType == QStyleOptionMenuItem::Exclusive)
1920 extraClass |= PseudoClass_Exclusive;
1921 else if (mi->checkType == QStyleOptionMenuItem::NonExclusive)
1922 extraClass |= PseudoClass_NonExclusive;
1923 if (mi->checkType != QStyleOptionMenuItem::NotCheckable)
1924 extraClass |= (mi->checked) ? (PseudoClass_On|PseudoClass_Checked)
1925 : (PseudoClass_Off|PseudoClass_Unchecked);
1926 } else if (const QStyleOptionHeader *hdr = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
1927 if (hdr->position == QStyleOptionHeader::OnlyOneSection)
1928 extraClass |= PseudoClass_OnlyOne;
1929 else if (hdr->position == QStyleOptionHeader::Beginning)
1930 extraClass |= PseudoClass_First;
1931 else if (hdr->position == QStyleOptionHeader::End)
1932 extraClass |= PseudoClass_Last;
1933 else if (hdr->position == QStyleOptionHeader::Middle)
1934 extraClass |= PseudoClass_Middle;
1935
1936 if (hdr->selectedPosition == QStyleOptionHeader::NextAndPreviousAreSelected)
1937 extraClass |= (PseudoClass_NextSelected | PseudoClass_PreviousSelected);
1938 else if (hdr->selectedPosition == QStyleOptionHeader::NextIsSelected)
1939 extraClass |= PseudoClass_NextSelected;
1940 else if (hdr->selectedPosition == QStyleOptionHeader::PreviousIsSelected)
1941 extraClass |= PseudoClass_PreviousSelected;
1942#if QT_CONFIG(tabwidget)
1943 } else if (const QStyleOptionTabWidgetFrame *tab = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
1944 switch (tab->shape) {
1945 case QTabBar::RoundedNorth:
1946 case QTabBar::TriangularNorth:
1947 extraClass |= PseudoClass_Top;
1948 break;
1949 case QTabBar::RoundedSouth:
1950 case QTabBar::TriangularSouth:
1951 extraClass |= PseudoClass_Bottom;
1952 break;
1953 case QTabBar::RoundedEast:
1954 case QTabBar::TriangularEast:
1955 extraClass |= PseudoClass_Right;
1956 break;
1957 case QTabBar::RoundedWest:
1958 case QTabBar::TriangularWest:
1959 extraClass |= PseudoClass_Left;
1960 break;
1961 default:
1962 break;
1963 }
1964#endif
1965#if QT_CONFIG(tabbar)
1966 } else if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
1967 if (tab->position == QStyleOptionTab::OnlyOneTab)
1968 extraClass |= PseudoClass_OnlyOne;
1969 else if (tab->position == QStyleOptionTab::Beginning)
1970 extraClass |= PseudoClass_First;
1971 else if (tab->position == QStyleOptionTab::End)
1972 extraClass |= PseudoClass_Last;
1973 else if (tab->position == QStyleOptionTab::Middle)
1974 extraClass |= PseudoClass_Middle;
1975
1976 if (tab->selectedPosition == QStyleOptionTab::NextIsSelected)
1977 extraClass |= PseudoClass_NextSelected;
1978 else if (tab->selectedPosition == QStyleOptionTab::PreviousIsSelected)
1979 extraClass |= PseudoClass_PreviousSelected;
1980
1981 switch (tab->shape) {
1982 case QTabBar::RoundedNorth:
1983 case QTabBar::TriangularNorth:
1984 extraClass |= PseudoClass_Top;
1985 break;
1986 case QTabBar::RoundedSouth:
1987 case QTabBar::TriangularSouth:
1988 extraClass |= PseudoClass_Bottom;
1989 break;
1990 case QTabBar::RoundedEast:
1991 case QTabBar::TriangularEast:
1992 extraClass |= PseudoClass_Right;
1993 break;
1994 case QTabBar::RoundedWest:
1995 case QTabBar::TriangularWest:
1996 extraClass |= PseudoClass_Left;
1997 break;
1998 default:
1999 break;
2000 }
2001#endif // QT_CONFIG(tabbar)
2002 } else if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
2003 if (btn->features & QStyleOptionButton::Flat)
2004 extraClass |= PseudoClass_Flat;
2005 if (btn->features & QStyleOptionButton::DefaultButton)
2006 extraClass |= PseudoClass_Default;
2007 } else if (const QStyleOptionFrame *frm = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
2008 if (frm->lineWidth == 0)
2009 extraClass |= PseudoClass_Frameless;
2010 if (frm->features & QStyleOptionFrame::Flat)
2011 extraClass |= PseudoClass_Flat;
2012 }
2013#if QT_CONFIG(toolbar)
2014 else if (const QStyleOptionToolBar *tb = qstyleoption_cast<const QStyleOptionToolBar *>(opt)) {
2015 if (tb->toolBarArea == Qt::LeftToolBarArea)
2016 extraClass |= PseudoClass_Left;
2017 else if (tb->toolBarArea == Qt::RightToolBarArea)
2018 extraClass |= PseudoClass_Right;
2019 else if (tb->toolBarArea == Qt::TopToolBarArea)
2020 extraClass |= PseudoClass_Top;
2021 else if (tb->toolBarArea == Qt::BottomToolBarArea)
2022 extraClass |= PseudoClass_Bottom;
2023
2024 if (tb->positionWithinLine == QStyleOptionToolBar::Beginning)
2025 extraClass |= PseudoClass_First;
2026 else if (tb->positionWithinLine == QStyleOptionToolBar::Middle)
2027 extraClass |= PseudoClass_Middle;
2028 else if (tb->positionWithinLine == QStyleOptionToolBar::End)
2029 extraClass |= PseudoClass_Last;
2030 else if (tb->positionWithinLine == QStyleOptionToolBar::OnlyOne)
2031 extraClass |= PseudoClass_OnlyOne;
2032 }
2033#endif // QT_CONFIG(toolbar)
2034#if QT_CONFIG(toolbox)
2035 else if (const QStyleOptionToolBox *tb = qstyleoption_cast<const QStyleOptionToolBox *>(opt)) {
2036 if (tb->position == QStyleOptionToolBox::OnlyOneTab)
2037 extraClass |= PseudoClass_OnlyOne;
2038 else if (tb->position == QStyleOptionToolBox::Beginning)
2039 extraClass |= PseudoClass_First;
2040 else if (tb->position == QStyleOptionToolBox::End)
2041 extraClass |= PseudoClass_Last;
2042 else if (tb->position == QStyleOptionToolBox::Middle)
2043 extraClass |= PseudoClass_Middle;
2044
2045 if (tb->selectedPosition == QStyleOptionToolBox::NextIsSelected)
2046 extraClass |= PseudoClass_NextSelected;
2047 else if (tb->selectedPosition == QStyleOptionToolBox::PreviousIsSelected)
2048 extraClass |= PseudoClass_PreviousSelected;
2049 }
2050#endif // QT_CONFIG(toolbox)
2051#if QT_CONFIG(dockwidget)
2052 else if (const QStyleOptionDockWidget *dw = qstyleoption_cast<const QStyleOptionDockWidget *>(opt)) {
2053 if (dw->verticalTitleBar)
2054 extraClass |= PseudoClass_Vertical;
2055 else
2056 extraClass |= PseudoClass_Horizontal;
2057 if (dw->closable)
2058 extraClass |= PseudoClass_Closable;
2059 if (dw->floatable)
2060 extraClass |= PseudoClass_Floatable;
2061 if (dw->movable)
2062 extraClass |= PseudoClass_Movable;
2063 }
2064#endif // QT_CONFIG(dockwidget)
2065#if QT_CONFIG(itemviews)
2066 else if (const QStyleOptionViewItem *vopt = qstyleoption_cast<const QStyleOptionViewItem *>(opt)) {
2067 if (vopt->features & QStyleOptionViewItem::Alternate)
2068 extraClass |= PseudoClass_Alternate;
2069 if (vopt->viewItemPosition == QStyleOptionViewItem::OnlyOne)
2070 extraClass |= PseudoClass_OnlyOne;
2071 else if (vopt->viewItemPosition == QStyleOptionViewItem::Beginning)
2072 extraClass |= PseudoClass_First;
2073 else if (vopt->viewItemPosition == QStyleOptionViewItem::End)
2074 extraClass |= PseudoClass_Last;
2075 else if (vopt->viewItemPosition == QStyleOptionViewItem::Middle)
2076 extraClass |= PseudoClass_Middle;
2077
2078 }
2079#endif
2080#if QT_CONFIG(lineedit)
2081 // LineEdit sets Sunken flag to indicate Sunken frame (argh)
2082 if (const QLineEdit *lineEdit = qobject_cast<const QLineEdit *>(obj)) {
2083 state &= ~QStyle::State_Sunken;
2084 if (lineEdit->hasFrame()) {
2085 extraClass &= ~PseudoClass_Frameless;
2086 } else {
2087 extraClass |= PseudoClass_Frameless;
2088 }
2089 } else
2090#endif
2091 if (const QFrame *frm = qobject_cast<const QFrame *>(obj)) {
2092 if (frm->lineWidth() == 0)
2093 extraClass |= PseudoClass_Frameless;
2094 }
2095 }
2096
2097 return renderRule(obj, pseudoElement, pseudoClass(state) | extraClass);
2098}
2099
2100bool QStyleSheetStyle::hasStyleRule(const QObject *obj, int part) const
2101{
2102 QHash<int, bool> &cache = styleSheetCaches->hasStyleRuleCache[obj];
2103 QHash<int, bool>::const_iterator cacheIt = cache.constFind(part);
2104 if (cacheIt != cache.constEnd())
2105 return cacheIt.value();
2106
2107 if (!initObject(obj))
2108 return false;
2109
2110
2111 const QVector<StyleRule> &rules = styleRules(obj);
2112 if (part == PseudoElement_None) {
2113 bool result = obj && !rules.isEmpty();
2114 cache[part] = result;
2115 return result;
2116 }
2117
2118 QString pseudoElement = QLatin1String(knownPseudoElements[part].name);
2119 for (int i = 0; i < rules.count(); i++) {
2120 const Selector& selector = rules.at(i).selectors.at(0);
2121 if (pseudoElement.compare(selector.pseudoElement(), Qt::CaseInsensitive) == 0) {
2122 cache[part] = true;
2123 return true;
2124 }
2125 }
2126
2127 cache[part] = false;
2128 return false;
2129}
2130
2131static Origin defaultOrigin(int pe)
2132{
2133 switch (pe) {
2134 case PseudoElement_ScrollBarAddPage:
2135 case PseudoElement_ScrollBarSubPage:
2136 case PseudoElement_ScrollBarAddLine:
2137 case PseudoElement_ScrollBarSubLine:
2138 case PseudoElement_ScrollBarFirst:
2139 case PseudoElement_ScrollBarLast:
2140 case PseudoElement_GroupBoxTitle:
2141 case PseudoElement_GroupBoxIndicator: // never used
2142 case PseudoElement_ToolButtonMenu:
2143 case PseudoElement_SliderAddPage:
2144 case PseudoElement_SliderSubPage:
2145 return Origin_Border;
2146
2147 case PseudoElement_SpinBoxUpButton:
2148 case PseudoElement_SpinBoxDownButton:
2149 case PseudoElement_PushButtonMenuIndicator:
2150 case PseudoElement_ComboBoxDropDown:
2151 case PseudoElement_ToolButtonDownArrow:
2152 case PseudoElement_MenuCheckMark:
2153 case PseudoElement_MenuIcon:
2154 case PseudoElement_MenuRightArrow:
2155 return Origin_Padding;
2156
2157 case PseudoElement_Indicator:
2158 case PseudoElement_ExclusiveIndicator:
2159 case PseudoElement_ComboBoxArrow:
2160 case PseudoElement_ScrollBarSlider:
2161 case PseudoElement_ScrollBarUpArrow:
2162 case PseudoElement_ScrollBarDownArrow:
2163 case PseudoElement_ScrollBarLeftArrow:
2164 case PseudoElement_ScrollBarRightArrow:
2165 case PseudoElement_SpinBoxUpArrow:
2166 case PseudoElement_SpinBoxDownArrow:
2167 case PseudoElement_ToolButtonMenuArrow:
2168 case PseudoElement_HeaderViewUpArrow:
2169 case PseudoElement_HeaderViewDownArrow:
2170 case PseudoElement_SliderGroove:
2171 case PseudoElement_SliderHandle:
2172 return Origin_Content;
2173
2174 default:
2175 return Origin_Margin;
2176 }
2177}
2178
2179static Qt::Alignment defaultPosition(int pe)
2180{
2181 switch (pe) {
2182 case PseudoElement_Indicator:
2183 case PseudoElement_ExclusiveIndicator:
2184 case PseudoElement_MenuCheckMark:
2185 case PseudoElement_MenuIcon:
2186 return Qt::AlignLeft | Qt::AlignVCenter;
2187
2188 case PseudoElement_ScrollBarAddLine:
2189 case PseudoElement_ScrollBarLast:
2190 case PseudoElement_SpinBoxDownButton:
2191 case PseudoElement_PushButtonMenuIndicator:
2192 case PseudoElement_ToolButtonDownArrow:
2193 return Qt::AlignRight | Qt::AlignBottom;
2194
2195 case PseudoElement_ScrollBarSubLine:
2196 case PseudoElement_ScrollBarFirst:
2197 case PseudoElement_SpinBoxUpButton:
2198 case PseudoElement_ComboBoxDropDown:
2199 case PseudoElement_ToolBut