source: trunk/src/gui/styles/qstylesheetstyle.cpp@ 427

Last change on this file since 427 was 2, checked in by Dmitry A. Kuminov, 17 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

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