source: trunk/src/gui/styles/qmacstyle_mac.mm@ 776

Last change on this file since 776 was 769, checked in by Dmitry A. Kuminov, 15 years ago

trunk: Merged in qt 4.6.3 sources from branches/vendor/nokia/qt.

File size: 249.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this 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 have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*
43 Note: The qdoc comments for QMacStyle are contained in
44 .../doc/src/qstyles.qdoc.
45*/
46
47#include "qmacstyle_mac.h"
48
49#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
50#define QMAC_QAQUASTYLE_SIZE_CONSTRAIN
51//#define DEBUG_SIZE_CONSTRAINT
52
53#include <private/qapplication_p.h>
54#include <private/qcombobox_p.h>
55#include <private/qmacstylepixmaps_mac_p.h>
56#include <private/qpaintengine_mac_p.h>
57#include <private/qpainter_p.h>
58#include <private/qprintengine_mac_p.h>
59#include <private/qstylehelper_p.h>
60#include <qapplication.h>
61#include <qbitmap.h>
62#include <qcheckbox.h>
63#include <qcombobox.h>
64#include <qdialogbuttonbox.h>
65#include <qdockwidget.h>
66#include <qevent.h>
67#include <qfocusframe.h>
68#include <qformlayout.h>
69#include <qgroupbox.h>
70#include <qhash.h>
71#include <qheaderview.h>
72#include <qlayout.h>
73#include <qlineedit.h>
74#include <qlistview.h>
75#include <qmainwindow.h>
76#include <qmap.h>
77#include <qmenubar.h>
78#include <qpaintdevice.h>
79#include <qpainter.h>
80#include <qpixmapcache.h>
81#include <qpointer.h>
82#include <qprogressbar.h>
83#include <qpushbutton.h>
84#include <qradiobutton.h>
85#include <qrubberband.h>
86#include <qsizegrip.h>
87#include <qspinbox.h>
88#include <qsplitter.h>
89#include <qstyleoption.h>
90#include <qtextedit.h>
91#include <qtextstream.h>
92#include <qtoolbar.h>
93#include <qtoolbutton.h>
94#include <qtreeview.h>
95#include <qtableview.h>
96#include <qwizard.h>
97#include <qdebug.h>
98#include <qlibrary.h>
99#include <qdatetimeedit.h>
100#include <QtGui/qgraphicsproxywidget.h>
101#include <QtGui/qgraphicsview.h>
102#include <private/qt_cocoa_helpers_mac_p.h>
103
104QT_BEGIN_NAMESPACE
105
106extern QRegion qt_mac_convert_mac_region(RgnHandle); //qregion_mac.cpp
107
108// The following constants are used for adjusting the size
109// of push buttons so that they are drawn inside their bounds.
110static const int PushButtonLeftOffset = 6;
111static const int PushButtonTopOffset = 4;
112static const int PushButtonRightOffset = 12;
113static const int PushButtonBottomOffset = 12;
114static const int MiniButtonH = 26;
115static const int SmallButtonH = 30;
116static const int BevelButtonW = 50;
117static const int BevelButtonH = 22;
118static const int PushButtonContentPadding = 6;
119
120// These colors specify the titlebar gradient colors on
121// Leopard. Ideally we should get them from the system.
122static const QColor titlebarGradientActiveBegin(220, 220, 220);
123static const QColor titlebarGradientActiveEnd(151, 151, 151);
124static const QColor titlebarSeparatorLineActive(111, 111, 111);
125static const QColor titlebarGradientInactiveBegin(241, 241, 241);
126static const QColor titlebarGradientInactiveEnd(207, 207, 207);
127static const QColor titlebarSeparatorLineInactive(131, 131, 131);
128
129// Gradient colors used for the dock widget title bar and
130// non-unifed tool bar bacground.
131static const QColor mainWindowGradientBegin(240, 240, 240);
132static const QColor mainWindowGradientEnd(200, 200, 200);
133
134#if (MAC_OS_X_VERSION_MAX_ALLOWED <= MAC_OS_X_VERSION_10_5)
135enum {
136 kThemePushButtonTextured = 31,
137 kThemePushButtonTexturedSmall = 32,
138 kThemePushButtonTexturedMini = 33
139};
140
141/* Search fields */
142enum {
143 kHIThemeFrameTextFieldRound = 1000,
144 kHIThemeFrameTextFieldRoundSmall = 1001,
145 kHIThemeFrameTextFieldRoundMini = 1002
146};
147#endif
148
149// Resolve these at run-time, since the functions was moved in Leopard.
150typedef HIRect * (*PtrHIShapeGetBounds)(HIShapeRef, HIRect *);
151static PtrHIShapeGetBounds ptrHIShapeGetBounds = 0;
152
153static bool isVerticalTabs(const QTabBar::Shape shape) {
154 return (shape == QTabBar::RoundedEast
155 || shape == QTabBar::TriangularEast
156 || shape == QTabBar::RoundedWest
157 || shape == QTabBar::TriangularWest);
158}
159
160static int closeButtonSize = 12;
161
162void drawTabCloseButton(QPainter *p, bool hover, bool active, bool selected)
163{
164 // draw background circle
165 p->setRenderHints(QPainter::Antialiasing);
166 QRect rect(0, 0, closeButtonSize, closeButtonSize);
167 QColor background;
168 if (hover) {
169 background = QColor(124, 124, 124);
170 } else {
171 if (active) {
172 if (selected)
173 background = QColor(104, 104, 104);
174 else
175 background = QColor(83, 83, 83);
176 } else {
177 if (selected)
178 background = QColor(144, 144, 144);
179 else
180 background = QColor(114, 114, 114);
181 }
182 }
183 p->setPen(Qt::transparent);
184 p->setBrush(background);
185 p->drawEllipse(rect);
186
187 // draw cross
188 int min = 3;
189 int max = 9;
190 QPen crossPen;
191 crossPen.setColor(QColor(194, 194, 194));
192 crossPen.setWidthF(1.3);
193 crossPen.setCapStyle(Qt::FlatCap);
194 p->setPen(crossPen);
195 p->drawLine(min, min, max, max);
196 p->drawLine(min, max, max, min);
197}
198
199QRect rotateTabPainter(QPainter *p, QTabBar::Shape shape, QRect tabRect)
200{
201 if (isVerticalTabs(shape)) {
202 int newX, newY, newRot;
203 if (shape == QTabBar::RoundedEast
204 || shape == QTabBar::TriangularEast) {
205 newX = tabRect.width();
206 newY = tabRect.y();
207 newRot = 90;
208 } else {
209 newX = 0;
210 newY = tabRect.y() + tabRect.height();
211 newRot = -90;
212 }
213 tabRect.setRect(0, 0, tabRect.height(), tabRect.width());
214 QMatrix m;
215 m.translate(newX, newY);
216 m.rotate(newRot);
217 p->setMatrix(m, true);
218 }
219 return tabRect;
220}
221
222void drawTabShape(QPainter *p, const QStyleOptionTabV3 *tabOpt)
223{
224 QRect r = tabOpt->rect;
225 p->translate(tabOpt->rect.x(), tabOpt->rect.y());
226 r.moveLeft(0);
227 r.moveTop(0);
228 QRect tabRect = rotateTabPainter(p, tabOpt->shape, r);
229
230 int width = tabRect.width();
231 int height = 20;
232 bool active = (tabOpt->state & QStyle::State_Active);
233 bool selected = (tabOpt->state & QStyle::State_Selected);
234
235 if (selected) {
236 QRect rect(1, 0, width - 2, height);
237
238 // fill body
239 if (active) {
240 p->fillRect(rect, QColor(151, 151, 151));
241 } else {
242 QLinearGradient gradient(rect.topLeft(), rect.bottomLeft());
243 gradient.setColorAt(0, QColor(207, 207, 207));
244 gradient.setColorAt(0.5, QColor(206, 206, 206));
245 gradient.setColorAt(1, QColor(201, 201, 201));
246 p->fillRect(rect, gradient);
247 }
248
249 // draw border
250 QColor borderSides;
251 QColor borderBottom;
252 if (active) {
253 borderSides = QColor(88, 88, 88);
254 borderBottom = QColor(88, 88, 88);
255 } else {
256 borderSides = QColor(121, 121, 121);
257 borderBottom = QColor(116, 116, 116);
258 }
259
260 p->setPen(borderSides);
261
262 int bottom = height;
263 // left line
264 p->drawLine(0, 1, 0, bottom-2);
265 // right line
266 p->drawLine(width-1, 1, width-1, bottom-2);
267
268 // bottom line
269 if (active) {
270 p->setPen(QColor(168, 168, 168));
271 p->drawLine(3, bottom-1, width-3, bottom-1);
272 }
273 p->setPen(borderBottom);
274 p->drawLine(2, bottom, width-2, bottom);
275
276 int w = 3;
277 QRectF rectangleLeft(1, height - w, w, w);
278 QRectF rectangleRight(width - 2, height - 1, w, w);
279 int startAngle = 180 * 16;
280 int spanAngle = 90 * 16;
281 p->setRenderHint(QPainter::Antialiasing);
282 p->drawArc(rectangleLeft, startAngle, spanAngle);
283 p->drawArc(rectangleRight, startAngle, -spanAngle);
284 } else {
285 // when the mouse is over non selected tabs they get a new color
286 bool hover = (tabOpt->state & QStyle::State_MouseOver);
287 if (hover) {
288 QRect rect(1, 2, width - 1, height - 1);
289 p->fillRect(rect, QColor(110, 110, 110));
290 }
291
292 // seperator lines between tabs
293 bool west = (tabOpt->shape == QTabBar::RoundedWest || tabOpt->shape == QTabBar::TriangularWest);
294 bool drawOnRight = !west;
295 if ((!drawOnRight && tabOpt->selectedPosition != QStyleOptionTab::NextIsSelected)
296 || (drawOnRight && tabOpt->selectedPosition != QStyleOptionTab::NextIsSelected)) {
297 QColor borderColor;
298 QColor borderHighlightColor;
299 if (active) {
300 borderColor = QColor(64, 64, 64);
301 borderHighlightColor = QColor(140, 140, 140);
302 } else {
303 borderColor = QColor(135, 135, 135);
304 borderHighlightColor = QColor(178, 178, 178);
305 }
306
307 int x = drawOnRight ? width : 0;
308
309 // tab seperator line
310 p->setPen(borderColor);
311 p->drawLine(x, 2, x, height + 1);
312
313 // tab seperator highlight
314 p->setPen(borderHighlightColor);
315 p->drawLine(x-1, 2, x-1, height + 1);
316 p->drawLine(x+1, 2, x+1, height + 1);
317 }
318 }
319}
320
321void drawTabBase(QPainter *p, const QStyleOptionTabBarBaseV2 *tbb, const QWidget *w)
322{
323 QRect r = tbb->rect;
324 if (isVerticalTabs(tbb->shape)) {
325 r.setWidth(w->width());
326 } else {
327 r.setHeight(w->height());
328 }
329 QRect tabRect = rotateTabPainter(p, tbb->shape, r);
330 int width = tabRect.width();
331 int height = tabRect.height();
332 bool active = (tbb->state & QStyle::State_Active);
333
334 // top border lines
335 QColor borderHighlightTop;
336 QColor borderTop;
337 if (active) {
338 borderTop = QColor(64, 64, 64);
339 borderHighlightTop = QColor(174, 174, 174);
340 } else {
341 borderTop = QColor(135, 135, 135);
342 borderHighlightTop = QColor(207, 207, 207);
343 }
344 p->setPen(borderHighlightTop);
345 p->drawLine(tabRect.x(), 0, width, 0);
346 p->setPen(borderTop);
347 p->drawLine(tabRect.x(), 1, width, 1);
348
349 // center block
350 QRect centralRect(tabRect.x(), 2, width, height - 2);
351 if (active) {
352 QColor mainColor = QColor(120, 120, 120);
353 p->fillRect(centralRect, mainColor);
354 } else {
355 QLinearGradient gradient(centralRect.topLeft(), centralRect.bottomLeft());
356 gradient.setColorAt(0, QColor(165, 165, 165));
357 gradient.setColorAt(0.5, QColor(164, 164, 164));
358 gradient.setColorAt(1, QColor(158, 158, 158));
359 p->fillRect(centralRect, gradient);
360 }
361
362 // bottom border lines
363 QColor borderHighlightBottom;
364 QColor borderBottom;
365 if (active) {
366 borderHighlightBottom = QColor(153, 153, 153);
367 borderBottom = QColor(64, 64, 64);
368 } else {
369 borderHighlightBottom = QColor(177, 177, 177);
370 borderBottom = QColor(127, 127, 127);
371 }
372 p->setPen(borderHighlightBottom);
373 p->drawLine(tabRect.x(), height - 2, width, height - 2);
374 p->setPen(borderBottom);
375 p->drawLine(tabRect.x(), height - 1, width, height - 1);
376}
377
378/*
379 AHIG:
380 Apple Human Interface Guidelines
381 http://developer.apple.com/documentation/UserExperience/Conceptual/OSXHIGuidelines/
382
383 Builder:
384 Apple Interface Builder v. 3.1.1
385*/
386
387// this works as long as we have at most 16 different control types
388#define CT1(c) CT2(c, c)
389#define CT2(c1, c2) ((uint(c1) << 16) | uint(c2))
390
391enum QAquaWidgetSize { QAquaSizeLarge = 0, QAquaSizeSmall = 1, QAquaSizeMini = 2,
392 QAquaSizeUnknown = -1 };
393
394#define SIZE(large, small, mini) \
395 (controlSize == QAquaSizeLarge ? (large) : controlSize == QAquaSizeSmall ? (small) : (mini))
396
397// same as return SIZE(...) but optimized
398#define return_SIZE(large, small, mini) \
399 do { \
400 static const int sizes[] = { (large), (small), (mini) }; \
401 return sizes[controlSize]; \
402 } while (0)
403
404static int getControlSize(const QStyleOption *option, const QWidget *widget)
405{
406 if (option) {
407 if (option->state & (QStyle::State_Small | QStyle::State_Mini))
408 return (option->state & QStyle::State_Mini) ? QAquaSizeMini : QAquaSizeSmall;
409 } else if (widget) {
410 switch (QMacStyle::widgetSizePolicy(widget)) {
411 case QMacStyle::SizeSmall:
412 return QAquaSizeSmall;
413 case QMacStyle::SizeMini:
414 return QAquaSizeMini;
415 default:
416 break;
417 }
418 }
419 return QAquaSizeLarge;
420}
421
422
423static inline bool isTreeView(const QWidget *widget)
424{
425 return (widget && widget->parentWidget() &&
426 (qobject_cast<const QTreeView *>(widget->parentWidget())
427#ifdef QT3_SUPPORT
428 || widget->parentWidget()->inherits("Q3ListView")
429#endif
430 ));
431}
432
433QString qt_mac_removeMnemonics(const QString &original)
434{
435 // copied from qt_format_text (to be bug-for-bug compatible).
436 QString returnText(original.size(), 0);
437 int finalDest = 0;
438 int currPos = 0;
439 int l = original.length();
440 while (l) {
441 if (original.at(currPos) == QLatin1Char('&')) {
442 ++currPos;
443 --l;
444 if (l == 0)
445 break;
446 }
447 returnText[finalDest] = original.at(currPos);
448 ++currPos;
449 ++finalDest;
450 --l;
451 }
452 returnText.truncate(finalDest);
453 return returnText;
454}
455
456static inline ThemeTabDirection getTabDirection(QTabBar::Shape shape)
457{
458 ThemeTabDirection ttd;
459 switch (shape) {
460 case QTabBar::RoundedSouth:
461 case QTabBar::TriangularSouth:
462 ttd = kThemeTabSouth;
463 break;
464 default: // Added to remove the warning, since all values are taken care of, really!
465 case QTabBar::RoundedNorth:
466 case QTabBar::TriangularNorth:
467 ttd = kThemeTabNorth;
468 break;
469 case QTabBar::RoundedWest:
470 case QTabBar::TriangularWest:
471 ttd = kThemeTabWest;
472 break;
473 case QTabBar::RoundedEast:
474 case QTabBar::TriangularEast:
475 ttd = kThemeTabEast;
476 break;
477 }
478 return ttd;
479}
480
481class QMacStylePrivate : public QObject
482{
483 Q_OBJECT
484
485public:
486 QMacStylePrivate(QMacStyle *style);
487
488 // Stuff from QAquaAnimate:
489 bool addWidget(QWidget *);
490 void removeWidget(QWidget *);
491
492 enum Animates { AquaPushButton, AquaProgressBar, AquaListViewItemOpen };
493 bool animatable(Animates, const QWidget *) const;
494 void stopAnimate(Animates, QWidget *);
495 void startAnimate(Animates, QWidget *);
496 static ThemeDrawState getDrawState(QStyle::State flags);
497 QAquaWidgetSize aquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
498 QStyle::ContentsType ct = QStyle::CT_CustomBase,
499 QSize szHint=QSize(-1, -1), QSize *insz = 0) const;
500 void getSliderInfo(QStyle::ComplexControl cc, const QStyleOptionSlider *slider,
501 HIThemeTrackDrawInfo *tdi, const QWidget *needToRemoveMe);
502 bool doAnimate(Animates);
503 inline int animateSpeed(Animates) const { return 33; }
504
505 // Utility functions
506 void drawColorlessButton(const HIRect &macRect, HIThemeButtonDrawInfo *bdi,
507 QPainter *p, const QStyleOption *opt) const;
508
509 QSize pushButtonSizeFromContents(const QStyleOptionButton *btn) const;
510
511 HIRect pushButtonContentBounds(const QStyleOptionButton *btn,
512 const HIThemeButtonDrawInfo *bdi) const;
513
514 void initComboboxBdi(const QStyleOptionComboBox *combo, HIThemeButtonDrawInfo *bdi,
515 const QWidget *widget, const ThemeDrawState &tds);
516
517 static HIRect comboboxInnerBounds(const HIRect &outerBounds, int buttonKind);
518
519 static QRect comboboxEditBounds(const QRect &outerBounds, const HIThemeButtonDrawInfo &bdi);
520
521 static void drawCombobox(const HIRect &outerBounds, const HIThemeButtonDrawInfo &bdi, QPainter *p);
522 static void drawTableHeader(const HIRect &outerBounds, bool drawTopBorder, bool drawLeftBorder,
523 const HIThemeButtonDrawInfo &bdi, QPainter *p);
524 bool contentFitsInPushButton(const QStyleOptionButton *btn, HIThemeButtonDrawInfo *bdi,
525 ThemeButtonKind buttonKindToCheck) const;
526 void initHIThemePushButton(const QStyleOptionButton *btn, const QWidget *widget,
527 const ThemeDrawState tds,
528 HIThemeButtonDrawInfo *bdi) const;
529 QPixmap generateBackgroundPattern() const;
530protected:
531 bool eventFilter(QObject *, QEvent *);
532 void timerEvent(QTimerEvent *);
533
534private slots:
535 void startAnimationTimer();
536
537public:
538 QPointer<QPushButton> defaultButton; //default push buttons
539 int timerID;
540 QList<QPointer<QWidget> > progressBars; //existing progress bars that need animation
541
542 struct ButtonState {
543 int frame;
544 enum { ButtonDark, ButtonLight } dir;
545 } buttonState;
546 UInt8 progressFrame;
547 QPointer<QFocusFrame> focusWidget;
548 CFAbsoluteTime defaultButtonStart;
549 QMacStyle *q;
550 bool mouseDown;
551};
552
553QT_BEGIN_INCLUDE_NAMESPACE
554#include "qmacstyle_mac.moc"
555QT_END_INCLUDE_NAMESPACE
556
557/*****************************************************************************
558 External functions
559 *****************************************************************************/
560extern CGContextRef qt_mac_cg_context(const QPaintDevice *); //qpaintdevice_mac.cpp
561extern QRegion qt_mac_convert_mac_region(HIShapeRef); //qregion_mac.cpp
562void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
563extern QPaintDevice *qt_mac_safe_pdev; //qapplication_mac.cpp
564
565/*****************************************************************************
566 QMacCGStyle globals
567 *****************************************************************************/
568const int qt_mac_hitheme_version = 0; //the HITheme version we speak
569const int macItemFrame = 2; // menu item frame width
570const int macItemHMargin = 3; // menu item hor text margin
571const int macItemVMargin = 2; // menu item ver text margin
572const int macRightBorder = 12; // right border on mac
573const ThemeWindowType QtWinType = kThemeDocumentWindow; // Window type we use for QTitleBar.
574QPixmap *qt_mac_backgroundPattern = 0; // stores the standard widget background.
575
576/*****************************************************************************
577 QMacCGStyle utility functions
578 *****************************************************************************/
579static inline int qt_mac_hitheme_tab_version()
580{
581 return 1;
582}
583
584static inline HIRect qt_hirectForQRect(const QRect &convertRect, const QRect &rect = QRect())
585{
586 return CGRectMake(convertRect.x() + rect.x(), convertRect.y() + rect.y(),
587 convertRect.width() - rect.width(), convertRect.height() - rect.height());
588}
589
590static inline const QRect qt_qrectForHIRect(const HIRect &hirect)
591{
592 return QRect(QPoint(int(hirect.origin.x), int(hirect.origin.y)),
593 QSize(int(hirect.size.width), int(hirect.size.height)));
594}
595
596inline bool qt_mac_is_metal(const QWidget *w)
597{
598 for (; w; w = w->parentWidget()) {
599 if (w->testAttribute(Qt::WA_MacBrushedMetal))
600 return true;
601 if (w->isWindow() && w->testAttribute(Qt::WA_WState_Created)) { // If not created will fall through to the opaque check and be fine anyway.
602 return macWindowIsTextured(qt_mac_window_for(w));
603 }
604 if (w->d_func()->isOpaque)
605 break;
606 }
607 return false;
608}
609
610static int qt_mac_aqua_get_metric(ThemeMetric met)
611{
612 SInt32 ret;
613 GetThemeMetric(met, &ret);
614 return ret;
615}
616
617static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QWidget *widg, QSize szHint,
618 QAquaWidgetSize sz)
619{
620 QSize ret(-1, -1);
621 if (sz != QAquaSizeSmall && sz != QAquaSizeLarge && sz != QAquaSizeMini) {
622 qDebug("Not sure how to return this...");
623 return ret;
624 }
625 if ((widg && widg->testAttribute(Qt::WA_SetFont)) || !QApplication::desktopSettingsAware()) {
626 // If you're using a custom font and it's bigger than the default font,
627 // then no constraints for you. If you are smaller, we can try to help you out
628 QFont font = qt_app_fonts_hash()->value(widg->metaObject()->className(), QFont());
629 if (widg->font().pointSize() > font.pointSize())
630 return ret;
631 }
632
633 if (ct == QStyle::CT_CustomBase && widg) {
634 if (qobject_cast<const QPushButton *>(widg))
635 ct = QStyle::CT_PushButton;
636 else if (qobject_cast<const QRadioButton *>(widg))
637 ct = QStyle::CT_RadioButton;
638 else if (qobject_cast<const QCheckBox *>(widg))
639 ct = QStyle::CT_CheckBox;
640 else if (qobject_cast<const QComboBox *>(widg))
641 ct = QStyle::CT_ComboBox;
642 else if (qobject_cast<const QToolButton *>(widg))
643 ct = QStyle::CT_ToolButton;
644 else if (qobject_cast<const QSlider *>(widg))
645 ct = QStyle::CT_Slider;
646 else if (qobject_cast<const QProgressBar *>(widg))
647 ct = QStyle::CT_ProgressBar;
648 else if (qobject_cast<const QLineEdit *>(widg))
649 ct = QStyle::CT_LineEdit;
650 else if (qobject_cast<const QHeaderView *>(widg)
651#ifdef QT3_SUPPORT
652 || widg->inherits("Q3Header")
653#endif
654 )
655 ct = QStyle::CT_HeaderSection;
656 else if (qobject_cast<const QMenuBar *>(widg)
657#ifdef QT3_SUPPORT
658 || widg->inherits("Q3MenuBar")
659#endif
660 )
661 ct = QStyle::CT_MenuBar;
662 else if (qobject_cast<const QSizeGrip *>(widg))
663 ct = QStyle::CT_SizeGrip;
664 else
665 return ret;
666 }
667
668 switch (ct) {
669 case QStyle::CT_PushButton: {
670 const QPushButton *psh = qobject_cast<const QPushButton *>(widg);
671 // If this comparison is false, then the widget was not a push button.
672 // This is bad and there's very little we can do since we were requested to find a
673 // sensible size for a widget that pretends to be a QPushButton but is not.
674 if(psh) {
675 QString buttonText = qt_mac_removeMnemonics(psh->text());
676 if (buttonText.contains(QLatin1Char('\n')))
677 ret = QSize(-1, -1);
678 else if (sz == QAquaSizeLarge)
679 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
680 else if (sz == QAquaSizeSmall)
681 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPushButtonHeight));
682 else if (sz == QAquaSizeMini)
683 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPushButtonHeight));
684
685 if (!psh->icon().isNull()){
686 // If the button got an icon, and the icon is larger than the
687 // button, we can't decide on a default size
688 ret.setWidth(-1);
689 if (ret.height() < psh->iconSize().height())
690 ret.setHeight(-1);
691 }
692 else if (buttonText == QLatin1String("OK") || buttonText == QLatin1String("Cancel")){
693 // Aqua Style guidelines restrict the size of OK and Cancel buttons to 68 pixels.
694 // However, this doesn't work for German, therefore only do it for English,
695 // I suppose it would be better to do some sort of lookups for languages
696 // that like to have really long words.
697 ret.setWidth(77 - 8);
698 }
699 } else {
700 // The only sensible thing to do is to return whatever the style suggests...
701 if (sz == QAquaSizeLarge)
702 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
703 else if (sz == QAquaSizeSmall)
704 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPushButtonHeight));
705 else if (sz == QAquaSizeMini)
706 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPushButtonHeight));
707 else
708 // Since there's no default size we return the large size...
709 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
710 }
711#if 0 //Not sure we are applying the rules correctly for RadioButtons/CheckBoxes --Sam
712 } else if (ct == QStyle::CT_RadioButton) {
713 QRadioButton *rdo = static_cast<QRadioButton *>(widg);
714 // Exception for case where multiline radio button text requires no size constrainment
715 if (rdo->text().find('\n') != -1)
716 return ret;
717 if (sz == QAquaSizeLarge)
718 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricRadioButtonHeight));
719 else if (sz == QAquaSizeSmall)
720 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallRadioButtonHeight));
721 else if (sz == QAquaSizeMini)
722 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniRadioButtonHeight));
723 } else if (ct == QStyle::CT_CheckBox) {
724 if (sz == QAquaSizeLarge)
725 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricCheckBoxHeight));
726 else if (sz == QAquaSizeSmall)
727 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallCheckBoxHeight));
728 else if (sz == QAquaSizeMini)
729 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniCheckBoxHeight));
730#endif
731 break;
732 }
733 case QStyle::CT_SizeGrip:
734 if (sz == QAquaSizeLarge || sz == QAquaSizeSmall) {
735 HIRect r;
736 HIPoint p = { 0, 0 };
737 HIThemeGrowBoxDrawInfo gbi;
738 gbi.version = 0;
739 gbi.state = kThemeStateActive;
740 gbi.kind = kHIThemeGrowBoxKindNormal;
741 gbi.direction = QApplication::isRightToLeft() ? kThemeGrowLeft | kThemeGrowDown
742 : kThemeGrowRight | kThemeGrowDown;
743 gbi.size = sz == QAquaSizeSmall ? kHIThemeGrowBoxSizeSmall : kHIThemeGrowBoxSizeNormal;
744 if (HIThemeGetGrowBoxBounds(&p, &gbi, &r) == noErr)
745 ret = QSize(r.size.width, r.size.height);
746 }
747 break;
748 case QStyle::CT_ComboBox:
749 switch (sz) {
750 case QAquaSizeLarge:
751 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPopupButtonHeight));
752 break;
753 case QAquaSizeSmall:
754 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPopupButtonHeight));
755 break;
756 case QAquaSizeMini:
757 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPopupButtonHeight));
758 break;
759 default:
760 break;
761 }
762 break;
763 case QStyle::CT_ToolButton:
764 if (sz == QAquaSizeSmall) {
765 int width = 0, height = 0;
766 if (szHint == QSize(-1, -1)) { //just 'guess'..
767 const QToolButton *bt = qobject_cast<const QToolButton *>(widg);
768 // If this conversion fails then the widget was not what it claimed to be.
769 if(bt) {
770 if (!bt->icon().isNull()) {
771 QSize iconSize = bt->iconSize();
772 QSize pmSize = bt->icon().actualSize(QSize(32, 32), QIcon::Normal);
773 width = qMax(width, qMax(iconSize.width(), pmSize.width()));
774 height = qMax(height, qMax(iconSize.height(), pmSize.height()));
775 }
776 if (!bt->text().isNull() && bt->toolButtonStyle() != Qt::ToolButtonIconOnly) {
777 int text_width = bt->fontMetrics().width(bt->text()),
778 text_height = bt->fontMetrics().height();
779 if (bt->toolButtonStyle() == Qt::ToolButtonTextUnderIcon) {
780 width = qMax(width, text_width);
781 height += text_height;
782 } else {
783 width += text_width;
784 width = qMax(height, text_height);
785 }
786 }
787 } else {
788 // Let's return the size hint...
789 width = szHint.width();
790 height = szHint.height();
791 }
792 } else {
793 width = szHint.width();
794 height = szHint.height();
795 }
796 width = qMax(20, width + 5); //border
797 height = qMax(20, height + 5); //border
798 ret = QSize(width, height);
799 }
800 break;
801 case QStyle::CT_Slider: {
802 int w = -1;
803 const QSlider *sld = qobject_cast<const QSlider *>(widg);
804 // If this conversion fails then the widget was not what it claimed to be.
805 if(sld) {
806 if (sz == QAquaSizeLarge) {
807 if (sld->orientation() == Qt::Horizontal) {
808 w = qt_mac_aqua_get_metric(kThemeMetricHSliderHeight);
809 if (sld->tickPosition() != QSlider::NoTicks)
810 w += qt_mac_aqua_get_metric(kThemeMetricHSliderTickHeight);
811 } else {
812 w = qt_mac_aqua_get_metric(kThemeMetricVSliderWidth);
813 if (sld->tickPosition() != QSlider::NoTicks)
814 w += qt_mac_aqua_get_metric(kThemeMetricVSliderTickWidth);
815 }
816 } else if (sz == QAquaSizeSmall) {
817 if (sld->orientation() == Qt::Horizontal) {
818 w = qt_mac_aqua_get_metric(kThemeMetricSmallHSliderHeight);
819 if (sld->tickPosition() != QSlider::NoTicks)
820 w += qt_mac_aqua_get_metric(kThemeMetricSmallHSliderTickHeight);
821 } else {
822 w = qt_mac_aqua_get_metric(kThemeMetricSmallVSliderWidth);
823 if (sld->tickPosition() != QSlider::NoTicks)
824 w += qt_mac_aqua_get_metric(kThemeMetricSmallVSliderTickWidth);
825 }
826 } else if (sz == QAquaSizeMini) {
827 if (sld->orientation() == Qt::Horizontal) {
828 w = qt_mac_aqua_get_metric(kThemeMetricMiniHSliderHeight);
829 if (sld->tickPosition() != QSlider::NoTicks)
830 w += qt_mac_aqua_get_metric(kThemeMetricMiniHSliderTickHeight);
831 } else {
832 w = qt_mac_aqua_get_metric(kThemeMetricMiniVSliderWidth);
833 if (sld->tickPosition() != QSlider::NoTicks)
834 w += qt_mac_aqua_get_metric(kThemeMetricMiniVSliderTickWidth);
835 }
836 }
837 } else {
838 // This is tricky, we were requested to find a size for a slider which is not
839 // a slider. We don't know if this is vertical or horizontal or if we need to
840 // have tick marks or not.
841 // For this case we will return an horizontal slider without tick marks.
842 w = qt_mac_aqua_get_metric(kThemeMetricHSliderHeight);
843 w += qt_mac_aqua_get_metric(kThemeMetricHSliderTickHeight);
844 }
845 if (sld->orientation() == Qt::Horizontal)
846 ret.setHeight(w);
847 else
848 ret.setWidth(w);
849 break;
850 }
851 case QStyle::CT_ProgressBar: {
852 int finalValue = -1;
853 Qt::Orientation orient = Qt::Horizontal;
854 if (const QProgressBar *pb = qobject_cast<const QProgressBar *>(widg))
855 orient = pb->orientation();
856
857 if (sz == QAquaSizeLarge)
858 finalValue = qt_mac_aqua_get_metric(kThemeMetricLargeProgressBarThickness)
859 + qt_mac_aqua_get_metric(kThemeMetricProgressBarShadowOutset);
860 else
861 finalValue = qt_mac_aqua_get_metric(kThemeMetricNormalProgressBarThickness)
862 + qt_mac_aqua_get_metric(kThemeMetricSmallProgressBarShadowOutset);
863 if (orient == Qt::Horizontal)
864 ret.setHeight(finalValue);
865 else
866 ret.setWidth(finalValue);
867 break;
868 }
869 case QStyle::CT_LineEdit:
870 if (!widg || !qobject_cast<QComboBox *>(widg->parentWidget())) {
871 //should I take into account the font dimentions of the lineedit? -Sam
872 if (sz == QAquaSizeLarge)
873 ret = QSize(-1, 22);
874 else
875 ret = QSize(-1, 19);
876 }
877 break;
878 case QStyle::CT_HeaderSection:
879 if (isTreeView(widg))
880 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricListHeaderHeight));
881 break;
882 case QStyle::CT_MenuBar:
883 if (sz == QAquaSizeLarge) {
884#ifndef QT_MAC_USE_COCOA
885 SInt16 size;
886 if (!GetThemeMenuBarHeight(&size))
887 ret = QSize(-1, size);
888#else
889 ret = QSize(-1, [[NSApp mainMenu] menuBarHeight]);
890 // In the qt_mac_set_native_menubar(false) case,
891 // we come it here with a zero-height main menu,
892 // preventing the in-window menu from displaying.
893 // Use 22 pixels for the height, by observation.
894 if (ret.height() <= 0)
895 ret.setHeight(22);
896#endif
897 }
898 break;
899 default:
900 break;
901 }
902 return ret;
903}
904
905
906#if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT)
907static QAquaWidgetSize qt_aqua_guess_size(const QWidget *widg, QSize large, QSize small, QSize mini)
908{
909 if (large == QSize(-1, -1)) {
910 if (small != QSize(-1, -1))
911 return QAquaSizeSmall;
912 if (mini != QSize(-1, -1))
913 return QAquaSizeMini;
914 return QAquaSizeUnknown;
915 } else if (small == QSize(-1, -1)) {
916 if (mini != QSize(-1, -1))
917 return QAquaSizeMini;
918 return QAquaSizeLarge;
919 } else if (mini == QSize(-1, -1)) {
920 return QAquaSizeLarge;
921 }
922
923#ifndef QT_NO_MAINWINDOW
924 if (qobject_cast<QDockWidget *>(widg->window()) || !qgetenv("QWIDGET_ALL_SMALL").isNull()) {
925 //if (small.width() != -1 || small.height() != -1)
926 return QAquaSizeSmall;
927 } else if (!qgetenv("QWIDGET_ALL_MINI").isNull()) {
928 return QAquaSizeMini;
929 }
930#endif
931
932#if 0
933 /* Figure out which size we're closer to, I just hacked this in, I haven't
934 tested it as it would probably look pretty strange to have some widgets
935 big and some widgets small in the same window?? -Sam */
936 int large_delta=0;
937 if (large.width() != -1) {
938 int delta = large.width() - widg->width();
939 large_delta += delta * delta;
940 }
941 if (large.height() != -1) {
942 int delta = large.height() - widg->height();
943 large_delta += delta * delta;
944 }
945 int small_delta=0;
946 if (small.width() != -1) {
947 int delta = small.width() - widg->width();
948 small_delta += delta * delta;
949 }
950 if (small.height() != -1) {
951 int delta = small.height() - widg->height();
952 small_delta += delta * delta;
953 }
954 int mini_delta=0;
955 if (mini.width() != -1) {
956 int delta = mini.width() - widg->width();
957 mini_delta += delta * delta;
958 }
959 if (mini.height() != -1) {
960 int delta = mini.height() - widg->height();
961 mini_delta += delta * delta;
962 }
963 if (mini_delta < small_delta && mini_delta < large_delta)
964 return QAquaSizeMini;
965 else if (small_delta < large_delta)
966 return QAquaSizeSmall;
967#endif
968 return QAquaSizeLarge;
969}
970#endif
971
972QAquaWidgetSize QMacStylePrivate::aquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
973 QStyle::ContentsType ct, QSize szHint, QSize *insz) const
974{
975#if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT)
976 if (option) {
977 if (option->state & QStyle::State_Small)
978 return QAquaSizeSmall;
979 if (option->state & QStyle::State_Mini)
980 return QAquaSizeMini;
981 }
982
983 if (!widg) {
984 if (insz)
985 *insz = QSize();
986 if (!qgetenv("QWIDGET_ALL_SMALL").isNull())
987 return QAquaSizeSmall;
988 if (!qgetenv("QWIDGET_ALL_MINI").isNull())
989 return QAquaSizeMini;
990 return QAquaSizeUnknown;
991 }
992 QSize large = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeLarge),
993 small = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeSmall),
994 mini = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeMini);
995 bool guess_size = false;
996 QAquaWidgetSize ret = QAquaSizeUnknown;
997 QMacStyle::WidgetSizePolicy wsp = q->widgetSizePolicy(widg);
998 if (wsp == QMacStyle::SizeDefault)
999 guess_size = true;
1000 else if (wsp == QMacStyle::SizeMini)
1001 ret = QAquaSizeMini;
1002 else if (wsp == QMacStyle::SizeSmall)
1003 ret = QAquaSizeSmall;
1004 else if (wsp == QMacStyle::SizeLarge)
1005 ret = QAquaSizeLarge;
1006 if (guess_size)
1007 ret = qt_aqua_guess_size(widg, large, small, mini);
1008
1009 QSize *sz = 0;
1010 if (ret == QAquaSizeSmall)
1011 sz = &small;
1012 else if (ret == QAquaSizeLarge)
1013 sz = &large;
1014 else if (ret == QAquaSizeMini)
1015 sz = &mini;
1016 if (insz)
1017 *insz = sz ? *sz : QSize(-1, -1);
1018#ifdef DEBUG_SIZE_CONSTRAINT
1019 if (sz) {
1020 const char *size_desc = "Unknown";
1021 if (sz == &small)
1022 size_desc = "Small";
1023 else if (sz == &large)
1024 size_desc = "Large";
1025 else if (sz == &mini)
1026 size_desc = "Mini";
1027 qDebug("%s - %s: %s taken (%d, %d) [%d, %d]",
1028 widg ? widg->objectName().toLatin1().constData() : "*Unknown*",
1029 widg ? widg->metaObject()->className() : "*Unknown*", size_desc, widg->width(), widg->height(),
1030 sz->width(), sz->height());
1031 }
1032#endif
1033 return ret;
1034#else
1035 if (insz)
1036 *insz = QSize();
1037 Q_UNUSED(widg);
1038 Q_UNUSED(ct);
1039 Q_UNUSED(szHint);
1040 return QAquaSizeUnknown;
1041#endif
1042}
1043
1044/**
1045 Returns the free space awailable for contents inside the
1046 button (and not the size of the contents itself)
1047*/
1048HIRect QMacStylePrivate::pushButtonContentBounds(const QStyleOptionButton *btn,
1049 const HIThemeButtonDrawInfo *bdi) const
1050{
1051 HIRect outerBounds = qt_hirectForQRect(btn->rect);
1052 // Adjust the bounds to correct for
1053 // carbon not calculating the content bounds fully correct
1054 if (bdi->kind == kThemePushButton || bdi->kind == kThemePushButtonSmall){
1055 outerBounds.origin.y += PushButtonTopOffset;
1056 outerBounds.size.height -= PushButtonBottomOffset;
1057 } else if (bdi->kind == kThemePushButtonMini) {
1058 outerBounds.origin.y += PushButtonTopOffset;
1059 }
1060
1061 HIRect contentBounds;
1062 HIThemeGetButtonContentBounds(&outerBounds, bdi, &contentBounds);
1063 return contentBounds;
1064}
1065
1066/**
1067 Calculates the size of the button contents.
1068 This includes both the text and the icon.
1069*/
1070QSize QMacStylePrivate::pushButtonSizeFromContents(const QStyleOptionButton *btn) const
1071{
1072 QSize csz(0, 0);
1073 QSize iconSize = btn->icon.isNull() ? QSize(0, 0)
1074 : (btn->iconSize + QSize(PushButtonContentPadding, 0));
1075 QRect textRect = btn->text.isEmpty() ? QRect(0, 0, 1, 1)
1076 : btn->fontMetrics.boundingRect(QRect(), Qt::AlignCenter, btn->text);
1077 csz.setWidth(iconSize.width() + textRect.width()
1078 + ((btn->features & QStyleOptionButton::HasMenu)
1079 ? q->proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, btn, 0) : 0));
1080 csz.setHeight(qMax(iconSize.height(), textRect.height()));
1081 return csz;
1082}
1083
1084/**
1085 Checks if the actual contents of btn fits inside the free content bounds of
1086 'buttonKindToCheck'. Meant as a helper function for 'initHIThemePushButton'
1087 for determining which button kind to use for drawing.
1088*/
1089bool QMacStylePrivate::contentFitsInPushButton(const QStyleOptionButton *btn,
1090 HIThemeButtonDrawInfo *bdi,
1091 ThemeButtonKind buttonKindToCheck) const
1092{
1093 ThemeButtonKind tmp = bdi->kind;
1094 bdi->kind = buttonKindToCheck;
1095 QSize contentSize = pushButtonSizeFromContents(btn);
1096 QRect freeContentRect = qt_qrectForHIRect(pushButtonContentBounds(btn, bdi));
1097 bdi->kind = tmp;
1098 return freeContentRect.contains(QRect(freeContentRect.x(), freeContentRect.y(),
1099 contentSize.width(), contentSize.height()));
1100}
1101
1102/**
1103 Creates a HIThemeButtonDrawInfo structure that specifies the correct button
1104 kind and other details to use for drawing the given push button. Which
1105 button kind depends on the size of the button, the size of the contents,
1106 explicit user style settings, etc.
1107*/
1108void QMacStylePrivate::initHIThemePushButton(const QStyleOptionButton *btn,
1109 const QWidget *widget,
1110 const ThemeDrawState tds,
1111 HIThemeButtonDrawInfo *bdi) const
1112{
1113 bool drawColorless = btn->palette.currentColorGroup() == QPalette::Active;
1114 ThemeDrawState tdsModified = tds;
1115 if (btn->state & QStyle::State_On)
1116 tdsModified = kThemeStatePressed;
1117 bdi->version = qt_mac_hitheme_version;
1118 bdi->state = tdsModified;
1119 bdi->value = kThemeButtonOff;
1120
1121 if (drawColorless && tdsModified == kThemeStateInactive)
1122 bdi->state = kThemeStateActive;
1123 if (btn->state & QStyle::State_HasFocus)
1124 bdi->adornment = kThemeAdornmentFocus;
1125 else
1126 bdi->adornment = kThemeAdornmentNone;
1127
1128
1129 if (btn->features & (QStyleOptionButton::Flat)) {
1130 bdi->kind = kThemeBevelButton;
1131 } else {
1132 switch (aquaSizeConstrain(btn, widget)) {
1133 case QAquaSizeSmall:
1134 bdi->kind = kThemePushButtonSmall;
1135 break;
1136 case QAquaSizeMini:
1137 bdi->kind = kThemePushButtonMini;
1138 break;
1139 case QAquaSizeLarge:
1140 // ... We should honor if the user is explicit about using the
1141 // large button. But right now Qt will specify the large button
1142 // as default rather than QAquaSizeUnknown.
1143 // So we treat it like QAquaSizeUnknown
1144 // to get the dynamic choosing of button kind.
1145 case QAquaSizeUnknown:
1146 // Choose the button kind that closest match the button rect, but at the
1147 // same time displays the button contents without clipping.
1148 bdi->kind = kThemeBevelButton;
1149 if (btn->rect.width() >= BevelButtonW && btn->rect.height() >= BevelButtonH){
1150 if (widget && widget->testAttribute(Qt::WA_MacVariableSize)) {
1151 if (btn->rect.height() <= MiniButtonH){
1152 if (contentFitsInPushButton(btn, bdi, kThemePushButtonMini))
1153 bdi->kind = kThemePushButtonMini;
1154 } else if (btn->rect.height() <= SmallButtonH){
1155 if (contentFitsInPushButton(btn, bdi, kThemePushButtonSmall))
1156 bdi->kind = kThemePushButtonSmall;
1157 } else if (contentFitsInPushButton(btn, bdi, kThemePushButton)) {
1158 bdi->kind = kThemePushButton;
1159 }
1160 } else {
1161 bdi->kind = kThemePushButton;
1162 }
1163 }
1164 }
1165 }
1166}
1167
1168/**
1169 Creates a HIThemeButtonDrawInfo structure that specifies the correct button
1170 kind and other details to use for drawing the given combobox. Which button
1171 kind depends on the size of the combo, wether or not it is editable,
1172 explicit user style settings, etc.
1173*/
1174void QMacStylePrivate::initComboboxBdi(const QStyleOptionComboBox *combo, HIThemeButtonDrawInfo *bdi,
1175 const QWidget *widget, const ThemeDrawState &tds)
1176{
1177 bdi->version = qt_mac_hitheme_version;
1178 bdi->adornment = kThemeAdornmentArrowLeftArrow;
1179 bdi->value = kThemeButtonOff;
1180 if (combo->state & QStyle::State_HasFocus)
1181 bdi->adornment = kThemeAdornmentFocus;
1182 bool drawColorless = combo->palette.currentColorGroup() == QPalette::Active && tds == kThemeStateInactive;
1183 if (combo->activeSubControls & QStyle::SC_ComboBoxArrow)
1184 bdi->state = kThemeStatePressed;
1185 else if (drawColorless)
1186 bdi->state = kThemeStateActive;
1187 else
1188 bdi->state = tds;
1189
1190 QAquaWidgetSize aSize = aquaSizeConstrain(combo, widget);
1191 switch (aSize) {
1192 case QAquaSizeMini:
1193 bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxMini)
1194 : ThemeButtonKind(kThemePopupButtonMini);
1195 break;
1196 case QAquaSizeSmall:
1197 bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxSmall)
1198 : ThemeButtonKind(kThemePopupButtonSmall);
1199 break;
1200 case QAquaSizeUnknown:
1201 case QAquaSizeLarge:
1202 // Unless the user explicitly specified large buttons, determine the
1203 // kind by looking at the combox size.
1204 // ... specifying small and mini-buttons it not a current feature of
1205 // Qt (e.g. QWidget::getAttribute(WA_ButtonSize)). But when it is, add
1206 // an extra check here before using the mini and small buttons.
1207 int h = combo->rect.size().height();
1208 if (combo->editable){
1209 if (h < 21)
1210 bdi->kind = kThemeComboBoxMini;
1211 else if (h < 26)
1212 bdi->kind = kThemeComboBoxSmall;
1213 else
1214 bdi->kind = kThemeComboBox;
1215 } else {
1216 // Even if we specify that we want the kThemePopupButton, Carbon
1217 // will use the kThemePopupButtonSmall if the size matches. So we
1218 // do the same size check explicit to have the size of the inner
1219 // text field be correct. Therefore, do this even if the user specifies
1220 // the use of LargeButtons explicit.
1221 if (h < 21)
1222 bdi->kind = kThemePopupButtonMini;
1223 else if (h < 26)
1224 bdi->kind = kThemePopupButtonSmall;
1225 else
1226 bdi->kind = kThemePopupButton;
1227 }
1228 break;
1229 }
1230}
1231
1232/**
1233 Carbon draws comboboxes (and other views) outside the rect given as argument. Use this function to obtain
1234 the corresponding inner rect for drawing the same combobox so that it stays inside the given outerBounds.
1235*/
1236HIRect QMacStylePrivate::comboboxInnerBounds(const HIRect &outerBounds, int buttonKind)
1237{
1238 HIRect innerBounds = outerBounds;
1239 // Carbon draw parts of the view outside the rect.
1240 // So make the rect a bit smaller to compensate
1241 // (I wish HIThemeGetButtonBackgroundBounds worked)
1242 switch (buttonKind){
1243 case kThemePopupButton:
1244 innerBounds.origin.x += 2;
1245 innerBounds.origin.y += 3;
1246 innerBounds.size.width -= 5;
1247 innerBounds.size.height -= 6;
1248 break;
1249 case kThemePopupButtonSmall:
1250 innerBounds.origin.x += 3;
1251 innerBounds.origin.y += 3;
1252 innerBounds.size.width -= 6;
1253 innerBounds.size.height -= 7;
1254 break;
1255 case kThemePopupButtonMini:
1256 innerBounds.origin.x += 2;
1257 innerBounds.origin.y += 2;
1258 innerBounds.size.width -= 5;
1259 innerBounds.size.height -= 6;
1260 break;
1261 case kThemeComboBox:
1262 innerBounds.origin.x += 3;
1263 innerBounds.origin.y += 3;
1264 innerBounds.size.width -= 6;
1265 innerBounds.size.height -= 6;
1266 break;
1267 case kThemeComboBoxSmall:
1268 innerBounds.origin.x += 3;
1269 innerBounds.origin.y += 3;
1270 innerBounds.size.width -= 7;
1271 innerBounds.size.height -= 8;
1272 break;
1273 case kThemeComboBoxMini:
1274 innerBounds.origin.x += 3;
1275 innerBounds.origin.y += 3;
1276 innerBounds.size.width -= 4;
1277 innerBounds.size.height -= 8;
1278 break;
1279 default:
1280 break;
1281 }
1282 return innerBounds;
1283}
1284
1285/**
1286 Inside a combobox Qt places a line edit widget. The size of this widget should depend on the kind
1287 of combobox we choose to draw. This function calculates and returns this size.
1288*/
1289QRect QMacStylePrivate::comboboxEditBounds(const QRect &outerBounds, const HIThemeButtonDrawInfo &bdi)
1290{
1291 QRect ret = outerBounds;
1292 switch (bdi.kind){
1293 case kThemeComboBox:
1294 ret.adjust(5, 8, -21, -4);
1295 break;
1296 case kThemeComboBoxSmall:
1297 ret.adjust(4, 5, -18, 0);
1298 ret.setHeight(16);
1299 break;
1300 case kThemeComboBoxMini:
1301 ret.adjust(4, 5, -16, 0);
1302 ret.setHeight(13);
1303 break;
1304 case kThemePopupButton:
1305 ret.adjust(10, 3, -23, -3);
1306 break;
1307 case kThemePopupButtonSmall:
1308 ret.adjust(9, 3, -20, -3);
1309 break;
1310 case kThemePopupButtonMini:
1311 ret.adjust(8, 3, -19, 0);
1312 ret.setHeight(13);
1313 break;
1314 }
1315 return ret;
1316}
1317
1318/**
1319 Carbon comboboxes don't scale (sight). If the size of the combo suggest a scaled version,
1320 create it manually by drawing a small Carbon combo onto a pixmap (use pixmap cache), chop
1321 it up, and copy it back onto the widget. Othervise, draw the combobox supplied by Carbon directly.
1322*/
1323void QMacStylePrivate::drawCombobox(const HIRect &outerBounds, const HIThemeButtonDrawInfo &bdi, QPainter *p)
1324{
1325 if (!(bdi.kind == kThemeComboBox && outerBounds.size.height > 28)){
1326 // We have an unscaled combobox, or popup-button; use Carbon directly.
1327 HIRect innerBounds = QMacStylePrivate::comboboxInnerBounds(outerBounds, bdi.kind);
1328 HIThemeDrawButton(&innerBounds, &bdi, QMacCGContext(p), kHIThemeOrientationNormal, 0);
1329 } else {
1330 QPixmap buffer;
1331 QString key = QString(QLatin1String("$qt_cbox%1-%2")).arg(int(bdi.state)).arg(int(bdi.adornment));
1332 if (!QPixmapCache::find(key, buffer)) {
1333 HIRect innerBoundsSmallCombo = {{3, 3}, {29, 25}};
1334 buffer = QPixmap(35, 28);
1335 buffer.fill(Qt::transparent);
1336 QPainter buffPainter(&buffer);
1337 HIThemeDrawButton(&innerBoundsSmallCombo, &bdi, QMacCGContext(&buffPainter), kHIThemeOrientationNormal, 0);
1338 buffPainter.end();
1339 QPixmapCache::insert(key, buffer);
1340 }
1341
1342 const int bwidth = 20;
1343 const int fwidth = 10;
1344 const int fheight = 10;
1345 int w = qRound(outerBounds.size.width);
1346 int h = qRound(outerBounds.size.height);
1347 int bstart = w - bwidth;
1348 int blower = fheight + 1;
1349 int flower = h - fheight;
1350 int sheight = flower - fheight;
1351 int center = qRound(outerBounds.size.height + outerBounds.origin.y) / 2;
1352
1353 // Draw upper and lower gap
1354 p->drawPixmap(fwidth, 0, bstart - fwidth, fheight, buffer, fwidth, 0, 1, fheight);
1355 p->drawPixmap(fwidth, flower, bstart - fwidth, fheight, buffer, fwidth, buffer.height() - fheight, 1, fheight);
1356 // Draw left and right gap. Right gap is drawn top and bottom separatly
1357 p->drawPixmap(0, fheight, fwidth, sheight, buffer, 0, fheight, fwidth, 1);
1358 p->drawPixmap(bstart, fheight, bwidth, center - fheight, buffer, buffer.width() - bwidth, fheight - 1, bwidth, 1);
1359 p->drawPixmap(bstart, center, bwidth, sheight / 2, buffer, buffer.width() - bwidth, fheight + 6, bwidth, 1);
1360 // Draw arrow
1361 p->drawPixmap(bstart, center - 4, bwidth - 3, 6, buffer, buffer.width() - bwidth, fheight, bwidth - 3, 6);
1362 // Draw corners
1363 p->drawPixmap(0, 0, fwidth, fheight, buffer, 0, 0, fwidth, fheight);
1364 p->drawPixmap(bstart, 0, bwidth, fheight, buffer, buffer.width() - bwidth, 0, bwidth, fheight);
1365 p->drawPixmap(0, flower, fwidth, fheight, buffer, 0, buffer.height() - fheight, fwidth, fheight);
1366 p->drawPixmap(bstart, h - blower, bwidth, blower, buffer, buffer.width() - bwidth, buffer.height() - blower, bwidth, blower);
1367 }
1368}
1369
1370/**
1371 Carbon tableheaders don't scale (sight). So create it manually by drawing a small Carbon header
1372 onto a pixmap (use pixmap cache), chop it up, and copy it back onto the widget.
1373*/
1374void QMacStylePrivate::drawTableHeader(const HIRect &outerBounds,
1375 bool drawTopBorder, bool drawLeftBorder, const HIThemeButtonDrawInfo &bdi, QPainter *p)
1376{
1377 static SInt32 headerHeight = 0;
1378 static OSStatus err = GetThemeMetric(kThemeMetricListHeaderHeight, &headerHeight);
1379 Q_UNUSED(err);
1380
1381 QPixmap buffer;
1382 QString key = QString(QLatin1String("$qt_tableh%1-%2-%3")).arg(int(bdi.state)).arg(int(bdi.adornment)).arg(int(bdi.value));
1383 if (!QPixmapCache::find(key, buffer)) {
1384 HIRect headerNormalRect = {{0., 0.}, {16., CGFloat(headerHeight)}};
1385 buffer = QPixmap(headerNormalRect.size.width, headerNormalRect.size.height);
1386 buffer.fill(Qt::transparent);
1387 QPainter buffPainter(&buffer);
1388 HIThemeDrawButton(&headerNormalRect, &bdi, QMacCGContext(&buffPainter), kHIThemeOrientationNormal, 0);
1389 buffPainter.end();
1390 QPixmapCache::insert(key, buffer);
1391 }
1392 const int buttonw = qRound(outerBounds.size.width);
1393 const int buttonh = qRound(outerBounds.size.height);
1394 const int framew = 1;
1395 const int frameh_n = 4;
1396 const int frameh_s = 3;
1397 const int transh = buffer.height() - frameh_n - frameh_s;
1398 int center = buttonh - frameh_s - int(transh / 2.0f) + 1; // Align bottom;
1399
1400 int skipTopBorder = 0;
1401 if (!drawTopBorder)
1402 skipTopBorder = 1;
1403
1404 p->translate(outerBounds.origin.x, outerBounds.origin.y);
1405
1406 p->drawPixmap(QRect(QRect(0, -skipTopBorder, buttonw - framew , frameh_n)), buffer, QRect(framew, 0, 1, frameh_n));
1407 p->drawPixmap(QRect(0, buttonh - frameh_s, buttonw - framew, frameh_s), buffer, QRect(framew, buffer.height() - frameh_s, 1, frameh_s));
1408 // Draw upper and lower center blocks
1409 p->drawPixmap(QRect(0, frameh_n - skipTopBorder, buttonw - framew, center - frameh_n + skipTopBorder), buffer, QRect(framew, frameh_n, 1, 1));
1410 p->drawPixmap(QRect(0, center, buttonw - framew, buttonh - center - frameh_s), buffer, QRect(framew, buffer.height() - frameh_s, 1, 1));
1411 // Draw right center block borders
1412 p->drawPixmap(QRect(buttonw - framew, frameh_n - skipTopBorder, framew, center - frameh_n), buffer, QRect(buffer.width() - framew, frameh_n, framew, 1));
1413 p->drawPixmap(QRect(buttonw - framew, center, framew, buttonh - center - 1), buffer, QRect(buffer.width() - framew, buffer.height() - frameh_s, framew, 1));
1414 // Draw right corners
1415 p->drawPixmap(QRect(buttonw - framew, -skipTopBorder, framew, frameh_n), buffer, QRect(buffer.width() - framew, 0, framew, frameh_n));
1416 p->drawPixmap(QRect(buttonw - framew, buttonh - frameh_s, framew, frameh_s), buffer, QRect(buffer.width() - framew, buffer.height() - frameh_s, framew, frameh_s));
1417 // Draw center transition block
1418 p->drawPixmap(QRect(0, center - qRound(transh / 2.0f), buttonw - framew, buffer.height() - frameh_n - frameh_s), buffer, QRect(framew, frameh_n + 1, 1, transh));
1419 // Draw right center transition block border
1420 p->drawPixmap(QRect(buttonw - framew, center - qRound(transh / 2.0f), framew, buffer.height() - frameh_n - frameh_s), buffer, QRect(buffer.width() - framew, frameh_n + 1, framew, transh));
1421 if (drawLeftBorder){
1422 // Draw left center block borders
1423 p->drawPixmap(QRect(0, frameh_n - skipTopBorder, framew, center - frameh_n + skipTopBorder), buffer, QRect(0, frameh_n, framew, 1));
1424 p->drawPixmap(QRect(0, center, framew, buttonh - center - 1), buffer, QRect(0, buffer.height() - frameh_s, framew, 1));
1425 // Draw left corners
1426 p->drawPixmap(QRect(0, -skipTopBorder, framew, frameh_n), buffer, QRect(0, 0, framew, frameh_n));
1427 p->drawPixmap(QRect(0, buttonh - frameh_s, framew, frameh_s), buffer, QRect(0, buffer.height() - frameh_s, framew, frameh_s));
1428 // Draw left center transition block border
1429 p->drawPixmap(QRect(0, center - qRound(transh / 2.0f), framew, buffer.height() - frameh_n - frameh_s), buffer, QRect(0, frameh_n + 1, framew, transh));
1430 }
1431
1432 p->translate(-outerBounds.origin.x, -outerBounds.origin.y);
1433}
1434
1435/*
1436 Returns cutoff sizes for scroll bars.
1437 thumbIndicatorCutoff is the smallest size where the thumb indicator is drawn.
1438 scrollButtonsCutoff is the smallest size where the up/down buttons is drawn.
1439*/
1440enum ScrollBarCutoffType { thumbIndicatorCutoff = 0, scrollButtonsCutoff = 1 };
1441static int scrollButtonsCutoffSize(ScrollBarCutoffType cutoffType, QMacStyle::WidgetSizePolicy widgetSize)
1442{
1443 // Mini scroll bars do not exist as of version 10.4.
1444 if (widgetSize == QMacStyle::SizeMini)
1445 return 0;
1446
1447 const int sizeIndex = (widgetSize == QMacStyle::SizeSmall) ? 1 : 0;
1448 static const int sizeTable[2][2] = { { 61, 56 }, { 49, 44 } };
1449 return sizeTable[sizeIndex][cutoffType];
1450}
1451
1452void QMacStylePrivate::getSliderInfo(QStyle::ComplexControl cc, const QStyleOptionSlider *slider,
1453 HIThemeTrackDrawInfo *tdi, const QWidget *needToRemoveMe)
1454{
1455 memset(tdi, 0, sizeof(HIThemeTrackDrawInfo)); // We don't get it all for some reason or another...
1456 tdi->version = qt_mac_hitheme_version;
1457 tdi->reserved = 0;
1458 tdi->filler1 = 0;
1459 bool isScrollbar = (cc == QStyle::CC_ScrollBar);
1460 switch (aquaSizeConstrain(0, needToRemoveMe)) {
1461 case QAquaSizeUnknown:
1462 case QAquaSizeLarge:
1463 if (isScrollbar)
1464 tdi->kind = kThemeMediumScrollBar;
1465 else
1466 tdi->kind = kThemeMediumSlider;
1467 break;
1468 case QAquaSizeMini:
1469 if (isScrollbar)
1470 tdi->kind = kThemeSmallScrollBar; // should be kThemeMiniScrollBar, but not implemented
1471 else
1472 tdi->kind = kThemeMiniSlider;
1473 break;
1474 case QAquaSizeSmall:
1475 if (isScrollbar)
1476 tdi->kind = kThemeSmallScrollBar;
1477 else
1478 tdi->kind = kThemeSmallSlider;
1479 break;
1480 }
1481 tdi->bounds = qt_hirectForQRect(slider->rect);
1482 tdi->min = slider->minimum;
1483 tdi->max = slider->maximum;
1484 tdi->value = slider->sliderPosition;
1485 tdi->attributes = kThemeTrackShowThumb;
1486 if (slider->upsideDown)
1487 tdi->attributes |= kThemeTrackRightToLeft;
1488 if (slider->orientation == Qt::Horizontal) {
1489 tdi->attributes |= kThemeTrackHorizontal;
1490 if (isScrollbar && slider->direction == Qt::RightToLeft) {
1491 if (!slider->upsideDown)
1492 tdi->attributes |= kThemeTrackRightToLeft;
1493 else
1494 tdi->attributes &= ~kThemeTrackRightToLeft;
1495 }
1496 }
1497
1498 // Tiger broke reverse scroll bars so put them back and "fake it"
1499 if (isScrollbar && (tdi->attributes & kThemeTrackRightToLeft)) {
1500 tdi->attributes &= ~kThemeTrackRightToLeft;
1501 tdi->value = tdi->max - slider->sliderPosition;
1502 }
1503
1504 tdi->enableState = (slider->state & QStyle::State_Enabled) ? kThemeTrackActive
1505 : kThemeTrackDisabled;
1506 if (!(slider->state & QStyle::State_Active))
1507 tdi->enableState = kThemeTrackInactive;
1508 if (!isScrollbar) {
1509 if (slider->state & QStyle::QStyle::State_HasFocus)
1510 tdi->attributes |= kThemeTrackHasFocus;
1511 if (slider->tickPosition == QSlider::NoTicks || slider->tickPosition == QSlider::TicksBothSides)
1512 tdi->trackInfo.slider.thumbDir = kThemeThumbPlain;
1513 else if (slider->tickPosition == QSlider::TicksAbove)
1514 tdi->trackInfo.slider.thumbDir = kThemeThumbUpward;
1515 else
1516 tdi->trackInfo.slider.thumbDir = kThemeThumbDownward;
1517 } else {
1518 tdi->trackInfo.scrollbar.viewsize = slider->pageStep;
1519 }
1520}
1521#endif
1522
1523QMacStylePrivate::QMacStylePrivate(QMacStyle *style)
1524 : timerID(-1), progressFrame(0), q(style), mouseDown(false)
1525{
1526 defaultButtonStart = CFAbsoluteTimeGetCurrent();
1527 memset(&buttonState, 0, sizeof(ButtonState));
1528
1529 if (ptrHIShapeGetBounds == 0) {
1530 QLibrary library(QLatin1String("/System/Library/Frameworks/Carbon.framework/Carbon"));
1531 library.setLoadHints(QLibrary::ExportExternalSymbolsHint);
1532 ptrHIShapeGetBounds = reinterpret_cast<PtrHIShapeGetBounds>(library.resolve("HIShapeGetBounds"));
1533 }
1534
1535}
1536
1537bool QMacStylePrivate::animatable(QMacStylePrivate::Animates as, const QWidget *w) const
1538{
1539 if (as == AquaPushButton) {
1540 QPushButton *pb = const_cast<QPushButton *>(static_cast<const QPushButton *>(w));
1541 if (w->window()->isActiveWindow() && pb && !mouseDown) {
1542 if (static_cast<const QPushButton *>(w) != defaultButton) {
1543 // Changed on its own, update the value.
1544 const_cast<QMacStylePrivate *>(this)->stopAnimate(as, defaultButton);
1545 const_cast<QMacStylePrivate *>(this)->startAnimate(as, pb);
1546 }
1547 return true;
1548 }
1549 } else if (as == AquaProgressBar) {
1550 if (progressBars.contains((const_cast<QWidget *>(w))))
1551 return true;
1552 }
1553 return false;
1554}
1555
1556void QMacStylePrivate::stopAnimate(QMacStylePrivate::Animates as, QWidget *w)
1557{
1558 if (as == AquaPushButton && defaultButton) {
1559 QPushButton *tmp = defaultButton;
1560 defaultButton = 0;
1561 tmp->update();
1562 } else if (as == AquaProgressBar) {
1563 progressBars.removeAll(w);
1564 }
1565}
1566
1567void QMacStylePrivate::startAnimate(QMacStylePrivate::Animates as, QWidget *w)
1568{
1569 if (as == AquaPushButton)
1570 defaultButton = static_cast<QPushButton *>(w);
1571 else if (as == AquaProgressBar)
1572 progressBars.append(w);
1573 startAnimationTimer();
1574}
1575
1576void QMacStylePrivate::startAnimationTimer()
1577{
1578 if ((defaultButton || !progressBars.isEmpty()) && timerID <= -1)
1579 timerID = startTimer(animateSpeed(AquaListViewItemOpen));
1580}
1581
1582bool QMacStylePrivate::addWidget(QWidget *w)
1583{
1584 //already knew of it
1585 if (static_cast<QPushButton*>(w) == defaultButton
1586 || progressBars.contains(static_cast<QProgressBar*>(w)))
1587 return false;
1588
1589 if (QPushButton *btn = qobject_cast<QPushButton *>(w)) {
1590 btn->installEventFilter(this);
1591 if (btn->isDefault() || (btn->autoDefault() && btn->hasFocus()))
1592 startAnimate(AquaPushButton, btn);
1593 return true;
1594 } else {
1595 bool isProgressBar = (qobject_cast<QProgressBar *>(w)
1596#ifdef QT3_SUPPORT
1597 || w->inherits("Q3ProgressBar")
1598#endif
1599 );
1600 if (isProgressBar) {
1601 w->installEventFilter(this);
1602 startAnimate(AquaProgressBar, w);
1603 return true;
1604 }
1605 }
1606 if (w->isWindow()) {
1607 w->installEventFilter(this);
1608 return true;
1609 }
1610 return false;
1611}
1612
1613void QMacStylePrivate::removeWidget(QWidget *w)
1614{
1615 QPushButton *btn = qobject_cast<QPushButton *>(w);
1616 if (btn && btn == defaultButton) {
1617 stopAnimate(AquaPushButton, btn);
1618 } else if (qobject_cast<QProgressBar *>(w)
1619#ifdef QT3_SUPPORT
1620 || w->inherits("Q3ProgressBar")
1621#endif
1622 ) {
1623 stopAnimate(AquaProgressBar, w);
1624 }
1625}
1626
1627ThemeDrawState QMacStylePrivate::getDrawState(QStyle::State flags)
1628{
1629 ThemeDrawState tds = kThemeStateActive;
1630 if (flags & QStyle::State_Sunken) {
1631 tds = kThemeStatePressed;
1632 } else if (flags & QStyle::State_Active) {
1633 if (!(flags & QStyle::State_Enabled))
1634 tds = kThemeStateUnavailable;
1635 } else {
1636 if (flags & QStyle::State_Enabled)
1637 tds = kThemeStateInactive;
1638 else
1639 tds = kThemeStateUnavailableInactive;
1640 }
1641 return tds;
1642}
1643
1644void QMacStylePrivate::timerEvent(QTimerEvent *)
1645{
1646 int animated = 0;
1647 if (defaultButton && defaultButton->isEnabled() && defaultButton->window()->isActiveWindow()
1648 && defaultButton->isVisibleTo(0) && (defaultButton->isDefault()
1649 || (defaultButton->autoDefault() && defaultButton->hasFocus()))
1650 && doAnimate(AquaPushButton)) {
1651 ++animated;
1652 defaultButton->update();
1653 }
1654 if (!progressBars.isEmpty()) {
1655 int i = 0;
1656 while (i < progressBars.size()) {
1657 QWidget *maybeProgress = progressBars.at(i);
1658 if (!maybeProgress) {
1659 progressBars.removeAt(i);
1660 } else {
1661 if (QProgressBar *pb = qobject_cast<QProgressBar *>(maybeProgress)) {
1662 if (pb->maximum() == 0 || pb->value() > 0
1663 && pb->value() < pb->maximum()) {
1664 if (doAnimate(AquaProgressBar))
1665 pb->update();
1666 }
1667 }
1668#ifdef QT3_SUPPORT
1669 else {
1670 // Watch me now...
1671 QVariant progress = maybeProgress->property("progress");
1672 QVariant totalSteps = maybeProgress->property("totalSteps");
1673 if (progress.isValid() && totalSteps.isValid()) {
1674 int intProgress = progress.toInt();
1675 int intTotalSteps = totalSteps.toInt();
1676 if (intTotalSteps == 0 || intProgress > 0 && intProgress < intTotalSteps) {
1677 if (doAnimate(AquaProgressBar))
1678 maybeProgress->update();
1679 }
1680 }
1681 }
1682#endif
1683 ++i;
1684 }
1685 }
1686 if (i > 0) {
1687 ++progressFrame;
1688 animated += i;
1689 }
1690 }
1691 if (animated <= 0) {
1692 killTimer(timerID);
1693 timerID = -1;
1694 }
1695}
1696
1697bool QMacStylePrivate::eventFilter(QObject *o, QEvent *e)
1698{
1699 //animate
1700 if (QProgressBar *pb = qobject_cast<QProgressBar *>(o)) {
1701 switch (e->type()) {
1702 default:
1703 break;
1704 case QEvent::Show:
1705 if (!progressBars.contains(pb))
1706 startAnimate(AquaProgressBar, pb);
1707 break;
1708 case QEvent::Destroy:
1709 case QEvent::Hide:
1710 progressBars.removeAll(pb);
1711 }
1712 } else if (QPushButton *btn = qobject_cast<QPushButton *>(o)) {
1713 switch (e->type()) {
1714 default:
1715 break;
1716 case QEvent::FocusIn:
1717 if (btn->autoDefault())
1718 startAnimate(AquaPushButton, btn);
1719 break;
1720 case QEvent::Destroy:
1721 case QEvent::Hide:
1722 if (btn == defaultButton)
1723 stopAnimate(AquaPushButton, btn);
1724 break;
1725 case QEvent::MouseButtonPress:
1726 // It is very confusing to keep the button pulsing, so just stop the animation.
1727 if (static_cast<QMouseEvent *>(e)->button() == Qt::LeftButton)
1728 mouseDown = true;
1729 stopAnimate(AquaPushButton, btn);
1730 break;
1731 case QEvent::MouseButtonRelease:
1732 if (static_cast<QMouseEvent *>(e)->button() == Qt::LeftButton)
1733 mouseDown = false;
1734 // fall through
1735 case QEvent::FocusOut:
1736 case QEvent::Show:
1737 case QEvent::WindowActivate: {
1738 QList<QPushButton *> list = qFindChildren<QPushButton *>(btn->window());
1739 for (int i = 0; i < list.size(); ++i) {
1740 QPushButton *pBtn = list.at(i);
1741 if ((e->type() == QEvent::FocusOut
1742 && (pBtn->isDefault() || (pBtn->autoDefault() && pBtn->hasFocus()))
1743 && pBtn != btn)
1744 || ((e->type() == QEvent::Show || e->type() == QEvent::MouseButtonRelease
1745 || e->type() == QEvent::WindowActivate)
1746 && pBtn->isDefault())) {
1747 if (pBtn->window()->isActiveWindow()) {
1748 startAnimate(AquaPushButton, pBtn);
1749 }
1750 break;
1751 }
1752 }
1753 break; }
1754 }
1755 }
1756 return false;
1757}
1758
1759bool QMacStylePrivate::doAnimate(QMacStylePrivate::Animates as)
1760{
1761 if (as == AquaPushButton) {
1762 } else if (as == AquaProgressBar) {
1763 // something for later...
1764 } else if (as == AquaListViewItemOpen) {
1765 // To be revived later...
1766 }
1767 return true;
1768}
1769
1770void QMacStylePrivate::drawColorlessButton(const HIRect &macRect, HIThemeButtonDrawInfo *bdi,
1771 QPainter *p, const QStyleOption *opt) const
1772{
1773 int xoff = 0,
1774 yoff = 0,
1775 extraWidth = 0,
1776 extraHeight = 0,
1777 finalyoff = 0;
1778
1779 const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt);
1780 int width = int(macRect.size.width) + extraWidth;
1781 int height = int(macRect.size.height) + extraHeight;
1782
1783 if (width <= 0 || height <= 0)
1784 return; // nothing to draw
1785
1786 QString key = QLatin1String("$qt_mac_style_ctb_") + QString::number(bdi->kind) + QLatin1Char('_')
1787 + QString::number(bdi->value) + QLatin1Char('_') + QString::number(width)
1788 + QLatin1Char('_') + QString::number(height);
1789 QPixmap pm;
1790 if (!QPixmapCache::find(key, pm)) {
1791 QPixmap activePixmap(width, height);
1792 activePixmap.fill(Qt::transparent);
1793 {
1794 if (combo){
1795 // Carbon combos don't scale. Therefore we draw it
1796 // ourselves, if a scaled version is needed.
1797 QPainter tmpPainter(&activePixmap);
1798 QMacStylePrivate::drawCombobox(macRect, *bdi, &tmpPainter);
1799 }
1800 else {
1801 QMacCGContext cg(&activePixmap);
1802 HIRect newRect = CGRectMake(xoff, yoff, macRect.size.width, macRect.size.height);
1803 HIThemeDrawButton(&newRect, bdi, cg, kHIThemeOrientationNormal, 0);
1804 }
1805 }
1806
1807 if (!combo && bdi->value == kThemeButtonOff) {
1808 pm = activePixmap;
1809 } else if (combo) {
1810 QImage image = activePixmap.toImage();
1811
1812 for (int y = 0; y < height; ++y) {
1813 QRgb *scanLine = reinterpret_cast<QRgb *>(image.scanLine(y));
1814
1815 for (int x = 0; x < width; ++x) {
1816 QRgb &pixel = scanLine[x];
1817
1818 int darkest = qRed(pixel);
1819 int mid = qGreen(pixel);
1820 int lightest = qBlue(pixel);
1821
1822 if (darkest > mid)
1823 qSwap(darkest, mid);
1824 if (mid > lightest)
1825 qSwap(mid, lightest);
1826 if (darkest > mid)
1827 qSwap(darkest, mid);
1828
1829 int gray = (mid + 2 * lightest) / 3;
1830 pixel = qRgba(gray, gray, gray, qAlpha(pixel));
1831 }
1832 }
1833 pm = QPixmap::fromImage(image);
1834 } else {
1835 QImage activeImage = activePixmap.toImage();
1836 QImage colorlessImage;
1837 {
1838 QPixmap colorlessPixmap(width, height);
1839 colorlessPixmap.fill(Qt::transparent);
1840
1841 QMacCGContext cg(&colorlessPixmap);
1842 HIRect newRect = CGRectMake(xoff, yoff, macRect.size.width, macRect.size.height);
1843 int oldValue = bdi->value;
1844 bdi->value = kThemeButtonOff;
1845 HIThemeDrawButton(&newRect, bdi, cg, kHIThemeOrientationNormal, 0);
1846 bdi->value = oldValue;
1847 colorlessImage = colorlessPixmap.toImage();
1848 }
1849
1850 for (int y = 0; y < height; ++y) {
1851 QRgb *colorlessScanLine = reinterpret_cast<QRgb *>(colorlessImage.scanLine(y));
1852 const QRgb *activeScanLine = reinterpret_cast<const QRgb *>(activeImage.scanLine(y));
1853
1854 for (int x = 0; x < width; ++x) {
1855 QRgb &colorlessPixel = colorlessScanLine[x];
1856 QRgb activePixel = activeScanLine[x];
1857
1858 if (activePixel != colorlessPixel) {
1859 int max = qMax(qMax(qRed(activePixel), qGreen(activePixel)),
1860 qBlue(activePixel));
1861 QRgb newPixel = qRgba(max, max, max, qAlpha(activePixel));
1862 if (qGray(newPixel) < qGray(colorlessPixel)
1863 || qAlpha(newPixel) > qAlpha(colorlessPixel))
1864 colorlessPixel = newPixel;
1865 }
1866 }
1867 }
1868 pm = QPixmap::fromImage(colorlessImage);
1869 }
1870 QPixmapCache::insert(key, pm);
1871 }
1872 p->drawPixmap(int(macRect.origin.x), int(macRect.origin.y) + finalyoff, width, height, pm);
1873}
1874
1875QMacStyle::QMacStyle()
1876 : QWindowsStyle()
1877{
1878 d = new QMacStylePrivate(this);
1879}
1880
1881QMacStyle::~QMacStyle()
1882{
1883 delete qt_mac_backgroundPattern;
1884 qt_mac_backgroundPattern = 0;
1885 delete d;
1886}
1887
1888/*! \internal
1889 Generates the standard widget background pattern.
1890*/
1891QPixmap QMacStylePrivate::generateBackgroundPattern() const
1892{
1893 QPixmap px(4, 4);
1894 QMacCGContext cg(&px);
1895 HIThemeSetFill(kThemeBrushDialogBackgroundActive, 0, cg, kHIThemeOrientationNormal);
1896 const CGRect cgRect = CGRectMake(0, 0, px.width(), px.height());
1897 CGContextFillRect(cg, cgRect);
1898 return px;
1899}
1900
1901/*! \internal
1902 Fills the given \a rect with the pattern stored in \a brush. As an optimization,
1903 HIThemeSetFill us used directly if we are filling with the standard background.
1904*/
1905void qt_mac_fill_background(QPainter *painter, const QRegion &rgn, const QBrush &brush)
1906{
1907 QPoint dummy;
1908 const QPaintDevice *target = painter->device();
1909 const QPaintDevice *redirected = QPainter::redirected(target, &dummy);
1910 const bool usePainter = redirected && redirected != target;
1911
1912 if (!usePainter && qt_mac_backgroundPattern
1913 && qt_mac_backgroundPattern->cacheKey() == brush.texture().cacheKey()) {
1914
1915 painter->setClipRegion(rgn);
1916
1917 CGContextRef cg = qt_mac_cg_context(target);
1918 CGContextSaveGState(cg);
1919 HIThemeSetFill(kThemeBrushDialogBackgroundActive, 0, cg, kHIThemeOrientationInverted);
1920
1921 const QVector<QRect> &rects = rgn.rects();
1922 for (int i = 0; i < rects.size(); ++i) {
1923 const QRect rect(rects.at(i));
1924 // Anchor the pattern to the top so it stays put when the window is resized.
1925 CGContextSetPatternPhase(cg, CGSizeMake(rect.width(), rect.height()));
1926 CGRect mac_rect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
1927 CGContextFillRect(cg, mac_rect);
1928 }
1929
1930 CGContextRestoreGState(cg);
1931 } else {
1932 const QRect rect(rgn.boundingRect());
1933 painter->setClipRegion(rgn);
1934 painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft());
1935 }
1936}
1937
1938void QMacStyle::polish(QPalette &pal)
1939{
1940 if (!qt_mac_backgroundPattern) {
1941 if (!qApp)
1942 return;
1943 qt_mac_backgroundPattern = new QPixmap(d->generateBackgroundPattern());
1944 }
1945
1946 QColor pc(Qt::black);
1947 pc = qcolorForTheme(kThemeBrushDialogBackgroundActive);
1948 QBrush background(pc, *qt_mac_backgroundPattern);
1949 pal.setBrush(QPalette::All, QPalette::Window, background);
1950 pal.setBrush(QPalette::All, QPalette::Button, background);
1951
1952 QCFString theme;
1953 const OSErr err = CopyThemeIdentifier(&theme);
1954 if (err == noErr && CFStringCompare(theme, kThemeAppearanceAquaGraphite, 0) == kCFCompareEqualTo) {
1955 pal.setBrush(QPalette::All, QPalette::AlternateBase, QColor(240, 240, 240));
1956 } else {
1957 pal.setBrush(QPalette::All, QPalette::AlternateBase, QColor(237, 243, 254));
1958 }
1959}
1960
1961void QMacStyle::polish(QApplication *)
1962{
1963}
1964
1965void QMacStyle::unpolish(QApplication *)
1966{
1967}
1968
1969void QMacStyle::polish(QWidget* w)
1970{
1971 d->addWidget(w);
1972 if (qt_mac_is_metal(w) && !w->testAttribute(Qt::WA_SetPalette)) {
1973 // Set a clear brush so that the metal shines through.
1974 QPalette pal = w->palette();
1975 QBrush background(Qt::transparent);
1976 pal.setBrush(QPalette::All, QPalette::Window, background);
1977 pal.setBrush(QPalette::All, QPalette::Button, background);
1978 w->setPalette(pal);
1979 w->setAttribute(Qt::WA_SetPalette, false);
1980 }
1981
1982 if (qobject_cast<QMenu*>(w) || qobject_cast<QComboBoxPrivateContainer *>(w)) {
1983 w->setWindowOpacity(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5 ? 0.985 : 0.94);
1984 if (!w->testAttribute(Qt::WA_SetPalette)) {
1985 QPixmap px(64, 64);
1986 px.fill(Qt::white);
1987 HIThemeMenuDrawInfo mtinfo;
1988 mtinfo.version = qt_mac_hitheme_version;
1989 mtinfo.menuType = kThemeMenuTypePopUp;
1990 HIRect rect = CGRectMake(0, 0, px.width(), px.height());
1991 HIThemeDrawMenuBackground(&rect, &mtinfo, QCFType<CGContextRef>(qt_mac_cg_context(&px)),
1992 kHIThemeOrientationNormal);
1993 QPalette pal = w->palette();
1994 QBrush background(px);
1995 pal.setBrush(QPalette::All, QPalette::Window, background);
1996 pal.setBrush(QPalette::All, QPalette::Button, background);
1997 w->setPalette(pal);
1998 w->setAttribute(Qt::WA_SetPalette, false);
1999 }
2000 }
2001
2002 if (QTabBar *tb = qobject_cast<QTabBar*>(w)) {
2003 if (tb->documentMode()) {
2004 w->setAttribute(Qt::WA_Hover);
2005 w->setFont(qt_app_fonts_hash()->value("QSmallFont", QFont()));
2006 QPalette p = w->palette();
2007 p.setColor(QPalette::WindowText, QColor(17, 17, 17));
2008 w->setPalette(p);
2009 }
2010 }
2011
2012 QWindowsStyle::polish(w);
2013
2014 if (QRubberBand *rubber = qobject_cast<QRubberBand*>(w)) {
2015 rubber->setWindowOpacity(0.25);
2016 rubber->setAttribute(Qt::WA_PaintOnScreen, false);
2017 rubber->setAttribute(Qt::WA_NoSystemBackground, false);
2018 }
2019}
2020
2021void QMacStyle::unpolish(QWidget* w)
2022{
2023 d->removeWidget(w);
2024 if ((qobject_cast<QMenu*>(w) || qt_mac_is_metal(w)) && !w->testAttribute(Qt::WA_SetPalette)) {
2025 QPalette pal = qApp->palette(w);
2026 w->setPalette(pal);
2027 w->setAttribute(Qt::WA_SetPalette, false);
2028 w->setWindowOpacity(1.0);
2029 }
2030
2031 if (QComboBox *combo = qobject_cast<QComboBox *>(w)) {
2032 if (!combo->isEditable()) {
2033 if (QWidget *widget = combo->findChild<QComboBoxPrivateContainer *>())
2034 widget->setWindowOpacity(1.0);
2035 }
2036 }
2037
2038 if (QRubberBand *rubber = ::qobject_cast<QRubberBand*>(w)) {
2039 rubber->setWindowOpacity(1.0);
2040 rubber->setAttribute(Qt::WA_PaintOnScreen, true);
2041 rubber->setAttribute(Qt::WA_NoSystemBackground, true);
2042 }
2043
2044 if (QFocusFrame *frame = qobject_cast<QFocusFrame *>(w)) {
2045 frame->setAttribute(Qt::WA_NoSystemBackground, true);
2046 frame->setAutoFillBackground(true);
2047 }
2048 QWindowsStyle::unpolish(w);
2049}
2050
2051int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QWidget *widget) const
2052{
2053 int controlSize = getControlSize(opt, widget);
2054 SInt32 ret = 0;
2055
2056 switch (metric) {
2057 case PM_TabCloseIndicatorWidth:
2058 case PM_TabCloseIndicatorHeight:
2059 ret = closeButtonSize;
2060 break;
2061 case PM_ToolBarIconSize:
2062 ret = proxy()->pixelMetric(PM_LargeIconSize);
2063 break;
2064 case PM_FocusFrameVMargin:
2065 case PM_FocusFrameHMargin:
2066 GetThemeMetric(kThemeMetricFocusRectOutset, &ret);
2067 break;
2068 case PM_DialogButtonsSeparator:
2069 ret = -5;
2070 break;
2071 case PM_DialogButtonsButtonHeight: {
2072 QSize sz;
2073 ret = d->aquaSizeConstrain(opt, 0, QStyle::CT_PushButton, QSize(-1, -1), &sz);
2074 if (sz == QSize(-1, -1))
2075 ret = 32;
2076 else
2077 ret = sz.height();
2078 break; }
2079 case PM_CheckListButtonSize: {
2080 switch (d->aquaSizeConstrain(opt, widget)) {
2081 case QAquaSizeUnknown:
2082 case QAquaSizeLarge:
2083 GetThemeMetric(kThemeMetricCheckBoxWidth, &ret);
2084 break;
2085 case QAquaSizeMini:
2086 GetThemeMetric(kThemeMetricMiniCheckBoxWidth, &ret);
2087 break;
2088 case QAquaSizeSmall:
2089 GetThemeMetric(kThemeMetricSmallCheckBoxWidth, &ret);
2090 break;
2091 }
2092 break; }
2093 case PM_DialogButtonsButtonWidth: {
2094 QSize sz;
2095 ret = d->aquaSizeConstrain(opt, 0, QStyle::CT_PushButton, QSize(-1, -1), &sz);
2096 if (sz == QSize(-1, -1))
2097 ret = 70;
2098 else
2099 ret = sz.width();
2100 break; }
2101
2102 case PM_MenuBarHMargin:
2103 ret = 8;
2104 break;
2105
2106 case PM_MenuBarVMargin:
2107 ret = 0;
2108 break;
2109
2110 case QStyle::PM_MenuDesktopFrameWidth:
2111 ret = 5;
2112 break;
2113
2114 case PM_CheckBoxLabelSpacing:
2115 case PM_RadioButtonLabelSpacing:
2116 ret = 2;
2117 break;
2118 case PM_MenuScrollerHeight:
2119#if 0
2120 SInt16 ash, asw;
2121 GetThemeMenuItemExtra(kThemeMenuItemScrollUpArrow, &ash, &asw);
2122 ret = ash;
2123#else
2124 ret = 15; // I hate having magic numbers in here...
2125#endif
2126 break;
2127 case PM_DefaultFrameWidth:
2128#ifndef QT_NO_MAINWINDOW
2129 if (widget && (widget->isWindow() || !widget->parentWidget()
2130 || (qobject_cast<const QMainWindow*>(widget->parentWidget())
2131 && static_cast<QMainWindow *>(widget->parentWidget())->centralWidget() == widget))
2132 && (qobject_cast<const QAbstractScrollArea *>(widget)
2133#ifdef QT3_SUPPORT
2134 || widget->inherits("QScrollView")
2135#endif
2136 || widget->inherits("QWorkspaceChild")))
2137 ret = 0;
2138 else
2139#endif
2140 // The combo box popup has no frame.
2141 if (qstyleoption_cast<const QStyleOptionComboBox *>(opt) != 0)
2142 ret = 0;
2143 else
2144 ret = QWindowsStyle::pixelMetric(metric, opt, widget);
2145 break;
2146 case PM_MaximumDragDistance:
2147 ret = -1;
2148 break;
2149 case PM_ScrollBarSliderMin:
2150 ret = 24;
2151 break;
2152 case PM_SpinBoxFrameWidth:
2153 GetThemeMetric(kThemeMetricEditTextFrameOutset, &ret);
2154 switch (d->aquaSizeConstrain(opt, widget)) {
2155 default:
2156 ret += 2;
2157 break;
2158 case QAquaSizeMini:
2159 ret += 1;
2160 break;
2161 }
2162 break;
2163 case PM_ButtonShiftHorizontal:
2164 case PM_ButtonShiftVertical:
2165 ret = 0;
2166 break;
2167 case PM_SliderLength:
2168 ret = 17;
2169 break;
2170 case PM_ButtonDefaultIndicator:
2171 ret = 0;
2172 break;
2173 case PM_TitleBarHeight:
2174 if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
2175 HIThemeWindowDrawInfo wdi;
2176 wdi.version = qt_mac_hitheme_version;
2177 wdi.state = kThemeStateActive;
2178 wdi.windowType = QtWinType;
2179 if (tb->titleBarState)
2180 wdi.attributes = kThemeWindowHasFullZoom | kThemeWindowHasCloseBox
2181 | kThemeWindowHasCollapseBox;
2182 else if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
2183 wdi.attributes = kThemeWindowHasCloseBox;
2184 else
2185 wdi.attributes = 0;
2186 wdi.titleHeight = tb->rect.height();
2187 wdi.titleWidth = tb->rect.width();
2188 QCFType<HIShapeRef> region;
2189 HIRect hirect = qt_hirectForQRect(tb->rect);
2190 if (hirect.size.width <= 0)
2191 hirect.size.width = 100;
2192 if (hirect.size.height <= 0)
2193 hirect.size.height = 30;
2194
2195 HIThemeGetWindowShape(&hirect, &wdi, kWindowTitleBarRgn, &region);
2196 HIRect rect;
2197 ptrHIShapeGetBounds(region, &rect);
2198 ret = int(rect.size.height);
2199 ret += 4;
2200 }
2201 break;
2202 case PM_TabBarTabVSpace:
2203 ret = 4;
2204 break;
2205 case PM_TabBarTabShiftHorizontal:
2206 case PM_TabBarTabShiftVertical:
2207 ret = 0;
2208 break;
2209 case PM_TabBarBaseHeight:
2210 ret = 0;
2211 break;
2212 case PM_TabBarTabOverlap:
2213 ret = 0;
2214 break;
2215 case PM_TabBarBaseOverlap:
2216 switch (d->aquaSizeConstrain(opt, widget)) {
2217 case QAquaSizeUnknown:
2218 case QAquaSizeLarge:
2219 ret = 11;
2220 break;
2221 case QAquaSizeSmall:
2222 ret = 8;
2223 break;
2224 case QAquaSizeMini:
2225 ret = 7;
2226 break;
2227 }
2228 break;
2229 case PM_ScrollBarExtent: {
2230 switch (d->aquaSizeConstrain(opt, widget)) {
2231 case QAquaSizeUnknown:
2232 case QAquaSizeLarge:
2233 GetThemeMetric(kThemeMetricScrollBarWidth, &ret);
2234 break;
2235 case QAquaSizeMini:
2236 case QAquaSizeSmall:
2237 GetThemeMetric(kThemeMetricSmallScrollBarWidth, &ret);
2238 break;
2239 }
2240 break; }
2241 case PM_IndicatorHeight: {
2242 switch (d->aquaSizeConstrain(opt, widget)) {
2243 case QAquaSizeUnknown:
2244 case QAquaSizeLarge:
2245 GetThemeMetric(kThemeMetricCheckBoxHeight, &ret);
2246 break;
2247 case QAquaSizeMini:
2248 GetThemeMetric(kThemeMetricMiniCheckBoxHeight, &ret);
2249 break;
2250 case QAquaSizeSmall:
2251 GetThemeMetric(kThemeMetricSmallCheckBoxHeight, &ret);
2252 break;
2253 }
2254 break; }
2255 case PM_IndicatorWidth: {
2256 switch (d->aquaSizeConstrain(opt, widget)) {
2257 case QAquaSizeUnknown:
2258 case QAquaSizeLarge:
2259 GetThemeMetric(kThemeMetricCheckBoxWidth, &ret);
2260 break;
2261 case QAquaSizeMini:
2262 GetThemeMetric(kThemeMetricMiniCheckBoxWidth, &ret);
2263 break;
2264 case QAquaSizeSmall:
2265 GetThemeMetric(kThemeMetricSmallCheckBoxWidth, &ret);
2266 break;
2267 }
2268 ++ret;
2269 break; }
2270 case PM_ExclusiveIndicatorHeight: {
2271 switch (d->aquaSizeConstrain(opt, widget)) {
2272 case QAquaSizeUnknown:
2273 case QAquaSizeLarge:
2274 GetThemeMetric(kThemeMetricRadioButtonHeight, &ret);
2275 break;
2276 case QAquaSizeMini:
2277 GetThemeMetric(kThemeMetricMiniRadioButtonHeight, &ret);
2278 break;
2279 case QAquaSizeSmall:
2280 GetThemeMetric(kThemeMetricSmallRadioButtonHeight, &ret);
2281 break;
2282 }
2283 break; }
2284 case PM_ExclusiveIndicatorWidth: {
2285 switch (d->aquaSizeConstrain(opt, widget)) {
2286 case QAquaSizeUnknown:
2287 case QAquaSizeLarge:
2288 GetThemeMetric(kThemeMetricRadioButtonWidth, &ret);
2289 break;
2290 case QAquaSizeMini:
2291 GetThemeMetric(kThemeMetricMiniRadioButtonWidth, &ret);
2292 break;
2293 case QAquaSizeSmall:
2294 GetThemeMetric(kThemeMetricSmallRadioButtonWidth, &ret);
2295 break;
2296 }
2297 ++ret;
2298 break; }
2299 case PM_MenuVMargin:
2300 ret = 4;
2301 break;
2302 case PM_MenuPanelWidth:
2303 ret = 0;
2304 break;
2305 case PM_ToolTipLabelFrameWidth:
2306 ret = 0;
2307 break;
2308 case PM_SizeGripSize: {
2309 QAquaWidgetSize aSize;
2310 if (widget && widget->window()->windowType() == Qt::Tool)
2311 aSize = QAquaSizeSmall;
2312 else
2313 aSize = QAquaSizeLarge;
2314 const QSize size = qt_aqua_get_known_size(CT_SizeGrip, widget, QSize(), aSize);
2315 ret = size.width();
2316 break; }
2317 case PM_MdiSubWindowFrameWidth:
2318 ret = 1;
2319 break;
2320 case PM_DockWidgetFrameWidth:
2321 ret = 2;
2322 break;
2323 case PM_DockWidgetTitleMargin:
2324 ret = 0;
2325 break;
2326 case PM_DockWidgetSeparatorExtent:
2327 ret = 1;
2328 break;
2329 case PM_ToolBarHandleExtent:
2330 ret = 11;
2331 break;
2332 case PM_ToolBarItemMargin:
2333 ret = 0;
2334 break;
2335 case PM_ToolBarItemSpacing:
2336 ret = 4;
2337 break;
2338 case PM_SplitterWidth:
2339 ret = qMax(7, QApplication::globalStrut().width());
2340 break;
2341 case PM_LayoutLeftMargin:
2342 case PM_LayoutTopMargin:
2343 case PM_LayoutRightMargin:
2344 case PM_LayoutBottomMargin:
2345 {
2346 bool isWindow = false;
2347 if (opt) {
2348 isWindow = (opt->state & State_Window);
2349 } else if (widget) {
2350 isWindow = widget->isWindow();
2351 }
2352
2353 if (isWindow) {
2354 bool isMetal = widget && widget->testAttribute(Qt::WA_MacBrushedMetal);
2355 if (isMetal) {
2356 if (metric == PM_LayoutTopMargin) {
2357 return_SIZE(9 /* AHIG */, 6 /* guess */, 6 /* guess */);
2358 } else if (metric == PM_LayoutBottomMargin) {
2359 return_SIZE(18 /* AHIG */, 15 /* guess */, 13 /* guess */);
2360 } else {
2361 return_SIZE(14 /* AHIG */, 11 /* guess */, 9 /* guess */);
2362 }
2363 } else {
2364 /*
2365 AHIG would have (20, 8, 10) here but that makes
2366 no sense. It would also have 14 for the top margin
2367 but this contradicts both Builder and most
2368 applications.
2369 */
2370 return_SIZE(20, 10, 10); // AHIG
2371 }
2372 } else {
2373 // hack to detect QTabWidget
2374 if (widget && widget->parentWidget()
2375 && widget->parentWidget()->sizePolicy().controlType() == QSizePolicy::TabWidget) {
2376 if (metric == PM_LayoutTopMargin) {
2377 /*
2378 Builder would have 14 (= 20 - 6) instead of 12,
2379 but that makes the tab look disproportionate.
2380 */
2381 return_SIZE(12, 6, 6); // guess
2382 } else {
2383 return_SIZE(20 /* Builder */, 8 /* guess */, 8 /* guess */);
2384 }
2385 } else {
2386 /*
2387 Child margins are highly inconsistent in AHIG and Builder.
2388 */
2389 return_SIZE(12, 8, 6); // guess
2390 }
2391 }
2392 }
2393 case PM_LayoutHorizontalSpacing:
2394 case PM_LayoutVerticalSpacing:
2395 return -1;
2396 case QStyle::PM_TabBarTabHSpace:
2397 switch (d->aquaSizeConstrain(opt, widget)) {
2398 case QAquaSizeLarge:
2399 case QAquaSizeUnknown:
2400 ret = QWindowsStyle::pixelMetric(metric, opt, widget);
2401 break;
2402 case QAquaSizeSmall:
2403 ret = 20;
2404 break;
2405 case QAquaSizeMini:
2406 ret = 16;
2407 break;
2408 }
2409 break;
2410 case PM_MenuHMargin:
2411 ret = 0;
2412 break;
2413 case PM_ToolBarFrameWidth:
2414 ret = 1;
2415 if (widget) {
2416 if (QMainWindow * mainWindow = qobject_cast<QMainWindow *>(widget->parent()))
2417 if (mainWindow->unifiedTitleAndToolBarOnMac())
2418 ret = 0;
2419 }
2420 break;
2421 default:
2422 ret = QWindowsStyle::pixelMetric(metric, opt, widget);
2423 break;
2424 }
2425 return ret;
2426}
2427
2428QPalette QMacStyle::standardPalette() const
2429{
2430 QPalette pal = QWindowsStyle::standardPalette();
2431 pal.setColor(QPalette::Disabled, QPalette::Dark, QColor(191, 191, 191));
2432 pal.setColor(QPalette::Active, QPalette::Dark, QColor(191, 191, 191));
2433 pal.setColor(QPalette::Inactive, QPalette::Dark, QColor(191, 191, 191));
2434 return pal;
2435}
2436
2437int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w,
2438 QStyleHintReturn *hret) const
2439{
2440 SInt32 ret = 0;
2441 switch (sh) {
2442 case SH_Menu_SelectionWrap:
2443 ret = false;
2444 break;
2445 case SH_Menu_KeyboardSearch:
2446 ret = true;
2447 break;
2448 case SH_Menu_SpaceActivatesItem:
2449 ret = true;
2450 break;
2451 case SH_Slider_AbsoluteSetButtons:
2452 ret = Qt::LeftButton|Qt::MidButton;
2453 break;
2454 case SH_Slider_PageSetButtons:
2455 ret = 0;
2456 break;
2457 case SH_ScrollBar_ContextMenu:
2458 ret = false;
2459 break;
2460 case SH_TitleBar_AutoRaise:
2461 ret = true;
2462 break;
2463 case SH_Menu_AllowActiveAndDisabled:
2464 ret = false;
2465 break;
2466 case SH_Menu_SubMenuPopupDelay:
2467 ret = 100;
2468 break;
2469 case SH_ScrollBar_LeftClickAbsolutePosition: {
2470 extern bool qt_scrollbar_jump_to_pos; //qapplication_mac.cpp
2471 if(QApplication::keyboardModifiers() & Qt::AltModifier)
2472 ret = !qt_scrollbar_jump_to_pos;
2473 else
2474 ret = qt_scrollbar_jump_to_pos;
2475 break; }
2476 case SH_TabBar_PreferNoArrows:
2477 ret = true;
2478 break;
2479 case SH_LineEdit_PasswordCharacter:
2480 ret = kBulletUnicode;
2481 break;
2482 /*
2483 case SH_DialogButtons_DefaultButton:
2484 ret = QDialogButtons::Reject;
2485 break;
2486 */
2487 case SH_Menu_SloppySubMenus:
2488 ret = true;
2489 break;
2490 case SH_GroupBox_TextLabelVerticalAlignment:
2491 ret = Qt::AlignTop;
2492 break;
2493 case SH_ScrollView_FrameOnlyAroundContents:
2494 if (w && (w->isWindow() || !w->parentWidget() || w->parentWidget()->isWindow())
2495 && (w->inherits("QWorkspaceChild")
2496#ifdef QT3_SUPPORT
2497 || w->inherits("QScrollView")
2498#endif
2499 ))
2500 ret = true;
2501 else
2502 ret = QWindowsStyle::styleHint(sh, opt, w, hret);
2503 break;
2504 case SH_Menu_FillScreenWithScroll:
2505 ret = false;
2506 break;
2507 case SH_Menu_Scrollable:
2508 ret = true;
2509 break;
2510 case SH_RichText_FullWidthSelection:
2511 ret = true;
2512 break;
2513 case SH_BlinkCursorWhenTextSelected:
2514 ret = false;
2515 break;
2516 case SH_ScrollBar_StopMouseOverSlider:
2517 ret = true;
2518 break;
2519 case SH_Q3ListViewExpand_SelectMouseType:
2520 ret = QEvent::MouseButtonRelease;
2521 break;
2522 case SH_TabBar_SelectMouseType:
2523 if (const QStyleOptionTabBarBaseV2 *opt2 = qstyleoption_cast<const QStyleOptionTabBarBaseV2 *>(opt)) {
2524 ret = opt2->documentMode ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease;
2525 } else {
2526 ret = QEvent::MouseButtonRelease;
2527 }
2528 break;
2529 case SH_ComboBox_Popup:
2530 if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt))
2531 ret = !cmb->editable;
2532 else
2533 ret = 0;
2534 break;
2535 case SH_Workspace_FillSpaceOnMaximize:
2536 ret = true;
2537 break;
2538 case SH_Widget_ShareActivation:
2539 ret = true;
2540 break;
2541 case SH_Header_ArrowAlignment:
2542 ret = Qt::AlignRight;
2543 break;
2544 case SH_TabBar_Alignment: {
2545 if (const QTabWidget *tab = qobject_cast<const QTabWidget*>(w)) {
2546 if (tab->documentMode()) {
2547 ret = Qt::AlignLeft;
2548 break;
2549 }
2550 }
2551 if (const QTabBar *tab = qobject_cast<const QTabBar*>(w)) {
2552 if (tab->documentMode()) {
2553 ret = Qt::AlignLeft;
2554 break;
2555 }
2556 }
2557 ret = Qt::AlignCenter;
2558 } break;
2559 case SH_UnderlineShortcut:
2560 ret = false;
2561 break;
2562 case SH_ToolTipLabel_Opacity:
2563 ret = 242; // About 95%
2564 break;
2565 case SH_Button_FocusPolicy:
2566 ret = Qt::TabFocus;
2567 break;
2568 case SH_EtchDisabledText:
2569 ret = false;
2570 break;
2571 case SH_FocusFrame_Mask: {
2572 ret = true;
2573 if(QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) {
2574 const uchar fillR = 192, fillG = 191, fillB = 190;
2575 QImage img;
2576
2577 QSize pixmapSize = opt->rect.size();
2578 if (pixmapSize.isValid()) {
2579 QPixmap pix(pixmapSize);
2580 pix.fill(QColor(fillR, fillG, fillB));
2581 QPainter pix_paint(&pix);
2582 proxy()->drawControl(CE_FocusFrame, opt, &pix_paint, w);
2583 pix_paint.end();
2584 img = pix.toImage();
2585 }
2586
2587 const QRgb *sptr = (QRgb*)img.bits(), *srow;
2588 const int sbpl = img.bytesPerLine();
2589 const int w = sbpl/4, h = img.height();
2590
2591 QImage img_mask(img.width(), img.height(), QImage::Format_ARGB32);
2592 QRgb *dptr = (QRgb*)img_mask.bits(), *drow;
2593 const int dbpl = img_mask.bytesPerLine();
2594
2595 for (int y = 0; y < h; ++y) {
2596 srow = sptr+((y*sbpl)/4);
2597 drow = dptr+((y*dbpl)/4);
2598 for (int x = 0; x < w; ++x) {
2599 const int diff = (((qRed(*srow)-fillR)*(qRed(*srow)-fillR)) +
2600 ((qGreen(*srow)-fillG)*((qGreen(*srow)-fillG))) +
2601 ((qBlue(*srow)-fillB)*((qBlue(*srow)-fillB))));
2602 (*drow++) = (diff < 100) ? 0xffffffff : 0xff000000;
2603 ++srow;
2604 }
2605 }
2606 QBitmap qmask = QBitmap::fromImage(img_mask);
2607 mask->region = QRegion(qmask);
2608 }
2609 break; }
2610 case SH_TitleBar_NoBorder:
2611 ret = 1;
2612 break;
2613 case SH_RubberBand_Mask:
2614 ret = 0;
2615 break;
2616 case SH_ComboBox_LayoutDirection:
2617 ret = Qt::LeftToRight;
2618 break;
2619 case SH_ItemView_EllipsisLocation:
2620 ret = Qt::AlignHCenter;
2621 break;
2622 case SH_ItemView_ShowDecorationSelected:
2623 ret = true;
2624 break;
2625 case SH_TitleBar_ModifyNotification:
2626 ret = false;
2627 break;
2628 case SH_ScrollBar_RollBetweenButtons:
2629 ret = true;
2630 break;
2631 case SH_WindowFrame_Mask:
2632 ret = 1;
2633 if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(hret)) {
2634 mask->region = opt->rect;
2635 mask->region -= QRect(opt->rect.left(), opt->rect.top(), 5, 1);
2636 mask->region -= QRect(opt->rect.left(), opt->rect.top() + 1, 3, 1);
2637 mask->region -= QRect(opt->rect.left(), opt->rect.top() + 2, 2, 1);
2638 mask->region -= QRect(opt->rect.left(), opt->rect.top() + 3, 1, 2);
2639
2640 mask->region -= QRect(opt->rect.right() - 4, opt->rect.top(), 5, 1);
2641 mask->region -= QRect(opt->rect.right() - 2, opt->rect.top() + 1, 3, 1);
2642 mask->region -= QRect(opt->rect.right() - 1, opt->rect.top() + 2, 2, 1);
2643 mask->region -= QRect(opt->rect.right() , opt->rect.top() + 3, 1, 2);
2644 }
2645 break;
2646 case SH_TabBar_ElideMode:
2647 ret = Qt::ElideRight;
2648 break;
2649 case SH_DialogButtonLayout:
2650 ret = QDialogButtonBox::MacLayout;
2651 break;
2652 case SH_FormLayoutWrapPolicy:
2653 ret = QFormLayout::DontWrapRows;
2654 break;
2655 case SH_FormLayoutFieldGrowthPolicy:
2656 ret = QFormLayout::FieldsStayAtSizeHint;
2657 break;
2658 case SH_FormLayoutFormAlignment:
2659 ret = Qt::AlignHCenter | Qt::AlignTop;
2660 break;
2661 case SH_FormLayoutLabelAlignment:
2662 ret = Qt::AlignRight;
2663 break;
2664 case SH_ComboBox_PopupFrameStyle:
2665 ret = QFrame::NoFrame | QFrame::Plain;
2666 break;
2667 case SH_MessageBox_TextInteractionFlags:
2668 ret = Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard;
2669 break;
2670 case SH_SpellCheckUnderlineStyle:
2671 ret = QTextCharFormat::DashUnderline;
2672 break;
2673 case SH_MessageBox_CenterButtons:
2674 ret = false;
2675 break;
2676 case SH_MenuBar_AltKeyNavigation:
2677 ret = false;
2678 break;
2679 case SH_ItemView_MovementWithoutUpdatingSelection:
2680 ret = false;
2681 break;
2682 case SH_FocusFrame_AboveWidget:
2683 ret = true;
2684 break;
2685 case SH_WizardStyle:
2686 ret = QWizard::MacStyle;
2687 break;
2688 case SH_ItemView_ArrowKeysNavigateIntoChildren:
2689 ret = false;
2690 break;
2691 case SH_Menu_FlashTriggeredItem:
2692 ret = true;
2693 break;
2694 case SH_Menu_FadeOutOnHide:
2695 ret = true;
2696 break;
2697 case SH_Menu_Mask:
2698 if (opt) {
2699 if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) {
2700 ret = true;
2701 HIRect menuRect = CGRectMake(opt->rect.x(), opt->rect.y() + 4,
2702 opt->rect.width(), opt->rect.height() - 8);
2703 HIThemeMenuDrawInfo mdi;
2704 mdi.version = 0;
2705 if (w && qobject_cast<QMenu *>(w->parentWidget()))
2706 mdi.menuType = kThemeMenuTypeHierarchical;
2707 else
2708 mdi.menuType = kThemeMenuTypePopUp;
2709 QCFType<HIShapeRef> shape;
2710 HIThemeGetMenuBackgroundShape(&menuRect, &mdi, &shape);
2711 mask->region = QRegion::fromHIShapeRef(shape);
2712 }
2713 }
2714 break;
2715 case SH_ItemView_PaintAlternatingRowColorsForEmptyArea:
2716 ret = true;
2717 break;
2718 case SH_TabBar_CloseButtonPosition:
2719 ret = QTabBar::LeftSide;
2720 break;
2721 case SH_DockWidget_ButtonsHaveFrame:
2722 ret = false;
2723 break;
2724 default:
2725 ret = QWindowsStyle::styleHint(sh, opt, w, hret);
2726 break;
2727 }
2728 return ret;
2729}
2730
2731QPixmap QMacStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
2732 const QStyleOption *opt) const
2733{
2734 switch (iconMode) {
2735 case QIcon::Disabled: {
2736 QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
2737 int imgh = img.height();
2738 int imgw = img.width();
2739 QRgb pixel;
2740 for (int y = 0; y < imgh; ++y) {
2741 for (int x = 0; x < imgw; ++x) {
2742 pixel = img.pixel(x, y);
2743 img.setPixel(x, y, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel),
2744 qAlpha(pixel) / 2));
2745 }
2746 }
2747 return QPixmap::fromImage(img);
2748 }
2749 default:
2750 ;
2751 }
2752 return QWindowsStyle::generatedIconPixmap(iconMode, pixmap, opt);
2753}
2754
2755
2756QPixmap QMacStyle::standardPixmap(StandardPixmap standardPixmap, const QStyleOption *opt,
2757 const QWidget *widget) const
2758{
2759 // The default implementation of QStyle::standardIconImplementation() is to call standardPixmap()
2760 // I don't want infinite recursion so if we do get in that situation, just return the Window's
2761 // standard pixmap instead (since there is no mac-specific icon then). This should be fine until
2762 // someone changes how Windows standard
2763 // pixmap works.
2764 static bool recursionGuard = false;
2765
2766 if (recursionGuard)
2767 return QWindowsStyle::standardPixmap(standardPixmap, opt, widget);
2768
2769 recursionGuard = true;
2770 QIcon icon = standardIconImplementation(standardPixmap, opt, widget);
2771 recursionGuard = false;
2772 int size;
2773 switch (standardPixmap) {
2774 default:
2775 size = 32;
2776 break;
2777 case SP_MessageBoxCritical:
2778 case SP_MessageBoxQuestion:
2779 case SP_MessageBoxInformation:
2780 case SP_MessageBoxWarning:
2781 size = 64;
2782 break;
2783 }
2784 return icon.pixmap(size, size);
2785}
2786
2787void QMacStyle::setFocusRectPolicy(QWidget *w, FocusRectPolicy policy)
2788{
2789 switch (policy) {
2790 case FocusDefault:
2791 break;
2792 case FocusEnabled:
2793 case FocusDisabled:
2794 w->setAttribute(Qt::WA_MacShowFocusRect, policy == FocusEnabled);
2795 break;
2796 }
2797}
2798
2799QMacStyle::FocusRectPolicy QMacStyle::focusRectPolicy(const QWidget *w)
2800{
2801 return w->testAttribute(Qt::WA_MacShowFocusRect) ? FocusEnabled : FocusDisabled;
2802}
2803
2804void QMacStyle::setWidgetSizePolicy(const QWidget *widget, WidgetSizePolicy policy)
2805{
2806 QWidget *wadget = const_cast<QWidget *>(widget);
2807 wadget->setAttribute(Qt::WA_MacNormalSize, policy == SizeLarge);
2808 wadget->setAttribute(Qt::WA_MacSmallSize, policy == SizeSmall);
2809 wadget->setAttribute(Qt::WA_MacMiniSize, policy == SizeMini);
2810}
2811
2812QMacStyle::WidgetSizePolicy QMacStyle::widgetSizePolicy(const QWidget *widget)
2813{
2814 while (widget) {
2815 if (widget->testAttribute(Qt::WA_MacMiniSize)) {
2816 return SizeMini;
2817 } else if (widget->testAttribute(Qt::WA_MacSmallSize)) {
2818 return SizeSmall;
2819 } else if (widget->testAttribute(Qt::WA_MacNormalSize)) {
2820 return SizeLarge;
2821 }
2822 widget = widget->parentWidget();
2823 }
2824 return SizeDefault;
2825}
2826
2827void QMacStyle::drawPrimitive(PrimitiveElement pe, const QStyleOption *opt, QPainter *p,
2828 const QWidget *w) const
2829{
2830 ThemeDrawState tds = d->getDrawState(opt->state);
2831 QMacCGContext cg(p);
2832 switch (pe) {
2833 case PE_IndicatorArrowUp:
2834 case PE_IndicatorArrowDown:
2835 case PE_IndicatorArrowRight:
2836 case PE_IndicatorArrowLeft: {
2837 p->save();
2838 p->setRenderHint(QPainter::Antialiasing);
2839 int xOffset = opt->direction == Qt::LeftToRight ? 2 : -1;
2840 QMatrix matrix;
2841 matrix.translate(opt->rect.center().x() + xOffset, opt->rect.center().y() + 2);
2842 QPainterPath path;
2843 switch(pe) {
2844 default:
2845 case PE_IndicatorArrowDown:
2846 break;
2847 case PE_IndicatorArrowUp:
2848 matrix.rotate(180);
2849 break;
2850 case PE_IndicatorArrowLeft:
2851 matrix.rotate(90);
2852 break;
2853 case PE_IndicatorArrowRight:
2854 matrix.rotate(-90);
2855 break;
2856 }
2857 path.moveTo(0, 5);
2858 path.lineTo(-4, -3);
2859 path.lineTo(4, -3);
2860 p->setMatrix(matrix);
2861 p->setPen(Qt::NoPen);
2862 p->setBrush(QColor(0, 0, 0, 135));
2863 p->drawPath(path);
2864 p->restore();
2865 break; }
2866 case PE_FrameTabBarBase:
2867 if (const QStyleOptionTabBarBaseV2 *tbb
2868 = qstyleoption_cast<const QStyleOptionTabBarBaseV2 *>(opt)) {
2869 if (tbb->documentMode) {
2870 p->save();
2871 drawTabBase(p, tbb, w);
2872 p->restore();
2873 return;
2874 }
2875
2876 QRegion region(tbb->rect);
2877 region -= tbb->tabBarRect;
2878 p->save();
2879 p->setClipRegion(region);
2880 QStyleOptionTabWidgetFrame twf;
2881 twf.QStyleOption::operator=(*tbb);
2882 twf.shape = tbb->shape;
2883 switch (getTabDirection(twf.shape)) {
2884 case kThemeTabNorth:
2885 twf.rect = twf.rect.adjusted(0, 0, 0, 10);
2886 break;
2887 case kThemeTabSouth:
2888 twf.rect = twf.rect.adjusted(0, -10, 0, 0);
2889 break;
2890 case kThemeTabWest:
2891 twf.rect = twf.rect.adjusted(0, 0, 10, 0);
2892 break;
2893 case kThemeTabEast:
2894 twf.rect = twf.rect.adjusted(0, -10, 0, 0);
2895 break;
2896 }
2897 proxy()->drawPrimitive(PE_FrameTabWidget, &twf, p, w);
2898 p->restore();
2899 }
2900 break;
2901 case PE_PanelTipLabel:
2902 p->fillRect(opt->rect, opt->palette.brush(QPalette::ToolTipBase));
2903 break;
2904 case PE_FrameGroupBox:
2905 if (const QStyleOptionFrame *groupBox = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
2906 const QStyleOptionFrameV2 *frame2 = qstyleoption_cast<const QStyleOptionFrameV2 *>(opt);
2907 if (frame2 && frame2->features & QStyleOptionFrameV2::Flat) {
2908 QWindowsStyle::drawPrimitive(pe, groupBox, p, w);
2909 } else {
2910 HIThemeGroupBoxDrawInfo gdi;
2911 gdi.version = qt_mac_hitheme_version;
2912 gdi.state = tds;
2913 if (w && qobject_cast<QGroupBox *>(w->parentWidget()))
2914 gdi.kind = kHIThemeGroupBoxKindSecondary;
2915 else
2916 gdi.kind = kHIThemeGroupBoxKindPrimary;
2917 HIRect hirect = qt_hirectForQRect(opt->rect);
2918 HIThemeDrawGroupBox(&hirect, &gdi, cg, kHIThemeOrientationNormal);
2919 }
2920 }
2921 break;
2922 case PE_IndicatorToolBarSeparator: {
2923 QPainterPath path;
2924 if (opt->state & State_Horizontal) {
2925 int xpoint = opt->rect.center().x();
2926 path.moveTo(xpoint + 0.5, opt->rect.top() + 1);
2927 path.lineTo(xpoint + 0.5, opt->rect.bottom());
2928 } else {
2929 int ypoint = opt->rect.center().y();
2930 path.moveTo(opt->rect.left() + 2 , ypoint + 0.5);
2931 path.lineTo(opt->rect.right() + 1, ypoint + 0.5);
2932 }
2933 QPainterPathStroker theStroker;
2934 theStroker.setCapStyle(Qt::FlatCap);
2935 theStroker.setDashPattern(QVector<qreal>() << 1 << 2);
2936 path = theStroker.createStroke(path);
2937 p->fillPath(path, QColor(0, 0, 0, 119));
2938 }
2939 break;
2940 case PE_FrameWindow:
2941 break;
2942 case PE_IndicatorDockWidgetResizeHandle: {
2943 // The docwidget resize handle is drawn as a one-pixel wide line.
2944 p->save();
2945 if (opt->state & State_Horizontal) {
2946 p->setPen(QColor(160, 160, 160));
2947 p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
2948 } else {
2949 p->setPen(QColor(145, 145, 145));
2950 p->drawLine(opt->rect.topRight(), opt->rect.bottomRight());
2951 }
2952 p->restore();
2953 } break;
2954 case PE_IndicatorToolBarHandle: {
2955 p->save();
2956 QPainterPath path;
2957 int x = opt->rect.x() + 6;
2958 int y = opt->rect.y() + 5;
2959 static const int RectHeight = 2;
2960 if (opt->state & State_Horizontal) {
2961 while (y < opt->rect.height() - RectHeight - 6) {
2962 path.moveTo(x, y);
2963 path.addRect(x, y, RectHeight, RectHeight);
2964 y += 6;
2965 }
2966 } else {
2967 while (x < opt->rect.width() - RectHeight - 6) {
2968 path.moveTo(x, y);
2969 path.addRect(x, y, RectHeight, RectHeight);
2970 x += 6;
2971 }
2972 }
2973 p->setPen(Qt::NoPen);
2974 QColor dark = opt->palette.dark().color();
2975 dark.setAlphaF(0.75);
2976 QColor light = opt->palette.light().color();
2977 light.setAlphaF(0.6);
2978 p->fillPath(path, light);
2979 p->save();
2980 p->translate(1, 1);
2981 p->fillPath(path, dark);
2982 p->restore();
2983 p->translate(3, 3);
2984 p->fillPath(path, light);
2985 p->translate(1, 1);
2986 p->fillPath(path, dark);
2987 p->restore();
2988
2989 break;
2990 }
2991 case PE_IndicatorHeaderArrow:
2992 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
2993 // In HITheme, up is down, down is up and hamburgers eat people.
2994 if (header->sortIndicator != QStyleOptionHeader::None)
2995 proxy()->drawPrimitive(
2996 (header->sortIndicator == QStyleOptionHeader::SortDown) ?
2997 PE_IndicatorArrowUp : PE_IndicatorArrowDown, header, p, w);
2998 }
2999 break;
3000 case PE_IndicatorMenuCheckMark: {
3001 const int checkw = 8;
3002 const int checkh = 8;
3003 const int xoff = qMax(0, (opt->rect.width() - checkw) / 2);
3004 const int yoff = qMax(0, (opt->rect.width() - checkh) / 2);
3005 const int x1 = xoff + opt->rect.x();
3006 const int y1 = yoff + opt->rect.y() + checkw/2;
3007 const int x2 = xoff + opt->rect.x() + checkw/4;
3008 const int y2 = yoff + opt->rect.y() + checkh;
3009 const int x3 = xoff + opt->rect.x() + checkw;
3010 const int y3 = yoff + opt->rect.y();
3011
3012 QVector<QLineF> a(2);
3013 a << QLineF(x1, y1, x2, y2);
3014 a << QLineF(x2, y2, x3, y3);
3015 if (opt->palette.currentColorGroup() == QPalette::Active)
3016 p->setPen(QPen(Qt::white, 3));
3017 else
3018 p->setPen(QPen(QColor(100, 100, 100), 3));
3019 p->save();
3020 p->setRenderHint(QPainter::Antialiasing);
3021 p->drawLines(a);
3022 p->restore();
3023 break; }
3024 case PE_IndicatorViewItemCheck:
3025 case PE_Q3CheckListExclusiveIndicator:
3026 case PE_Q3CheckListIndicator:
3027 case PE_IndicatorRadioButton:
3028 case PE_IndicatorCheckBox: {
3029 bool drawColorless = (!(opt->state & State_Active))
3030 && opt->palette.currentColorGroup() == QPalette::Active;
3031 HIThemeButtonDrawInfo bdi;
3032 bdi.version = qt_mac_hitheme_version;
3033 bdi.state = tds;
3034 if (drawColorless && tds == kThemeStateInactive)
3035 bdi.state = kThemeStateActive;
3036 bdi.adornment = kThemeDrawIndicatorOnly;
3037 if (opt->state & State_HasFocus)
3038 bdi.adornment |= kThemeAdornmentFocus;
3039 bool isRadioButton = (pe == PE_Q3CheckListExclusiveIndicator
3040 || pe == PE_IndicatorRadioButton);
3041 switch (d->aquaSizeConstrain(opt, w)) {
3042 case QAquaSizeUnknown:
3043 case QAquaSizeLarge:
3044 if (isRadioButton)
3045 bdi.kind = kThemeRadioButton;
3046 else
3047 bdi.kind = kThemeCheckBox;
3048 break;
3049 case QAquaSizeMini:
3050 if (isRadioButton)
3051 bdi.kind = kThemeMiniRadioButton;
3052 else
3053 bdi.kind = kThemeMiniCheckBox;
3054 break;
3055 case QAquaSizeSmall:
3056 if (isRadioButton)
3057 bdi.kind = kThemeSmallRadioButton;
3058 else
3059 bdi.kind = kThemeSmallCheckBox;
3060 break;
3061 }
3062 if (opt->state & State_NoChange)
3063 bdi.value = kThemeButtonMixed;
3064 else if (opt->state & State_On)
3065 bdi.value = kThemeButtonOn;
3066 else
3067 bdi.value = kThemeButtonOff;
3068 HIRect macRect;
3069 if (pe == PE_Q3CheckListExclusiveIndicator || pe == PE_Q3CheckListIndicator)
3070 macRect = qt_hirectForQRect(opt->rect);
3071 else
3072 macRect = qt_hirectForQRect(opt->rect);
3073 if (!drawColorless)
3074 HIThemeDrawButton(&macRect, &bdi, cg, kHIThemeOrientationNormal, 0);
3075 else
3076 d->drawColorlessButton(macRect, &bdi, p, opt);
3077 break; }
3078 case PE_FrameFocusRect:
3079 // Use the our own focus widget stuff.
3080 break;
3081 case PE_IndicatorBranch: {
3082 if (!(opt->state & State_Children))
3083 break;
3084 HIThemeButtonDrawInfo bi;
3085 bi.version = qt_mac_hitheme_version;
3086 bi.state = tds;
3087 if (tds == kThemeStateInactive && opt->palette.currentColorGroup() == QPalette::Active)
3088 bi.state = kThemeStateActive;
3089 if (opt->state & State_Sunken)
3090 bi.state |= kThemeStatePressed;
3091 bi.kind = kThemeDisclosureButton;
3092 if (opt->state & State_Open)
3093 bi.value = kThemeDisclosureDown;
3094 else
3095 bi.value = opt->direction == Qt::LeftToRight ? kThemeDisclosureRight : kThemeDisclosureLeft;
3096 bi.adornment = kThemeAdornmentNone;
3097 HIRect hirect = qt_hirectForQRect(opt->rect);
3098 HIThemeDrawButton(&hirect, &bi, cg, kHIThemeOrientationNormal, 0);
3099 break; }
3100 case PE_Frame: {
3101 QPen oldPen = p->pen();
3102 QPen newPen;
3103 newPen.setBrush(opt->palette.dark());
3104 p->setPen(newPen);
3105 p->drawRect(opt->rect.adjusted(0, 0, -1, -1));
3106 p->setPen(oldPen);
3107 break; }
3108 case PE_FrameLineEdit:
3109 if (const QStyleOptionFrame *frame = qstyleoption_cast<const QStyleOptionFrame *>(opt)) {
3110 if (frame->state & State_Sunken) {
3111 QColor baseColor(frame->palette.background().color());
3112 HIThemeFrameDrawInfo fdi;
3113 fdi.version = qt_mac_hitheme_version;
3114 fdi.state = tds;
3115 SInt32 frame_size;
3116 if (pe == PE_FrameLineEdit) {
3117 fdi.kind = kHIThemeFrameTextFieldSquare;
3118 GetThemeMetric(kThemeMetricEditTextFrameOutset, &frame_size);
3119 if ((frame->state & State_ReadOnly) || !(frame->state & State_Enabled))
3120 fdi.state = kThemeStateInactive;
3121 } else {
3122 baseColor = QColor(150, 150, 150); //hardcoded since no query function --Sam
3123 fdi.kind = kHIThemeFrameListBox;
3124 GetThemeMetric(kThemeMetricListBoxFrameOutset, &frame_size);
3125 }
3126 fdi.isFocused = (frame->state & State_HasFocus);
3127 int lw = frame->lineWidth;
3128 if (lw <= 0)
3129 lw = proxy()->pixelMetric(PM_DefaultFrameWidth, frame, w);
3130 { //clear to base color
3131 p->save();
3132 p->setPen(QPen(baseColor, lw));
3133 p->setBrush(Qt::NoBrush);
3134 p->drawRect(frame->rect);
3135 p->restore();
3136 }
3137 HIRect hirect = qt_hirectForQRect(frame->rect,
3138 QRect(frame_size, frame_size,
3139 frame_size * 2, frame_size * 2));
3140
3141 HIThemeDrawFrame(&hirect, &fdi, cg, kHIThemeOrientationNormal);
3142 } else {
3143 QWindowsStyle::drawPrimitive(pe, opt, p, w);
3144 }
3145 }
3146 break;
3147 case PE_PanelLineEdit:
3148 QWindowsStyle::drawPrimitive(pe, opt, p, w);
3149 break;
3150 case PE_FrameTabWidget:
3151 if (const QStyleOptionTabWidgetFrame *twf
3152 = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
3153 HIRect hirect = qt_hirectForQRect(twf->rect);
3154 HIThemeTabPaneDrawInfo tpdi;
3155 tpdi.version = qt_mac_hitheme_tab_version();
3156 tpdi.state = tds;
3157 tpdi.direction = getTabDirection(twf->shape);
3158 tpdi.size = kHIThemeTabSizeNormal;
3159 tpdi.kind = kHIThemeTabKindNormal;
3160 tpdi.adornment = kHIThemeTabPaneAdornmentNormal;
3161 HIThemeDrawTabPane(&hirect, &tpdi, cg, kHIThemeOrientationNormal);
3162 }
3163 break;
3164 case PE_PanelScrollAreaCorner: {
3165 const QBrush brush(qApp->palette().brush(QPalette::Base));
3166 p->fillRect(opt->rect, brush);
3167 p->setPen(QPen(QColor(217, 217, 217)));
3168 p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
3169 p->drawLine(opt->rect.topLeft(), opt->rect.bottomLeft());
3170 } break;
3171 case PE_FrameStatusBarItem:
3172 QCommonStyle::drawPrimitive(pe, opt, p, w);
3173 break;
3174 case PE_IndicatorTabClose: {
3175 bool hover = (opt->state & State_MouseOver);
3176 bool selected = (opt->state & State_Selected);
3177 bool active = (opt->state & State_Active);
3178 drawTabCloseButton(p, hover, active, selected);
3179 } break;
3180 case PE_PanelStatusBar: {
3181 if (QSysInfo::MacintoshVersion <= QSysInfo::MV_10_4) {
3182 QWindowsStyle::drawPrimitive(pe, opt, p, w);
3183 break;
3184 }
3185 // Use the Leopard style only if the status bar is the status bar for a
3186 // QMainWindow with a unifed toolbar.
3187 if (w == 0 || w->parent() == 0 || qobject_cast<QMainWindow *>(w->parent()) == 0 ||
3188 qobject_cast<QMainWindow *>(w->parent())->unifiedTitleAndToolBarOnMac() == false ) {
3189 QWindowsStyle::drawPrimitive(pe, opt, p, w);
3190 break;
3191 }
3192
3193 // Fill the status bar with the titlebar gradient.
3194 QLinearGradient linearGrad(0, opt->rect.top(), 0, opt->rect.bottom());
3195 if (opt->state & QStyle::State_Active) {
3196 linearGrad.setColorAt(0, titlebarGradientActiveBegin);
3197 linearGrad.setColorAt(1, titlebarGradientActiveEnd);
3198 } else {
3199 linearGrad.setColorAt(0, titlebarGradientInactiveBegin);
3200 linearGrad.setColorAt(1, titlebarGradientInactiveEnd);
3201 }
3202 p->fillRect(opt->rect, linearGrad);
3203
3204 // Draw the black separator line at the top of the status bar.
3205 if (opt->state & QStyle::State_Active)
3206 p->setPen(titlebarSeparatorLineActive);
3207 else
3208 p->setPen(titlebarSeparatorLineInactive);
3209 p->drawLine(opt->rect.left(), opt->rect.top(), opt->rect.right(), opt->rect.top());
3210
3211 break;
3212 }
3213
3214 default:
3215 QWindowsStyle::drawPrimitive(pe, opt, p, w);
3216 break;
3217 }
3218}
3219
3220static inline QPixmap darkenPixmap(const QPixmap &pixmap)
3221{
3222 QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
3223 int imgh = img.height();
3224 int imgw = img.width();
3225 int h, s, v, a;
3226 QRgb pixel;
3227 for (int y = 0; y < imgh; ++y) {
3228 for (int x = 0; x < imgw; ++x) {
3229 pixel = img.pixel(x, y);
3230 a = qAlpha(pixel);
3231 QColor hsvColor(pixel);
3232 hsvColor.getHsv(&h, &s, &v);
3233 s = qMin(100, s * 2);
3234 v = v / 2;
3235 hsvColor.setHsv(h, s, v);
3236 pixel = hsvColor.rgb();
3237 img.setPixel(x, y, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel), a));
3238 }
3239 }
3240 return QPixmap::fromImage(img);
3241}
3242
3243
3244
3245void QMacStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *p,
3246 const QWidget *w) const
3247{
3248 ThemeDrawState tds = d->getDrawState(opt->state);
3249 QMacCGContext cg(p);
3250 switch (ce) {
3251 case CE_HeaderSection:
3252 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
3253 HIThemeButtonDrawInfo bdi;
3254 bdi.version = qt_mac_hitheme_version;
3255 State flags = header->state;
3256 QRect ir = header->rect;
3257 bdi.kind = kThemeListHeaderButton;
3258 bdi.adornment = kThemeAdornmentNone;
3259 bdi.state = kThemeStateActive;
3260
3261 if (flags & State_On)
3262 bdi.value = kThemeButtonOn;
3263 else
3264 bdi.value = kThemeButtonOff;
3265
3266 if (header->orientation == Qt::Horizontal){
3267 switch (header->position) {
3268 case QStyleOptionHeader::Beginning:
3269 break;
3270 case QStyleOptionHeader::Middle:
3271 case QStyleOptionHeader::End:
3272 ir.adjust(-1, 0, 0, 0);
3273 break;
3274 default:
3275 break;
3276 }
3277
3278 if (header->position != QStyleOptionHeader::Beginning
3279 && header->position != QStyleOptionHeader::OnlyOneSection) {
3280 bdi.adornment = header->direction == Qt::LeftToRight
3281 ? kThemeAdornmentHeaderButtonLeftNeighborSelected
3282 : kThemeAdornmentHeaderButtonRightNeighborSelected;
3283 }
3284 }
3285
3286 if (flags & State_Active) {
3287 if (!(flags & State_Enabled))
3288 bdi.state = kThemeStateUnavailable;
3289 else if (flags & State_Sunken)
3290 bdi.state = kThemeStatePressed;
3291 } else {
3292 if (flags & State_Enabled)
3293 bdi.state = kThemeStateInactive;
3294 else
3295 bdi.state = kThemeStateUnavailableInactive;
3296 }
3297
3298 if (header->sortIndicator != QStyleOptionHeader::None) {
3299 bdi.value = kThemeButtonOn;
3300 if (header->sortIndicator == QStyleOptionHeader::SortDown)
3301 bdi.adornment = kThemeAdornmentHeaderButtonSortUp;
3302 }
3303 if (flags & State_HasFocus)
3304 bdi.adornment = kThemeAdornmentFocus;
3305
3306 ir = visualRect(header->direction, header->rect, ir);
3307 HIRect bounds = qt_hirectForQRect(ir);
3308
3309 bool noVerticalHeader = true;
3310 if (w)
3311 if (const QTableView *table = qobject_cast<const QTableView *>(w->parentWidget()))
3312 noVerticalHeader = !table->verticalHeader()->isVisible();
3313
3314 bool drawTopBorder = header->orientation == Qt::Horizontal;
3315 bool drawLeftBorder = header->orientation == Qt::Vertical
3316 || header->position == QStyleOptionHeader::OnlyOneSection
3317 || (header->position == QStyleOptionHeader::Beginning && noVerticalHeader);
3318 d->drawTableHeader(bounds, drawTopBorder, drawLeftBorder, bdi, p);
3319 }
3320 break;
3321 case CE_HeaderLabel:
3322 if (const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
3323 QRect textr = header->rect;
3324 if (!header->icon.isNull()) {
3325 QIcon::Mode mode = QIcon::Disabled;
3326 if (opt->state & State_Enabled)
3327 mode = QIcon::Normal;
3328 QPixmap pixmap = header->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize), mode);
3329
3330 QRect pixr = header->rect;
3331 pixr.setY(header->rect.center().y() - (pixmap.height() - 1) / 2);
3332 proxy()->drawItemPixmap(p, pixr, Qt::AlignVCenter, pixmap);
3333 textr.translate(pixmap.width() + 2, 0);
3334 }
3335
3336 proxy()->drawItemText(p, textr, header->textAlignment | Qt::AlignVCenter, header->palette,
3337 header->state & State_Enabled, header->text, QPalette::ButtonText);
3338 }
3339 break;
3340 case CE_ToolButtonLabel:
3341 if (const QStyleOptionToolButton *tb = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
3342 QStyleOptionToolButton myTb = *tb;
3343 myTb.state &= ~State_AutoRaise;
3344 if (w && qobject_cast<QToolBar *>(w->parentWidget())) {
3345 QRect cr = tb->rect;
3346 int shiftX = 0;
3347 int shiftY = 0;
3348 bool needText = false;
3349 int alignment = 0;
3350 bool down = tb->state & (State_Sunken | State_On);
3351 if (down) {
3352 shiftX = proxy()->pixelMetric(PM_ButtonShiftHorizontal, tb, w);
3353 shiftY = proxy()->pixelMetric(PM_ButtonShiftVertical, tb, w);
3354 }
3355 // The down state is special for QToolButtons in a toolbar on the Mac
3356 // The text is a bit bolder and gets a drop shadow and the icons are also darkened.
3357 // This doesn't really fit into any particular case in QIcon, so we
3358 // do the majority of the work ourselves.
3359 if (!(tb->features & QStyleOptionToolButton::Arrow)) {
3360 Qt::ToolButtonStyle tbstyle = tb->toolButtonStyle;
3361 if (tb->icon.isNull() && !tb->text.isEmpty())
3362 tbstyle = Qt::ToolButtonTextOnly;
3363
3364 switch (tbstyle) {
3365 case Qt::ToolButtonTextOnly: {
3366 needText = true;
3367 alignment = Qt::AlignCenter;
3368 break; }
3369 case Qt::ToolButtonIconOnly:
3370 case Qt::ToolButtonTextBesideIcon:
3371 case Qt::ToolButtonTextUnderIcon: {
3372 QRect pr = cr;
3373 QIcon::Mode iconMode = (tb->state & State_Enabled) ? QIcon::Normal
3374 : QIcon::Disabled;
3375 QIcon::State iconState = (tb->state & State_On) ? QIcon::On
3376 : QIcon::Off;
3377 QPixmap pixmap = tb->icon.pixmap(tb->rect.size().boundedTo(tb->iconSize), iconMode, iconState);
3378
3379 // Draw the text if it's needed.
3380 if (tb->toolButtonStyle != Qt::ToolButtonIconOnly) {
3381 needText = true;
3382 if (tb->toolButtonStyle == Qt::ToolButtonTextUnderIcon) {
3383 QMainWindow *mw = qobject_cast<QMainWindow *>(w->window());
3384 if (mw && mw->unifiedTitleAndToolBarOnMac()) {
3385 pr.setHeight(pixmap.size().height());
3386 cr.adjust(0, pr.bottom() + 1, 0, 1);
3387 } else {
3388 pr.setHeight(pixmap.size().height() + 6);
3389 cr.adjust(0, pr.bottom(), 0, -3);
3390 }
3391 alignment |= Qt::AlignCenter;
3392 } else {
3393 pr.setWidth(pixmap.width() + 8);
3394 cr.adjust(pr.right(), 0, 0, 0);
3395 alignment |= Qt::AlignLeft | Qt::AlignVCenter;
3396 }
3397 }
3398 if (opt->state & State_Sunken) {
3399 pr.translate(shiftX, shiftY);
3400 pixmap = darkenPixmap(pixmap);
3401 }
3402 proxy()->drawItemPixmap(p, pr, Qt::AlignCenter, pixmap);
3403 break; }
3404 default:
3405 Q_ASSERT(false);
3406 break;
3407 }
3408
3409 if (needText) {
3410 QPalette pal = tb->palette;
3411 QPalette::ColorRole role = QPalette::NoRole;
3412 if (down)
3413 cr.translate(shiftX, shiftY);
3414 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5
3415 && (tbstyle == Qt::ToolButtonTextOnly
3416 || (tbstyle != Qt::ToolButtonTextOnly && !down))) {
3417 QPen pen = p->pen();
3418 QColor light = down ? Qt::black : Qt::white;
3419 light.setAlphaF(0.375f);
3420 p->setPen(light);
3421 p->drawText(cr.adjusted(0, 1, 0, 1), alignment, tb->text);
3422 p->setPen(pen);
3423 if (down && tbstyle == Qt::ToolButtonTextOnly) {
3424 pal = QApplication::palette("QMenu");
3425 pal.setCurrentColorGroup(tb->palette.currentColorGroup());
3426 role = QPalette::HighlightedText;
3427 }
3428 }
3429 drawItemText(p, cr, alignment, pal,
3430 tb->state & State_Enabled, tb->text, role);
3431 if (QSysInfo::MacintoshVersion < QSysInfo::MV_10_5 &&
3432 (tb->state & State_Sunken)) {
3433 // Draw a "drop shadow" in earlier versions.
3434 drawItemText(p, cr.adjusted(0, 1, 0, 1), alignment,
3435 tb->palette, tb->state & State_Enabled, tb->text);
3436 }
3437 }
3438 } else {
3439 QWindowsStyle::drawControl(ce, &myTb, p, w);
3440 }
3441 } else {
3442 QWindowsStyle::drawControl(ce, &myTb, p, w);
3443 }
3444 }
3445 break;
3446 case CE_ToolBoxTabShape:
3447 QCommonStyle::drawControl(ce, opt, p, w);
3448 break;
3449 case CE_PushButtonBevel:
3450 if (const QStyleOptionButton *btn = ::qstyleoption_cast<const QStyleOptionButton *>(opt)) {
3451 if (!(btn->state & (State_Raised | State_Sunken | State_On)))
3452 break;
3453
3454 if (btn->features & QStyleOptionButton::CommandLinkButton) {
3455 QWindowsStyle::drawControl(ce, opt, p, w);
3456 break;
3457 }
3458
3459 HIThemeButtonDrawInfo bdi;
3460 d->initHIThemePushButton(btn, w, tds, &bdi);
3461 if (btn->features & QStyleOptionButton::DefaultButton
3462 && d->animatable(QMacStylePrivate::AquaPushButton, w)) {
3463 bdi.adornment |= kThemeAdornmentDefault;
3464 bdi.animation.time.start = d->defaultButtonStart;
3465 bdi.animation.time.current = CFAbsoluteTimeGetCurrent();
3466 if (d->timerID <= -1)
3467 QMetaObject::invokeMethod(d, "startAnimationTimer", Qt::QueuedConnection);
3468 }
3469 // Unlike Carbon, we want the button to always be drawn inside its bounds.
3470 // Therefore, make the button a bit smaller, so that even if it got focus,
3471 // the focus 'shadow' will be inside.
3472 HIRect newRect = qt_hirectForQRect(btn->rect);
3473 if (bdi.kind == kThemePushButton || bdi.kind == kThemePushButtonSmall) {
3474 newRect.origin.x += PushButtonLeftOffset;
3475 newRect.origin.y += PushButtonTopOffset;
3476 newRect.size.width -= PushButtonRightOffset;
3477 newRect.size.height -= PushButtonBottomOffset;
3478 } else if (bdi.kind == kThemePushButtonMini) {
3479 newRect.origin.x += PushButtonLeftOffset - 2;
3480 newRect.origin.y += PushButtonTopOffset;
3481 newRect.size.width -= PushButtonRightOffset - 4;
3482 }
3483 HIThemeDrawButton(&newRect, &bdi, cg, kHIThemeOrientationNormal, 0);
3484
3485 if (btn->features & QStyleOptionButton::HasMenu) {
3486 int mbi = proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, btn, w);
3487 QRect ir = btn->rect;
3488 HIRect arrowRect = CGRectMake(ir.right() - mbi - PushButtonRightOffset,
3489 ir.height() / 2 - 4, mbi, ir.height() / 2);
3490 bool drawColorless = btn->palette.currentColorGroup() == QPalette::Active;
3491 if (drawColorless && tds == kThemeStateInactive)
3492 tds = kThemeStateActive;
3493
3494 HIThemePopupArrowDrawInfo pdi;
3495 pdi.version = qt_mac_hitheme_version;
3496 pdi.state = tds;
3497 pdi.orientation = kThemeArrowDown;
3498 if (arrowRect.size.width < 8.)
3499 pdi.size = kThemeArrow5pt;
3500 else
3501 pdi.size = kThemeArrow9pt;
3502 HIThemeDrawPopupArrow(&arrowRect, &pdi, cg, kHIThemeOrientationNormal);
3503 }
3504 }
3505 break;
3506 case CE_PushButtonLabel:
3507 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
3508 // We really don't want the label to be drawn the same as on
3509 // windows style if it has an icon and text, then it should be more like a
3510 // tab. So, cheat a little here. However, if it *is* only an icon
3511 // the windows style works great, so just use that implementation.
3512 bool hasMenu = btn->features & QStyleOptionButton::HasMenu;
3513 bool hasIcon = !btn->icon.isNull();
3514 bool hasText = !btn->text.isEmpty();
3515 if (!hasIcon && !hasMenu) {
3516 // ### this is really overly difficult, simplify.
3517 // It basically tries to get the right font for "small" and "mini" icons.
3518 QFont oldFont = p->font();
3519 QFont newFont = qt_app_fonts_hash()->value("QPushButton", QFont());
3520 ThemeFontID themeId = kThemePushButtonFont;
3521 if (oldFont == newFont) { // Yes, use HITheme to draw the text for small sizes.
3522 switch (d->aquaSizeConstrain(opt, w)) {
3523 default:
3524 break;
3525 case QAquaSizeSmall:
3526 themeId = kThemeSmallSystemFont;
3527 break;
3528 case QAquaSizeMini:
3529 themeId = kThemeMiniSystemFont;
3530 break;
3531 }
3532 }
3533 if (themeId == kThemePushButtonFont) {
3534 QWindowsStyle::drawControl(ce, btn, p, w);
3535 } else {
3536 p->save();
3537 CGContextSetShouldAntialias(cg, true);
3538 CGContextSetShouldSmoothFonts(cg, true);
3539 HIThemeTextInfo tti;
3540 tti.version = qt_mac_hitheme_version;
3541 tti.state = tds;
3542 QColor textColor = btn->palette.buttonText().color();
3543 CGFloat colorComp[] = { textColor.redF(), textColor.greenF(),
3544 textColor.blueF(), textColor.alphaF() };
3545 CGContextSetFillColorSpace(cg, QCoreGraphicsPaintEngine::macGenericColorSpace());
3546 CGContextSetFillColor(cg, colorComp);
3547 tti.fontID = themeId;
3548 tti.horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
3549 tti.verticalFlushness = kHIThemeTextVerticalFlushCenter;
3550 tti.options = kHIThemeTextBoxOptionNone;
3551 tti.truncationPosition = kHIThemeTextTruncationNone;
3552 tti.truncationMaxLines = 1 + btn->text.count(QLatin1Char('\n'));
3553 QCFString buttonText = qt_mac_removeMnemonics(btn->text);
3554 QRect r = btn->rect;
3555 HIRect bounds = qt_hirectForQRect(r);
3556 HIThemeDrawTextBox(buttonText, &bounds, &tti,
3557 cg, kHIThemeOrientationNormal);
3558 p->restore();
3559 }
3560 } else {
3561 if (hasIcon && !hasText) {
3562 QWindowsStyle::drawControl(ce, btn, p, w);
3563 } else {
3564 QRect freeContentRect = btn->rect;
3565 QRect textRect = itemTextRect(
3566 btn->fontMetrics, freeContentRect, Qt::AlignCenter, btn->state & State_Enabled, btn->text);
3567 if (hasMenu)
3568 textRect.adjust(-1, 0, -1, 0);
3569 // Draw the icon:
3570 if (hasIcon) {
3571 int contentW = textRect.width();
3572 if (hasMenu)
3573 contentW += proxy()->pixelMetric(PM_MenuButtonIndicator) + 4;
3574 QIcon::Mode mode = btn->state & State_Enabled ? QIcon::Normal : QIcon::Disabled;
3575 if (mode == QIcon::Normal && btn->state & State_HasFocus)
3576 mode = QIcon::Active;
3577 // Decide if the icon is should be on or off:
3578 QIcon::State state = QIcon::Off;
3579 if (btn->state & State_On)
3580 state = QIcon::On;
3581 QPixmap pixmap = btn->icon.pixmap(btn->iconSize, mode, state);
3582 contentW += pixmap.width() + PushButtonContentPadding;
3583 int iconLeftOffset = freeContentRect.x() + (freeContentRect.width() - contentW) / 2;
3584 int iconTopOffset = freeContentRect.y() + (freeContentRect.height() - pixmap.height()) / 2;
3585 QRect iconDestRect(iconLeftOffset, iconTopOffset, pixmap.width(), pixmap.height());
3586 QRect visualIconDestRect = visualRect(btn->direction, freeContentRect, iconDestRect);
3587 proxy()->drawItemPixmap(p, visualIconDestRect, Qt::AlignLeft | Qt::AlignVCenter, pixmap);
3588 int newOffset = iconDestRect.x() + iconDestRect.width()
3589 + PushButtonContentPadding - textRect.x();
3590 textRect.adjust(newOffset, 0, newOffset, 0);
3591 }
3592 // Draw the text:
3593 if (hasText) {
3594 textRect = visualRect(btn->direction, freeContentRect, textRect);
3595 proxy()->drawItemText(p, textRect, Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic, btn->palette,
3596 (btn->state & State_Enabled), btn->text, QPalette::ButtonText);
3597 }
3598 }
3599 }
3600 }
3601 break;
3602 case CE_ComboBoxLabel:
3603 if (const QStyleOptionComboBox *cb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
3604 QStyleOptionComboBox comboCopy = *cb;
3605 comboCopy.direction = Qt::LeftToRight;
3606 QWindowsStyle::drawControl(CE_ComboBoxLabel, &comboCopy, p, w);
3607 }
3608 break;
3609 case CE_TabBarTabShape:
3610 if (const QStyleOptionTab *tabOpt = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
3611
3612 if (const QStyleOptionTabV3 *tabOptV3 = qstyleoption_cast<const QStyleOptionTabV3 *>(opt)) {
3613 if (tabOptV3->documentMode) {
3614 p->save();
3615 QRect tabRect = tabOptV3->rect;
3616 drawTabShape(p, tabOptV3);
3617 p->restore();
3618 return;
3619 }
3620 }
3621 HIThemeTabDrawInfo tdi;
3622 tdi.version = 1;
3623 tdi.style = kThemeTabNonFront;
3624 tdi.direction = getTabDirection(tabOpt->shape);
3625 switch (d->aquaSizeConstrain(opt, w)) {
3626 default:
3627 case QAquaSizeUnknown:
3628 case QAquaSizeLarge:
3629 tdi.size = kHIThemeTabSizeNormal;
3630 break;
3631 case QAquaSizeSmall:
3632 tdi.size = kHIThemeTabSizeSmall;
3633 break;
3634 case QAquaSizeMini:
3635 tdi.size = kHIThemeTabSizeMini;
3636 break;
3637 }
3638 bool verticalTabs = tdi.direction == kThemeTabWest || tdi.direction == kThemeTabEast;
3639 QRect tabRect = tabOpt->rect;
3640
3641 bool selected = tabOpt->state & State_Selected;
3642 if (selected) {
3643 if (!(tabOpt->state & State_Active))
3644 tdi.style = kThemeTabFrontUnavailable;
3645 else if (!(tabOpt->state & State_Enabled))
3646 tdi.style = kThemeTabFrontInactive;
3647 else
3648 tdi.style = kThemeTabFront;
3649 } else if (!(tabOpt->state & State_Active)) {
3650 tdi.style = kThemeTabNonFrontUnavailable;
3651 } else if (!(tabOpt->state & State_Enabled)) {
3652 tdi.style = kThemeTabNonFrontInactive;
3653 } else if (tabOpt->state & State_Sunken) {
3654 tdi.style = kThemeTabFrontInactive; // (should be kThemeTabNonFrontPressed)
3655 }
3656 if (tabOpt->state & State_HasFocus)
3657 tdi.adornment = kHIThemeTabAdornmentFocus;
3658 else
3659 tdi.adornment = kHIThemeTabAdornmentNone;
3660 tdi.kind = kHIThemeTabKindNormal;
3661 if (!verticalTabs)
3662 tabRect.setY(tabRect.y() - 1);
3663 else
3664 tabRect.setX(tabRect.x() - 1);
3665 QStyleOptionTab::TabPosition tp = tabOpt->position;
3666 QStyleOptionTab::SelectedPosition sp = tabOpt->selectedPosition;
3667 if (tabOpt->direction == Qt::RightToLeft && !verticalTabs) {
3668 if (sp == QStyleOptionTab::NextIsSelected)
3669 sp = QStyleOptionTab::PreviousIsSelected;
3670 else if (sp == QStyleOptionTab::PreviousIsSelected)
3671 sp = QStyleOptionTab::NextIsSelected;
3672 switch (tp) {
3673 case QStyleOptionTab::Beginning:
3674 tp = QStyleOptionTab::End;
3675 break;
3676 case QStyleOptionTab::End:
3677 tp = QStyleOptionTab::Beginning;
3678 break;
3679 default:
3680 break;
3681 }
3682 }
3683 bool stretchTabs = (!verticalTabs && tabRect.height() > 22 || verticalTabs && tabRect.width() > 22);
3684
3685 switch (tp) {
3686 case QStyleOptionTab::Beginning:
3687 tdi.position = kHIThemeTabPositionFirst;
3688 if (sp != QStyleOptionTab::NextIsSelected || stretchTabs)
3689 tdi.adornment |= kHIThemeTabAdornmentTrailingSeparator;
3690 break;
3691 case QStyleOptionTab::Middle:
3692 tdi.position = kHIThemeTabPositionMiddle;
3693 if (selected)
3694 tdi.adornment |= kHIThemeTabAdornmentLeadingSeparator;
3695 if (sp != QStyleOptionTab::NextIsSelected || stretchTabs) // Also when we're selected.
3696 tdi.adornment |= kHIThemeTabAdornmentTrailingSeparator;
3697 break;
3698 case QStyleOptionTab::End:
3699 tdi.position = kHIThemeTabPositionLast;
3700 if (selected)
3701 tdi.adornment |= kHIThemeTabAdornmentLeadingSeparator;
3702 break;
3703 case QStyleOptionTab::OnlyOneTab:
3704 tdi.position = kHIThemeTabPositionOnly;
3705 break;
3706 }
3707 // HITheme doesn't stretch its tabs. Therefore we have to cheat and do the job ourselves.
3708 if (stretchTabs) {
3709 HIRect hirect = CGRectMake(0, 0, 23, 23);
3710 QPixmap pm(23, 23);
3711 pm.fill(Qt::transparent);
3712 {
3713 QMacCGContext pmcg(&pm);
3714 HIThemeDrawTab(&hirect, &tdi, pmcg, kHIThemeOrientationNormal, 0);
3715 }
3716 QStyleHelper::drawBorderPixmap(pm, p, tabRect, 7, 7, 7, 7);
3717 } else {
3718 HIRect hirect = qt_hirectForQRect(tabRect);
3719 HIThemeDrawTab(&hirect, &tdi, cg, kHIThemeOrientationNormal, 0);
3720 }
3721 }
3722 break;
3723 case CE_TabBarTabLabel:
3724 if (const QStyleOptionTab *tab = qstyleoption_cast<const QStyleOptionTab *>(opt)) {
3725 QStyleOptionTabV3 myTab = *tab;
3726 ThemeTabDirection ttd = getTabDirection(myTab.shape);
3727 bool verticalTabs = ttd == kThemeTabWest || ttd == kThemeTabEast;
3728
3729 // Check to see if we use have the same as the system font
3730 // (QComboMenuItem is internal and should never be seen by the
3731 // outside world, unless they read the source, in which case, it's
3732 // their own fault).
3733 bool nonDefaultFont = p->font() != qt_app_fonts_hash()->value("QComboMenuItem");
3734 if (verticalTabs || nonDefaultFont || !tab->icon.isNull()
3735 || !myTab.leftButtonSize.isNull() || !myTab.rightButtonSize.isNull()) {
3736 int heightOffset = 0;
3737 if (verticalTabs) {
3738 heightOffset = -1;
3739 } else if (nonDefaultFont) {
3740 if (p->fontMetrics().height() == myTab.rect.height())
3741 heightOffset = 2;
3742 }
3743 myTab.rect.setHeight(myTab.rect.height() + heightOffset);
3744
3745 if (myTab.documentMode) {
3746 p->save();
3747 rotateTabPainter(p, myTab.shape, myTab.rect);
3748
3749 QPalette np = tab->palette;
3750 np.setColor(QPalette::WindowText, QColor(255, 255, 255, 75));
3751 QRect nr = subElementRect(SE_TabBarTabText, opt, w);
3752 nr.moveTop(-1);
3753 int alignment = Qt::AlignCenter | Qt::TextShowMnemonic | Qt::TextHideMnemonic;
3754 proxy()->drawItemText(p, nr, alignment, np, tab->state & State_Enabled,
3755 tab->text, QPalette::WindowText);
3756 p->restore();
3757 }
3758
3759 QCommonStyle::drawControl(ce, &myTab, p, w);
3760 } else {
3761 p->save();
3762 CGContextSetShouldAntialias(cg, true);
3763 CGContextSetShouldSmoothFonts(cg, true);
3764 HIThemeTextInfo tti;
3765 tti.version = qt_mac_hitheme_version;
3766 tti.state = tds;
3767 QColor textColor = myTab.palette.windowText().color();
3768 CGFloat colorComp[] = { textColor.redF(), textColor.greenF(),
3769 textColor.blueF(), textColor.alphaF() };
3770 CGContextSetFillColorSpace(cg, QCoreGraphicsPaintEngine::macGenericColorSpace());
3771 CGContextSetFillColor(cg, colorComp);
3772 switch (d->aquaSizeConstrain(opt, w)) {
3773 default:
3774 case QAquaSizeUnknown:
3775 case QAquaSizeLarge:
3776 tti.fontID = kThemeSystemFont;
3777 break;
3778 case QAquaSizeSmall:
3779 tti.fontID = kThemeSmallSystemFont;
3780 break;
3781 case QAquaSizeMini:
3782 tti.fontID = kThemeMiniSystemFont;
3783 break;
3784 }
3785 tti.horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
3786 tti.verticalFlushness = kHIThemeTextVerticalFlushCenter;
3787 tti.options = verticalTabs ? kHIThemeTextBoxOptionStronglyVertical : kHIThemeTextBoxOptionNone;
3788 tti.truncationPosition = kHIThemeTextTruncationNone;
3789 tti.truncationMaxLines = 1 + myTab.text.count(QLatin1Char('\n'));
3790 QCFString tabText = qt_mac_removeMnemonics(myTab.text);
3791 QRect r = myTab.rect.adjusted(0, 0, 0, -1);
3792 HIRect bounds = qt_hirectForQRect(r);
3793 HIThemeDrawTextBox(tabText, &bounds, &tti, cg, kHIThemeOrientationNormal);
3794 p->restore();
3795 }
3796 }
3797 break;
3798 case CE_DockWidgetTitle:
3799 if (const QDockWidget *dockWidget = qobject_cast<const QDockWidget *>(w)) {
3800 bool floating = dockWidget->isFloating();
3801 if (floating) {
3802 ThemeDrawState tds = d->getDrawState(opt->state);
3803 HIThemeWindowDrawInfo wdi;
3804 wdi.version = qt_mac_hitheme_version;
3805 wdi.state = tds;
3806 wdi.windowType = kThemeMovableDialogWindow;
3807 wdi.titleHeight = opt->rect.height();
3808 wdi.titleWidth = opt->rect.width();
3809 wdi.attributes = 0;
3810
3811 HIRect titleBarRect;
3812 HIRect tmpRect = qt_hirectForQRect(opt->rect);
3813 {
3814 QCFType<HIShapeRef> titleRegion;
3815 QRect newr = opt->rect.adjusted(0, 0, 2, 0);
3816 HIThemeGetWindowShape(&tmpRect, &wdi, kWindowTitleBarRgn, &titleRegion);
3817 ptrHIShapeGetBounds(titleRegion, &tmpRect);
3818 newr.translate(newr.x() - int(tmpRect.origin.x), newr.y() - int(tmpRect.origin.y));
3819 titleBarRect = qt_hirectForQRect(newr);
3820 }
3821 QMacCGContext cg(p);
3822 HIThemeDrawWindowFrame(&titleBarRect, &wdi, cg, kHIThemeOrientationNormal, 0);
3823 } else {
3824 // fill title bar background
3825 QLinearGradient linearGrad(0, opt->rect.top(), 0, opt->rect.bottom());
3826 linearGrad.setColorAt(0, mainWindowGradientBegin);
3827 linearGrad.setColorAt(1, mainWindowGradientEnd);
3828 p->fillRect(opt->rect, linearGrad);
3829
3830 // draw horizontal lines at top and bottom
3831 p->save();
3832 p->setPen(mainWindowGradientBegin.lighter(114));
3833 p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
3834 p->setPen(mainWindowGradientEnd.darker(114));
3835 p->drawLine(opt->rect.bottomLeft(), opt->rect.bottomRight());
3836 p->restore();
3837 }
3838 }
3839
3840 // Draw the text...
3841 if (const QStyleOptionDockWidget *dwOpt = qstyleoption_cast<const QStyleOptionDockWidget *>(opt)) {
3842 if (!dwOpt->title.isEmpty()) {
3843 const QStyleOptionDockWidgetV2 *v2
3844 = qstyleoption_cast<const QStyleOptionDockWidgetV2*>(dwOpt);
3845 bool verticalTitleBar = v2 == 0 ? false : v2->verticalTitleBar;
3846
3847 QRect titleRect = subElementRect(SE_DockWidgetTitleBarText, opt, w);
3848 if (verticalTitleBar) {
3849 QRect rect = dwOpt->rect;
3850 QRect r = rect;
3851 QSize s = r.size();
3852 s.transpose();
3853 r.setSize(s);
3854
3855 titleRect = QRect(r.left() + rect.bottom()
3856 - titleRect.bottom(),
3857 r.top() + titleRect.left() - rect.left(),
3858 titleRect.height(), titleRect.width());
3859
3860 p->translate(r.left(), r.top() + r.width());
3861 p->rotate(-90);
3862 p->translate(-r.left(), -r.top());
3863 }
3864
3865 QFont oldFont = p->font();
3866 p->setFont(qt_app_fonts_hash()->value("QToolButton", p->font()));
3867 QString text = p->fontMetrics().elidedText(dwOpt->title, Qt::ElideRight,
3868 titleRect.width());
3869 drawItemText(p, titleRect,
3870 Qt::AlignCenter | Qt::TextShowMnemonic, dwOpt->palette,
3871 dwOpt->state & State_Enabled, text,
3872 QPalette::WindowText);
3873 p->setFont(oldFont);
3874 }
3875 }
3876 break;
3877 case CE_FocusFrame: {
3878 int xOff = proxy()->pixelMetric(PM_FocusFrameHMargin, opt, w) + 1;
3879 int yOff = proxy()->pixelMetric(PM_FocusFrameVMargin, opt, w) + 1;
3880 HIRect hirect = CGRectMake(xOff+opt->rect.x(), yOff+opt->rect.y(), opt->rect.width() - 2 * xOff,
3881 opt->rect.height() - 2 * yOff);
3882 HIThemeDrawFocusRect(&hirect, true, QMacCGContext(p), kHIThemeOrientationNormal);
3883 break; }
3884 case CE_MenuItem:
3885 case CE_MenuEmptyArea:
3886 if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
3887 p->fillRect(mi->rect, opt->palette.background());
3888 QAquaWidgetSize widgetSize = d->aquaSizeConstrain(opt, w);
3889 int tabwidth = mi->tabWidth;
3890 int maxpmw = mi->maxIconWidth;
3891 bool active = mi->state & State_Selected;
3892 bool enabled = mi->state & State_Enabled;
3893 HIRect menuRect = qt_hirectForQRect(mi->menuRect);
3894 HIRect itemRect = qt_hirectForQRect(mi->rect);
3895 HIThemeMenuItemDrawInfo mdi;
3896 mdi.version = qt_mac_hitheme_version;
3897 mdi.itemType = kThemeMenuItemPlain;
3898 if (!mi->icon.isNull())
3899 mdi.itemType |= kThemeMenuItemHasIcon;
3900 if (mi->menuItemType == QStyleOptionMenuItem::SubMenu)
3901 mdi.itemType |= kThemeMenuItemHierarchical | kThemeMenuItemHierBackground;
3902 else
3903 mdi.itemType |= kThemeMenuItemPopUpBackground;
3904 if (enabled)
3905 mdi.state = kThemeMenuActive;
3906 else
3907 mdi.state = kThemeMenuDisabled;
3908 if (active)
3909 mdi.state |= kThemeMenuSelected;
3910 QRect contentRect;
3911 if (mi->menuItemType == QStyleOptionMenuItem::Separator) {
3912 // First arg should be &menurect, but wacky stuff happens then.
3913 HIThemeDrawMenuSeparator(&itemRect, &itemRect, &mdi,
3914 cg, kHIThemeOrientationNormal);
3915 break;
3916 } else {
3917 HIRect cr;
3918 bool needAlpha = mi->palette.color(QPalette::Button) == Qt::transparent;
3919 if (needAlpha) {
3920 needAlpha = true;
3921 CGContextSaveGState(cg);
3922 CGContextSetAlpha(cg, 0.0);
3923 }
3924 HIThemeDrawMenuItem(&menuRect, &itemRect, &mdi,
3925 cg, kHIThemeOrientationNormal, &cr);
3926 if (needAlpha)
3927 CGContextRestoreGState(cg);
3928 if (ce == CE_MenuEmptyArea)
3929 break;
3930 contentRect = qt_qrectForHIRect(cr);
3931 }
3932 int xpos = contentRect.x() + 18;
3933 int checkcol = maxpmw;
3934 if (!enabled)
3935 p->setPen(mi->palette.text().color());
3936 else if (active)
3937 p->setPen(mi->palette.highlightedText().color());
3938 else
3939 p->setPen(mi->palette.buttonText().color());
3940
3941 if (mi->checked) {
3942 // Use the HIThemeTextInfo foo to draw the check mark correctly, if we do it,
3943 // we somehow need to use a special encoding as it doesn't look right with our
3944 // drawText().
3945 p->save();
3946 CGContextSetShouldAntialias(cg, true);
3947 CGContextSetShouldSmoothFonts(cg, true);
3948 QColor textColor = p->pen().color();
3949 CGFloat colorComp[] = { textColor.redF(), textColor.greenF(),
3950 textColor.blueF(), textColor.alphaF() };
3951 CGContextSetFillColorSpace(cg, QCoreGraphicsPaintEngine::macGenericColorSpace());
3952 CGContextSetFillColor(cg, colorComp);
3953 HIThemeTextInfo tti;
3954 tti.version = qt_mac_hitheme_version;
3955 tti.state = tds;
3956 if (active && enabled)
3957 tti.state = kThemeStatePressed;
3958 switch (widgetSize) {
3959 case QAquaSizeUnknown:
3960 case QAquaSizeLarge:
3961 tti.fontID = kThemeMenuItemMarkFont;
3962 break;
3963 case QAquaSizeSmall:
3964 tti.fontID = kThemeSmallSystemFont;
3965 break;
3966 case QAquaSizeMini:
3967 tti.fontID = kThemeMiniSystemFont;
3968 break;
3969 }
3970 tti.horizontalFlushness = kHIThemeTextHorizontalFlushLeft;
3971 tti.verticalFlushness = kHIThemeTextVerticalFlushCenter;
3972 tti.options = kHIThemeTextBoxOptionNone;
3973 tti.truncationPosition = kHIThemeTextTruncationNone;
3974 tti.truncationMaxLines = 1;
3975 QCFString checkmark;
3976#if 0
3977 if (mi->checkType == QStyleOptionMenuItem::Exclusive)
3978 checkmark = QString(QChar(kDiamondUnicode));
3979 else
3980#endif
3981 checkmark = QString(QChar(kCheckUnicode));
3982 int mw = checkcol + macItemFrame;
3983 int mh = contentRect.height() - 2 * macItemFrame;
3984 int xp = contentRect.x();
3985 xp += macItemFrame;
3986 CGFloat outWidth, outHeight, outBaseline;
3987 HIThemeGetTextDimensions(checkmark, 0, &tti, &outWidth, &outHeight,
3988 &outBaseline);
3989 if (widgetSize == QAquaSizeMini)
3990 outBaseline += 1;
3991 QRect r(xp, contentRect.y(), mw, mh);
3992 r.translate(0, p->fontMetrics().ascent() - int(outBaseline) + 1);
3993 HIRect bounds = qt_hirectForQRect(r);
3994 HIThemeDrawTextBox(checkmark, &bounds, &tti,
3995 cg, kHIThemeOrientationNormal);
3996 p->restore();
3997 }
3998 if (!mi->icon.isNull()) {
3999 QIcon::Mode mode = (mi->state & State_Enabled) ? QIcon::Normal
4000 : QIcon::Disabled;
4001 // Always be normal or disabled to follow the Mac style.
4002 int smallIconSize = proxy()->pixelMetric(PM_SmallIconSize);
4003 QSize iconSize(smallIconSize, smallIconSize);
4004 if (const QComboBox *comboBox = qobject_cast<const QComboBox *>(w)) {
4005 iconSize = comboBox->iconSize();
4006 }
4007 QPixmap pixmap = mi->icon.pixmap(iconSize, mode);
4008 int pixw = pixmap.width();
4009 int pixh = pixmap.height();
4010 QRect cr(xpos, contentRect.y(), checkcol, contentRect.height());
4011 QRect pmr(0, 0, pixw, pixh);
4012 pmr.moveCenter(cr.center());
4013 p->drawPixmap(pmr.topLeft(), pixmap);
4014 xpos += pixw + 6;
4015 }
4016
4017 QString s = mi->text;
4018 if (!s.isEmpty()) {
4019 int t = s.indexOf(QLatin1Char('\t'));
4020 int text_flags = Qt::AlignRight | Qt::AlignVCenter | Qt::TextHideMnemonic
4021 | Qt::TextSingleLine | Qt::AlignAbsolute;
4022 int yPos = contentRect.y();
4023 if (widgetSize == QAquaSizeMini)
4024 yPos += 1;
4025 p->save();
4026 if (t >= 0) {
4027 p->setFont(qt_app_fonts_hash()->value("QMenuItem", p->font()));
4028 int xp = contentRect.right() - tabwidth - macRightBorder
4029 - macItemHMargin - macItemFrame + 1;
4030 p->drawText(xp, yPos, tabwidth, contentRect.height(), text_flags,
4031 s.mid(t + 1));
4032 s = s.left(t);
4033 }
4034
4035 const int xm = macItemFrame + maxpmw + macItemHMargin;
4036 QFont myFont = mi->font;
4037 // myFont may not have any "hard" flags set. We override
4038 // the point size so that when it is resolved against the device, this font will win.
4039 // This is mainly to handle cases where someone sets the font on the window
4040 // and then the combo inherits it and passes it onward. At that point the resolve mask
4041 // is very, very weak. This makes it stonger.
4042 myFont.setPointSizeF(QFontInfo(mi->font).pointSizeF());
4043 p->setFont(myFont);
4044 p->drawText(xpos, yPos, contentRect.width() - xm - tabwidth + 1,
4045 contentRect.height(), text_flags ^ Qt::AlignRight, s);
4046 p->restore();
4047 }
4048 }
4049 break;
4050 case CE_MenuHMargin:
4051 case CE_MenuVMargin:
4052 case CE_MenuTearoff:
4053 case CE_MenuScroller:
4054 if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
4055 p->fillRect(mi->rect, opt->palette.background());
4056
4057 HIRect menuRect = qt_hirectForQRect(mi->menuRect);
4058 HIRect itemRect = qt_hirectForQRect(mi->rect);
4059 HIThemeMenuItemDrawInfo mdi;
4060 mdi.version = qt_mac_hitheme_version;
4061 if (!(opt->state & State_Enabled))
4062 mdi.state = kThemeMenuDisabled;
4063 else if (opt->state & State_Selected)
4064 mdi.state = kThemeMenuSelected;
4065 else
4066 mdi.state = kThemeMenuActive;
4067 if (ce == CE_MenuScroller) {
4068 if (opt->state & State_DownArrow)
4069 mdi.itemType = kThemeMenuItemScrollDownArrow;
4070 else
4071 mdi.itemType = kThemeMenuItemScrollUpArrow;
4072 } else {
4073 mdi.itemType = kThemeMenuItemPlain;
4074 }
4075 HIThemeDrawMenuItem(&menuRect, &itemRect, &mdi,
4076 cg,
4077 kHIThemeOrientationNormal, 0);
4078 if (ce == CE_MenuTearoff) {
4079 p->setPen(QPen(mi->palette.dark().color(), 1, Qt::DashLine));
4080 p->drawLine(mi->rect.x() + 2, mi->rect.y() + mi->rect.height() / 2 - 1,
4081 mi->rect.x() + mi->rect.width() - 4,
4082 mi->rect.y() + mi->rect.height() / 2 - 1);
4083 p->setPen(QPen(mi->palette.light().color(), 1, Qt::DashLine));
4084 p->drawLine(mi->rect.x() + 2, mi->rect.y() + mi->rect.height() / 2,
4085 mi->rect.x() + mi->rect.width() - 4,
4086 mi->rect.y() + mi->rect.height() / 2);
4087 }
4088 }
4089 break;
4090 case CE_MenuBarItem:
4091 if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
4092 HIRect menuRect = qt_hirectForQRect(mi->menuRect);
4093 HIRect itemRect = qt_hirectForQRect(mi->rect);
4094
4095 if ((opt->state & State_Selected) && (opt->state & State_Enabled) && (opt->state & State_Sunken)){
4096 // Draw a selected menu item background:
4097 HIThemeMenuItemDrawInfo mdi;
4098 mdi.version = qt_mac_hitheme_version;
4099 mdi.state = kThemeMenuSelected;
4100 mdi.itemType = kThemeMenuItemPlain;
4101 HIThemeDrawMenuItem(&menuRect, &itemRect, &mdi, cg, kHIThemeOrientationNormal, 0);
4102 } else {
4103 // Draw the toolbar background:
4104 HIThemeMenuBarDrawInfo bdi;
4105 bdi.version = qt_mac_hitheme_version;
4106 bdi.state = kThemeMenuBarNormal;
4107 bdi.attributes = 0;
4108 HIRect hirect = qt_hirectForQRect(mi->rect);
4109 HIThemeDrawMenuBarBackground(&menuRect, &bdi, cg, kHIThemeOrientationNormal);
4110 }
4111
4112 if (!mi->icon.isNull()) {
4113 drawItemPixmap(p, mi->rect,
4114 Qt::AlignCenter | Qt::TextHideMnemonic | Qt::TextDontClip
4115 | Qt::TextSingleLine,
4116 mi->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize),
4117 (mi->state & State_Enabled) ? QIcon::Normal : QIcon::Disabled));
4118 } else {
4119 drawItemText(p, mi->rect,
4120 Qt::AlignCenter | Qt::TextHideMnemonic | Qt::TextDontClip
4121 | Qt::TextSingleLine,
4122 mi->palette, mi->state & State_Enabled,
4123 mi->text, QPalette::ButtonText);
4124 }
4125 }
4126 break;
4127 case CE_MenuBarEmptyArea:
4128 if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
4129 HIThemeMenuBarDrawInfo bdi;
4130 bdi.version = qt_mac_hitheme_version;
4131 bdi.state = kThemeMenuBarNormal;
4132 bdi.attributes = 0;
4133 HIRect hirect = qt_hirectForQRect(mi->rect);
4134 HIThemeDrawMenuBarBackground(&hirect, &bdi, cg,
4135 kHIThemeOrientationNormal);
4136 break;
4137 }
4138 case CE_ProgressBarContents:
4139 if (const QStyleOptionProgressBar *pb = qstyleoption_cast<const QStyleOptionProgressBar *>(opt)) {
4140 HIThemeTrackDrawInfo tdi;
4141 tdi.version = qt_mac_hitheme_version;
4142 tdi.reserved = 0;
4143 bool isIndeterminate = (pb->minimum == 0 && pb->maximum == 0);
4144 bool vertical = false;
4145 bool inverted = false;
4146 if (const QStyleOptionProgressBarV2 *pb2 = qstyleoption_cast<const QStyleOptionProgressBarV2 *>(opt)) {
4147 vertical = (pb2->orientation == Qt::Vertical);
4148 inverted = pb2->invertedAppearance;
4149 }
4150 bool reverse = (!vertical && (pb->direction == Qt::RightToLeft));
4151 if (inverted)
4152 reverse = !reverse;
4153 switch (d->aquaSizeConstrain(opt, w)) {
4154 case QAquaSizeUnknown:
4155 case QAquaSizeLarge:
4156 tdi.kind = !isIndeterminate ? kThemeLargeProgressBar
4157 : kThemeLargeIndeterminateBar;
4158 break;
4159 case QAquaSizeMini:
4160 case QAquaSizeSmall:
4161 tdi.kind = !isIndeterminate ? kThemeProgressBar : kThemeIndeterminateBar;
4162 break;
4163 }
4164 tdi.bounds = qt_hirectForQRect(pb->rect);
4165 tdi.max = pb->maximum;
4166 tdi.min = pb->minimum;
4167 tdi.value = pb->progress;
4168 tdi.attributes = vertical ? 0 : kThemeTrackHorizontal;
4169 tdi.trackInfo.progress.phase = d->progressFrame;
4170 if (!(pb->state & State_Active))
4171 tdi.enableState = kThemeTrackInactive;
4172 else if (!(pb->state & State_Enabled))
4173 tdi.enableState = kThemeTrackDisabled;
4174 else
4175 tdi.enableState = kThemeTrackActive;
4176 HIThemeOrientation drawOrientation = kHIThemeOrientationNormal;
4177 if (reverse) {
4178 if (vertical) {
4179 drawOrientation = kHIThemeOrientationInverted;
4180 } else {
4181 CGContextSaveGState(cg);
4182 CGContextTranslateCTM(cg, pb->rect.width(), 0);
4183 CGContextScaleCTM(cg, -1, 1);
4184 }
4185 }
4186 HIThemeDrawTrack(&tdi, 0, cg, drawOrientation);
4187 if (reverse && !vertical)
4188 CGContextRestoreGState(cg);
4189 }
4190 break;
4191 case CE_ProgressBarLabel:
4192 case CE_ProgressBarGroove:
4193 break;
4194 case CE_SizeGrip: {
4195 if (w && w->testAttribute(Qt::WA_MacOpaqueSizeGrip)) {
4196 HIThemeGrowBoxDrawInfo gdi;
4197 gdi.version = qt_mac_hitheme_version;
4198 gdi.state = tds;
4199 gdi.kind = kHIThemeGrowBoxKindNormal;
4200 gdi.direction = kThemeGrowRight | kThemeGrowDown;
4201 gdi.size = kHIThemeGrowBoxSizeNormal;
4202 HIPoint pt = CGPointMake(opt->rect.x(), opt->rect.y());
4203 HIThemeDrawGrowBox(&pt, &gdi, cg, kHIThemeOrientationNormal);
4204 } else {
4205 // It isn't possible to draw a transparent size grip with the
4206 // native API, so we do it ourselves here.
4207 const bool metal = qt_mac_is_metal(w);
4208 QPen lineColor = metal ? QColor(236, 236, 236) : QColor(82, 82, 82, 192);
4209 QPen metalHighlight = QColor(5, 5, 5, 192);
4210 lineColor.setWidth(1);
4211 p->save();
4212 p->setRenderHint(QPainter::Antialiasing);
4213 p->setPen(lineColor);
4214 const Qt::LayoutDirection layoutDirection = w ? w->layoutDirection() : qApp->layoutDirection();
4215 const int NumLines = metal ? 4 : 3;
4216 for (int l = 0; l < NumLines; ++l) {
4217 const int offset = (l * 4 + (metal ? 2 : 3));
4218 QPoint start, end;
4219 if (layoutDirection == Qt::LeftToRight) {
4220 start = QPoint(opt->rect.width() - offset, opt->rect.height() - 1);
4221 end = QPoint(opt->rect.width() - 1, opt->rect.height() - offset);
4222 } else {
4223 start = QPoint(offset, opt->rect.height() - 1);
4224 end = QPoint(1, opt->rect.height() - offset);
4225 }
4226 p->drawLine(start, end);
4227 if (metal) {
4228 p->setPen(metalHighlight);
4229 p->setRenderHint(QPainter::Antialiasing, false);
4230 p->drawLine(start + QPoint(0, -1), end + QPoint(0, -1));
4231 p->setRenderHint(QPainter::Antialiasing, true);
4232 p->setPen(lineColor);
4233 }
4234 }
4235 p->restore();
4236 }
4237 break;
4238 }
4239 case CE_Splitter: {
4240 HIThemeSplitterDrawInfo sdi;
4241 sdi.version = qt_mac_hitheme_version;
4242 sdi.state = tds;
4243 sdi.adornment = qt_mac_is_metal(w) ? kHIThemeSplitterAdornmentMetal
4244 : kHIThemeSplitterAdornmentNone;
4245 HIRect hirect = qt_hirectForQRect(opt->rect);
4246 HIThemeDrawPaneSplitter(&hirect, &sdi, cg, kHIThemeOrientationNormal);
4247 break; }
4248 case CE_RubberBand:
4249 if (const QStyleOptionRubberBand *rubber = qstyleoption_cast<const QStyleOptionRubberBand *>(opt)) {
4250 QColor fillColor(opt->palette.color(QPalette::Disabled, QPalette::Highlight));
4251 if (!rubber->opaque) {
4252 QColor strokeColor;
4253 // I retrieved these colors from the Carbon-Dev mailing list
4254 strokeColor.setHsvF(0, 0, 0.86, 1.0);
4255 fillColor.setHsvF(0, 0, 0.53, 0.25);
4256 if (opt->rect.width() * opt->rect.height() <= 3) {
4257 p->fillRect(opt->rect, strokeColor);
4258 } else {
4259 QPen oldPen = p->pen();
4260 QBrush oldBrush = p->brush();
4261 QPen pen(strokeColor);
4262 p->setPen(pen);
4263 p->setBrush(fillColor);
4264 p->drawRect(opt->rect.adjusted(0, 0, -1, -1));
4265 p->setPen(oldPen);
4266 p->setBrush(oldBrush);
4267 }
4268 } else {
4269 p->fillRect(opt->rect, fillColor);
4270 }
4271 }
4272 break;
4273 case CE_ToolBar: {
4274 // For unified tool bars, draw nothing.
4275 if (w) {
4276 if (QMainWindow * mainWindow = qobject_cast<QMainWindow *>(w->window())) {
4277 if (mainWindow->unifiedTitleAndToolBarOnMac())
4278 break;
4279 }
4280 }
4281
4282 // draw background gradient
4283 QLinearGradient linearGrad;
4284 if (opt->state & State_Horizontal)
4285 linearGrad = QLinearGradient(0, opt->rect.top(), 0, opt->rect.bottom());
4286 else
4287 linearGrad = QLinearGradient(opt->rect.left(), 0, opt->rect.right(), 0);
4288
4289 linearGrad.setColorAt(0, mainWindowGradientBegin);
4290 linearGrad.setColorAt(1, mainWindowGradientEnd);
4291 p->fillRect(opt->rect, linearGrad);
4292
4293 p->save();
4294 if (opt->state & State_Horizontal) {
4295 p->setPen(mainWindowGradientBegin.lighter(114));
4296 p->drawLine(opt->rect.topLeft(), opt->rect.topRight());
4297 p->setPen(mainWindowGradientEnd.darker(114));
4298 p->drawLine(opt->rect.bottomLeft(), opt->rect.bottomRight());
4299
4300 } else {
4301 p->setPen(mainWindowGradientBegin.lighter(114));
4302 p->drawLine(opt->rect.topLeft(), opt->rect.bottomLeft());
4303 p->setPen(mainWindowGradientEnd.darker(114));
4304 p->drawLine(opt->rect.topRight(), opt->rect.bottomRight());
4305 }
4306 p->restore();
4307
4308
4309 } break;
4310 default:
4311 QWindowsStyle::drawControl(ce, opt, p, w);
4312 break;
4313 }
4314}
4315
4316static void setLayoutItemMargins(int left, int top, int right, int bottom, QRect *rect, Qt::LayoutDirection dir)
4317{
4318 if (dir == Qt::RightToLeft) {
4319 rect->adjust(-right, top, -left, bottom);
4320 } else {
4321 rect->adjust(left, top, right, bottom);
4322 }
4323}
4324
4325QRect QMacStyle::subElementRect(SubElement sr, const QStyleOption *opt,
4326 const QWidget *widget) const
4327{
4328 QRect rect;
4329 int controlSize = getControlSize(opt, widget);
4330
4331 switch (sr) {
4332 case SE_ToolBoxTabContents:
4333 rect = QCommonStyle::subElementRect(sr, opt, widget);
4334 break;
4335 case SE_PushButtonContents:
4336 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
4337 // Unlike Carbon, we want the button to always be drawn inside its bounds.
4338 // Therefore, the button is a bit smaller, so that even if it got focus,
4339 // the focus 'shadow' will be inside. Adjust the content rect likewise.
4340 HIThemeButtonDrawInfo bdi;
4341 d->initHIThemePushButton(btn, widget, d->getDrawState(opt->state), &bdi);
4342 HIRect contentRect = d->pushButtonContentBounds(btn, &bdi);
4343 rect = qt_qrectForHIRect(contentRect);
4344 }
4345 break;
4346 case SE_HeaderLabel:
4347 if (qstyleoption_cast<const QStyleOptionHeader *>(opt)) {
4348 rect = QWindowsStyle::subElementRect(sr, opt, widget);
4349 if (widget && widget->height() <= qt_mac_aqua_get_metric(kThemeMetricListHeaderHeight)){
4350 // We need to allow the text a bit more space when the header is as
4351 // small as kThemeMetricListHeaderHeight, otherwise it gets clipped:
4352 rect.setY(0);
4353 rect.setHeight(widget->height());
4354 }
4355 }
4356 break;
4357 case SE_ProgressBarGroove:
4358 case SE_ProgressBarLabel:
4359 break;
4360 case SE_ProgressBarContents:
4361 rect = opt->rect;
4362 break;
4363 case SE_TreeViewDisclosureItem: {
4364 HIRect inRect = CGRectMake(opt->rect.x(), opt->rect.y(),
4365 opt->rect.width(), opt->rect.height());
4366 HIThemeButtonDrawInfo bdi;
4367 bdi.version = qt_mac_hitheme_version;
4368 bdi.state = kThemeStateActive;
4369 bdi.kind = kThemeDisclosureButton;
4370 bdi.value = kThemeDisclosureRight;
4371 bdi.adornment = kThemeAdornmentNone;
4372 HIRect contentRect;
4373 HIThemeGetButtonContentBounds(&inRect, &bdi, &contentRect);
4374 QCFType<HIShapeRef> shape;
4375 HIRect outRect;
4376 HIThemeGetButtonShape(&inRect, &bdi, &shape);
4377 ptrHIShapeGetBounds(shape, &outRect);
4378 rect = QRect(int(outRect.origin.x), int(outRect.origin.y),
4379 int(contentRect.origin.x - outRect.origin.x), int(outRect.size.height));
4380 break;
4381 }
4382 case SE_TabWidgetLeftCorner:
4383 if (const QStyleOptionTabWidgetFrame *twf
4384 = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
4385 switch (twf->shape) {
4386 case QTabBar::RoundedNorth:
4387 case QTabBar::TriangularNorth:
4388 rect = QRect(QPoint(0, 0), twf->leftCornerWidgetSize);
4389 break;
4390 case QTabBar::RoundedSouth:
4391 case QTabBar::TriangularSouth:
4392 rect = QRect(QPoint(0, twf->rect.height() - twf->leftCornerWidgetSize.height()),
4393 twf->leftCornerWidgetSize);
4394 break;
4395 default:
4396 break;
4397 }
4398 rect = visualRect(twf->direction, twf->rect, rect);
4399 }
4400 break;
4401 case SE_TabWidgetRightCorner:
4402 if (const QStyleOptionTabWidgetFrame *twf
4403 = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
4404 switch (twf->shape) {
4405 case QTabBar::RoundedNorth:
4406 case QTabBar::TriangularNorth:
4407 rect = QRect(QPoint(twf->rect.width() - twf->rightCornerWidgetSize.width(), 0),
4408 twf->rightCornerWidgetSize);
4409 break;
4410 case QTabBar::RoundedSouth:
4411 case QTabBar::TriangularSouth:
4412 rect = QRect(QPoint(twf->rect.width() - twf->rightCornerWidgetSize.width(),
4413 twf->rect.height() - twf->rightCornerWidgetSize.height()),
4414 twf->rightCornerWidgetSize);
4415 break;
4416 default:
4417 break;
4418 }
4419 rect = visualRect(twf->direction, twf->rect, rect);
4420 }
4421 break;
4422 case SE_TabWidgetTabContents:
4423 rect = QWindowsStyle::subElementRect(sr, opt, widget);
4424 if (const QStyleOptionTabWidgetFrame *twf
4425 = qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
4426 if (twf->lineWidth != 0) {
4427 switch (getTabDirection(twf->shape)) {
4428 case kThemeTabNorth:
4429 rect.adjust(+1, +14, -1, -1);
4430 break;
4431 case kThemeTabSouth:
4432 rect.adjust(+1, +1, -1, -14);
4433 break;
4434 case kThemeTabWest:
4435 rect.adjust(+14, +1, -1, -1);
4436 break;
4437 case kThemeTabEast:
4438 rect.adjust(+1, +1, -14, -1);
4439 }
4440 }
4441 }
4442 break;
4443 case SE_LineEditContents:
4444 rect = QWindowsStyle::subElementRect(sr, opt, widget);
4445 if(widget->parentWidget() && qobject_cast<const QComboBox*>(widget->parentWidget()))
4446 rect.adjust(-1, -2, 0, 0);
4447 else
4448 rect.adjust(-1, 0, 0, +1);
4449 break;
4450 case SE_CheckBoxLayoutItem:
4451 rect = opt->rect;
4452 if (controlSize == QAquaSizeLarge) {
4453 setLayoutItemMargins(+2, +3, -9, -4, &rect, opt->direction);
4454 } else if (controlSize == QAquaSizeSmall) {
4455 setLayoutItemMargins(+1, +5, 0 /* fix */, -6, &rect, opt->direction);
4456 } else {
4457 setLayoutItemMargins(0, +7, 0 /* fix */, -6, &rect, opt->direction);
4458 }
4459 break;
4460 case SE_ComboBoxLayoutItem:
4461 if (widget && qobject_cast<QToolBar *>(widget->parentWidget())) {
4462 // Do nothing, because QToolbar needs the entire widget rect.
4463 // Otherwise it will be clipped. Equivalent to
4464 // widget->setAttribute(Qt::WA_LayoutUsesWidgetRect), but without
4465 // all the hassle.
4466 } else {
4467 rect = opt->rect;
4468 if (controlSize == QAquaSizeLarge) {
4469 rect.adjust(+3, +2, -3, -4);
4470 } else if (controlSize == QAquaSizeSmall) {
4471 setLayoutItemMargins(+2, +1, -3, -4, &rect, opt->direction);
4472 } else {
4473 setLayoutItemMargins(+1, 0, -2, 0, &rect, opt->direction);
4474 }
4475 }
4476 break;
4477 case SE_LabelLayoutItem:
4478 rect = opt->rect;
4479 setLayoutItemMargins(+1, 0 /* SHOULD be -1, done for alignment */, 0, 0 /* SHOULD be -1, done for alignment */, &rect, opt->direction);
4480 break;
4481 case SE_ProgressBarLayoutItem: {
4482 rect = opt->rect;
4483 int bottom = SIZE(3, 8, 8);
4484 if (opt->state & State_Horizontal) {
4485 rect.adjust(0, +1, 0, -bottom);
4486 } else {
4487 setLayoutItemMargins(+1, 0, -bottom, 0, &rect, opt->direction);
4488 }
4489 break;
4490 }
4491 case SE_PushButtonLayoutItem:
4492 if (const QStyleOptionButton *buttonOpt
4493 = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
4494 if ((buttonOpt->features & QStyleOptionButton::Flat))
4495 break; // leave rect alone
4496 }
4497 rect = opt->rect;
4498 if (controlSize == QAquaSizeLarge) {
4499 rect.adjust(+6, +4, -6, -8);
4500 } else if (controlSize == QAquaSizeSmall) {
4501 rect.adjust(+5, +4, -5, -6);
4502 } else {
4503 rect.adjust(+1, 0, -1, -2);
4504 }
4505 break;
4506 case SE_RadioButtonLayoutItem:
4507 rect = opt->rect;
4508 if (controlSize == QAquaSizeLarge) {
4509 setLayoutItemMargins(+2, +2 /* SHOULD BE +3, done for alignment */,
4510 0, -4 /* SHOULD BE -3, done for alignment */, &rect, opt->direction);
4511 } else if (controlSize == QAquaSizeSmall) {
4512 rect.adjust(0, +6, 0 /* fix */, -5);
4513 } else {
4514 rect.adjust(0, +6, 0 /* fix */, -7);
4515 }
4516 break;
4517 case SE_SliderLayoutItem:
4518 if (const QStyleOptionSlider *sliderOpt
4519 = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
4520 rect = opt->rect;
4521 if (sliderOpt->tickPosition == QSlider::NoTicks) {
4522 int above = SIZE(3, 0, 2);
4523 int below = SIZE(4, 3, 0);
4524 if (sliderOpt->orientation == Qt::Horizontal) {
4525 rect.adjust(0, +above, 0, -below);
4526 } else {
4527 rect.adjust(+above, 0, -below, 0); //### Seems that QSlider flip the position of the ticks in reverse mode.
4528 }
4529 } else if (sliderOpt->tickPosition == QSlider::TicksAbove) {
4530 int below = SIZE(3, 2, 0);
4531 if (sliderOpt->orientation == Qt::Horizontal) {
4532 rect.setHeight(rect.height() - below);
4533 } else {
4534 rect.setWidth(rect.width() - below);
4535 }
4536 } else if (sliderOpt->tickPosition == QSlider::TicksBelow) {
4537 int above = SIZE(3, 2, 0);
4538 if (sliderOpt->orientation == Qt::Horizontal) {
4539 rect.setTop(rect.top() + above);
4540 } else {
4541 rect.setLeft(rect.left() + above);
4542 }
4543 }
4544 }
4545 break;
4546 case SE_FrameLayoutItem:
4547 // hack because QStyleOptionFrameV2 doesn't have a frameStyle member
4548 if (const QFrame *frame = qobject_cast<const QFrame *>(widget)) {
4549 rect = opt->rect;
4550 switch (frame->frameStyle() & QFrame::Shape_Mask) {
4551 case QFrame::HLine:
4552 rect.adjust(0, +1, 0, -1);
4553 break;
4554 case QFrame::VLine:
4555 rect.adjust(+1, 0, -1, 0);
4556 break;
4557 default:
4558 ;
4559 }
4560 }
4561 break;
4562 case SE_GroupBoxLayoutItem:
4563 rect = opt->rect;
4564 if (const QStyleOptionGroupBox *groupBoxOpt =
4565 qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
4566 /*
4567 AHIG is very inconsistent when it comes to group boxes.
4568 Basically, we make sure that (non-checkable) group boxes
4569 and tab widgets look good when laid out side by side.
4570 */
4571 if (groupBoxOpt->subControls & (QStyle::SC_GroupBoxCheckBox
4572 | QStyle::SC_GroupBoxLabel)) {
4573 int delta;
4574 if (groupBoxOpt->subControls & QStyle::SC_GroupBoxCheckBox) {
4575 delta = SIZE(8, 4, 4); // guess
4576 } else {
4577 delta = SIZE(15, 12, 12); // guess
4578 }
4579 rect.setTop(rect.top() + delta);
4580 }
4581 }
4582 rect.setBottom(rect.bottom() - 1);
4583 break;
4584 case SE_TabWidgetLayoutItem:
4585 if (const QStyleOptionTabWidgetFrame *tabWidgetOpt =
4586 qstyleoption_cast<const QStyleOptionTabWidgetFrame *>(opt)) {
4587 /*
4588 AHIG specifies "12 or 14" as the distance from the window
4589 edge. We choose 14 and since the default top margin is 20,
4590 the overlap is 6.
4591 */
4592 rect = tabWidgetOpt->rect;
4593 if (tabWidgetOpt->shape == QTabBar::RoundedNorth)
4594 rect.setTop(rect.top() + SIZE(6 /* AHIG */, 3 /* guess */, 2 /* AHIG */));
4595 }
4596 break;
4597 default:
4598 rect = QWindowsStyle::subElementRect(sr, opt, widget);
4599 break;
4600 }
4601 return rect;
4602}
4603
4604static inline void drawToolbarButtonArrow(const QRect &toolButtonRect, ThemeDrawState tds, CGContextRef cg)
4605{
4606 QRect arrowRect = QRect(toolButtonRect.right() - 9, toolButtonRect.bottom() - 9, 7, 5);
4607 HIThemePopupArrowDrawInfo padi;
4608 padi.version = qt_mac_hitheme_version;
4609 padi.state = tds;
4610 padi.orientation = kThemeArrowDown;
4611 padi.size = kThemeArrow7pt;
4612 HIRect hirect = qt_hirectForQRect(arrowRect);
4613 HIThemeDrawPopupArrow(&hirect, &padi, cg, kHIThemeOrientationNormal);
4614}
4615
4616void QMacStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *p,
4617 const QWidget *widget) const
4618{
4619 ThemeDrawState tds = d->getDrawState(opt->state);
4620 QMacCGContext cg(p);
4621 switch (cc) {
4622 case CC_Slider:
4623 case CC_ScrollBar:
4624 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
4625 HIThemeTrackDrawInfo tdi;
4626 d->getSliderInfo(cc, slider, &tdi, widget);
4627 if (slider->state & State_Sunken) {
4628 if (cc == CC_Slider) {
4629 if (slider->activeSubControls == SC_SliderHandle)
4630 tdi.trackInfo.slider.pressState = kThemeThumbPressed;
4631 else if (slider->activeSubControls == SC_SliderGroove)
4632 tdi.trackInfo.slider.pressState = kThemeLeftTrackPressed;
4633 } else {
4634 if (slider->activeSubControls == SC_ScrollBarSubLine
4635 || slider->activeSubControls == SC_ScrollBarAddLine) {
4636 // This test looks complex but it basically boils down
4637 // to the following: The "RTL look" on the mac also
4638 // changed the directions of the controls, that's not
4639 // what people expect (an arrow is an arrow), so we
4640 // kind of fake and say the opposite button is hit.
4641 // This works great, up until 10.4 which broke the
4642 // scroll bars, so I also have actually do something
4643 // similar when I have an upside down scroll bar
4644 // because on Tiger I only "fake" the reverse stuff.
4645 bool reverseHorizontal = (slider->direction == Qt::RightToLeft
4646 && slider->orientation == Qt::Horizontal);
4647 if ((reverseHorizontal
4648 && slider->activeSubControls == SC_ScrollBarAddLine)
4649 || (!reverseHorizontal
4650 && slider->activeSubControls == SC_ScrollBarSubLine)) {
4651 tdi.trackInfo.scrollbar.pressState = kThemeRightInsideArrowPressed
4652 | kThemeLeftOutsideArrowPressed;
4653 } else {
4654 tdi.trackInfo.scrollbar.pressState = kThemeLeftInsideArrowPressed
4655 | kThemeRightOutsideArrowPressed;
4656 }
4657 } else if (slider->activeSubControls == SC_ScrollBarAddPage) {
4658 tdi.trackInfo.scrollbar.pressState = kThemeRightTrackPressed;
4659 } else if (slider->activeSubControls == SC_ScrollBarSubPage) {
4660 tdi.trackInfo.scrollbar.pressState = kThemeLeftTrackPressed;
4661 } else if (slider->activeSubControls == SC_ScrollBarSlider) {
4662 tdi.trackInfo.scrollbar.pressState = kThemeThumbPressed;
4663 }
4664 }
4665 }
4666 HIRect macRect;
4667 bool tracking = slider->sliderPosition == slider->sliderValue;
4668 if (!tracking) {
4669 // Small optimization, the same as q->subControlRect
4670 QCFType<HIShapeRef> shape;
4671 HIThemeGetTrackThumbShape(&tdi, &shape);
4672 ptrHIShapeGetBounds(shape, &macRect);
4673 tdi.value = slider->sliderValue;
4674 }
4675
4676 // Remove controls from the scroll bar if it is to short to draw them correctly.
4677 // This is done in two stages: first the thumb indicator is removed when it is
4678 // no longer possible to move it, second the up/down buttons are removed when
4679 // there is not enough space for them.
4680 if (cc == CC_ScrollBar) {
4681 const int scrollBarLength = (slider->orientation == Qt::Horizontal)
4682 ? slider->rect.width() : slider->rect.height();
4683 const QMacStyle::WidgetSizePolicy sizePolicy = widgetSizePolicy(widget);
4684 if (scrollBarLength < scrollButtonsCutoffSize(thumbIndicatorCutoff, sizePolicy))
4685 tdi.attributes &= ~kThemeTrackShowThumb;
4686 if (scrollBarLength < scrollButtonsCutoffSize(scrollButtonsCutoff, sizePolicy))
4687 tdi.enableState = kThemeTrackNothingToScroll;
4688 }
4689
4690 HIThemeDrawTrack(&tdi, tracking ? 0 : &macRect, cg,
4691 kHIThemeOrientationNormal);
4692 if (cc == CC_Slider && slider->subControls & SC_SliderTickmarks) {
4693 if (qt_mac_is_metal(widget)) {
4694 if (tdi.enableState == kThemeTrackInactive)
4695 tdi.enableState = kThemeTrackActive; // Looks more Cocoa-like
4696 }
4697 int interval = slider->tickInterval;
4698 if (interval == 0) {
4699 interval = slider->pageStep;
4700 if (interval == 0)
4701 interval = slider->singleStep;
4702 if (interval == 0)
4703 interval = 1;
4704 }
4705 int numMarks = 1 + ((slider->maximum - slider->minimum) / interval);
4706
4707 if (tdi.trackInfo.slider.thumbDir == kThemeThumbPlain) {
4708 // They asked for both, so we'll give it to them.
4709 tdi.trackInfo.slider.thumbDir = kThemeThumbDownward;
4710 HIThemeDrawTrackTickMarks(&tdi, numMarks,
4711 cg,
4712 kHIThemeOrientationNormal);
4713 tdi.trackInfo.slider.thumbDir = kThemeThumbUpward;
4714 HIThemeDrawTrackTickMarks(&tdi, numMarks,
4715 cg,
4716 kHIThemeOrientationNormal);
4717 } else {
4718 HIThemeDrawTrackTickMarks(&tdi, numMarks,
4719 cg,
4720 kHIThemeOrientationNormal);
4721
4722 }
4723 }
4724 }
4725 break;
4726 case CC_Q3ListView:
4727 if (const QStyleOptionQ3ListView *lv = qstyleoption_cast<const QStyleOptionQ3ListView *>(opt)) {
4728 if (lv->subControls & SC_Q3ListView)
4729 QWindowsStyle::drawComplexControl(cc, lv, p, widget);
4730 if (lv->subControls & (SC_Q3ListViewBranch | SC_Q3ListViewExpand)) {
4731 int y = lv->rect.y();
4732 int h = lv->rect.height();
4733 int x = lv->rect.right() - 10;
4734 for (int i = 1; i < lv->items.size() && y < h; ++i) {
4735 QStyleOptionQ3ListViewItem item = lv->items.at(i);
4736 if (y + item.height > 0 && (item.childCount > 0
4737 || (item.features & (QStyleOptionQ3ListViewItem::Expandable
4738 | QStyleOptionQ3ListViewItem::Visible))
4739 == (QStyleOptionQ3ListViewItem::Expandable
4740 | QStyleOptionQ3ListViewItem::Visible))) {
4741 QStyleOption treeOpt(0);
4742 treeOpt.rect.setRect(x, y + item.height / 2 - 4, 9, 9);
4743 treeOpt.palette = lv->palette;
4744 treeOpt.state = lv->state;
4745 treeOpt.state |= State_Children;
4746 if (item.state & State_Open)
4747 treeOpt.state |= State_Open;
4748 proxy()->drawPrimitive(PE_IndicatorBranch, &treeOpt, p, widget);
4749 }
4750 y += item.totalHeight;
4751 }
4752 }
4753 }
4754 break;
4755 case CC_SpinBox:
4756 if (const QStyleOptionSpinBox *sb = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
4757 QStyleOptionSpinBox newSB = *sb;
4758 if (sb->frame && (sb->subControls & SC_SpinBoxFrame)) {
4759 SInt32 frame_size;
4760 GetThemeMetric(kThemeMetricEditTextFrameOutset, &frame_size);
4761
4762 QRect lineeditRect = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxEditField, widget);
4763 lineeditRect.adjust(-frame_size, -frame_size, +frame_size, +frame_size);
4764
4765 HIThemeFrameDrawInfo fdi;
4766 fdi.version = qt_mac_hitheme_version;
4767 fdi.state = kThemeStateInactive;
4768 fdi.kind = kHIThemeFrameTextFieldSquare;
4769 fdi.isFocused = false;
4770 HIRect hirect = qt_hirectForQRect(lineeditRect);
4771 HIThemeDrawFrame(&hirect, &fdi, cg, kHIThemeOrientationNormal);
4772 }
4773 if (sb->subControls & (SC_SpinBoxUp | SC_SpinBoxDown)) {
4774 HIThemeButtonDrawInfo bdi;
4775 bdi.version = qt_mac_hitheme_version;
4776 QAquaWidgetSize aquaSize = d->aquaSizeConstrain(opt, widget);
4777 switch (aquaSize) {
4778 case QAquaSizeUnknown:
4779 case QAquaSizeLarge:
4780 bdi.kind = kThemeIncDecButton;
4781 break;
4782 case QAquaSizeMini:
4783 bdi.kind = kThemeIncDecButtonMini;
4784 break;
4785 case QAquaSizeSmall:
4786 bdi.kind = kThemeIncDecButtonSmall;
4787 break;
4788 }
4789 if (!(sb->stepEnabled & (QAbstractSpinBox::StepUpEnabled
4790 | QAbstractSpinBox::StepDownEnabled)))
4791 tds = kThemeStateUnavailable;
4792 if (sb->activeSubControls == SC_SpinBoxDown
4793 && (sb->state & State_Sunken))
4794 tds = kThemeStatePressedDown;
4795 else if (sb->activeSubControls == SC_SpinBoxUp
4796 && (sb->state & State_Sunken))
4797 tds = kThemeStatePressedUp;
4798 bdi.state = tds;
4799 if (!(sb->state & State_Active)
4800 && sb->palette.currentColorGroup() == QPalette::Active
4801 && tds == kThemeStateInactive)
4802 bdi.state = kThemeStateActive;
4803 bdi.value = kThemeButtonOff;
4804 bdi.adornment = kThemeAdornmentNone;
4805
4806 QRect updown = proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxUp, widget);
4807
4808 updown |= proxy()->subControlRect(CC_SpinBox, sb, SC_SpinBoxDown, widget);
4809 HIRect newRect = qt_hirectForQRect(updown);
4810 QRect off_rct;
4811 HIRect outRect;
4812 HIThemeGetButtonBackgroundBounds(&newRect, &bdi, &outRect);
4813 off_rct.setRect(int(newRect.origin.x - outRect.origin.x),
4814 int(newRect.origin.y - outRect.origin.y),
4815 int(outRect.size.width - newRect.size.width),
4816 int(outRect.size.height - newRect.size.height));
4817
4818 newRect = qt_hirectForQRect(updown, off_rct);
4819 HIThemeDrawButton(&newRect, &bdi, cg, kHIThemeOrientationNormal, 0);
4820 }
4821 }
4822 break;
4823 case CC_ComboBox:
4824 if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)){
4825 HIThemeButtonDrawInfo bdi;
4826 d->initComboboxBdi(combo, &bdi, widget, d->getDrawState(opt->state));
4827 bool drawColorless = combo->palette.currentColorGroup() == QPalette::Active && tds == kThemeStateInactive;
4828 if (!drawColorless)
4829 QMacStylePrivate::drawCombobox(qt_hirectForQRect(combo->rect), bdi, p);
4830 else
4831 d->drawColorlessButton(qt_hirectForQRect(combo->rect), &bdi, p, opt);
4832 }
4833 break;
4834 case CC_TitleBar:
4835 if (const QStyleOptionTitleBar *titlebar
4836 = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
4837 if (titlebar->state & State_Active) {
4838 if (titlebar->titleBarState & State_Active)
4839 tds = kThemeStateActive;
4840 else
4841 tds = kThemeStateInactive;
4842 } else {
4843 tds = kThemeStateInactive;
4844 }
4845
4846 HIThemeWindowDrawInfo wdi;
4847 wdi.version = qt_mac_hitheme_version;
4848 wdi.state = tds;
4849 wdi.windowType = QtWinType;
4850 wdi.titleHeight = titlebar->rect.height();
4851 wdi.titleWidth = titlebar->rect.width();
4852 wdi.attributes = kThemeWindowHasTitleText;
4853 // It seems HIThemeDrawTitleBarWidget is not able to draw a dirty
4854 // close button, so use HIThemeDrawWindowFrame instead.
4855 if (widget && widget->isWindowModified() && titlebar->subControls & SC_TitleBarCloseButton)
4856 wdi.attributes |= kThemeWindowHasCloseBox | kThemeWindowHasDirty;
4857
4858 HIRect titleBarRect;
4859 HIRect tmpRect = qt_hirectForQRect(titlebar->rect);
4860 {
4861 QCFType<HIShapeRef> titleRegion;
4862 QRect newr = titlebar->rect.adjusted(0, 0, 2, 0);
4863 HIThemeGetWindowShape(&tmpRect, &wdi, kWindowTitleBarRgn, &titleRegion);
4864 ptrHIShapeGetBounds(titleRegion, &tmpRect);
4865 newr.translate(newr.x() - int(tmpRect.origin.x), newr.y() - int(tmpRect.origin.y));
4866 titleBarRect = qt_hirectForQRect(newr);
4867 }
4868 HIThemeDrawWindowFrame(&titleBarRect, &wdi, cg, kHIThemeOrientationNormal, 0);
4869 if (titlebar->subControls & (SC_TitleBarCloseButton
4870 | SC_TitleBarMaxButton
4871 | SC_TitleBarMinButton
4872 | SC_TitleBarNormalButton)) {
4873 HIThemeWindowWidgetDrawInfo wwdi;
4874 wwdi.version = qt_mac_hitheme_version;
4875 wwdi.widgetState = tds;
4876 if (titlebar->state & State_MouseOver)
4877 wwdi.widgetState = kThemeStateRollover;
4878 wwdi.windowType = QtWinType;
4879 wwdi.attributes = wdi.attributes | kThemeWindowHasFullZoom | kThemeWindowHasCloseBox | kThemeWindowHasCollapseBox;
4880 wwdi.windowState = wdi.state;
4881 wwdi.titleHeight = wdi.titleHeight;
4882 wwdi.titleWidth = wdi.titleWidth;
4883 ThemeDrawState savedControlState = wwdi.widgetState;
4884 uint sc = SC_TitleBarMinButton;
4885 ThemeTitleBarWidget tbw = kThemeWidgetCollapseBox;
4886 bool active = titlebar->state & State_Active;
4887 if (qMacVersion() < QSysInfo::MV_10_6) {
4888 int border = 2;
4889 titleBarRect.origin.x += border;
4890 titleBarRect.origin.y -= border;
4891 }
4892
4893 while (sc <= SC_TitleBarCloseButton) {
4894 if (sc & titlebar->subControls) {
4895 uint tmp = sc;
4896 wwdi.widgetState = savedControlState;
4897 wwdi.widgetType = tbw;
4898 if (sc == SC_TitleBarMinButton)
4899 tmp |= SC_TitleBarNormalButton;
4900 if (active && (titlebar->activeSubControls & tmp)
4901 && (titlebar->state & State_Sunken))
4902 wwdi.widgetState = kThemeStatePressed;
4903 // Draw all sub controllers except the dirty close button
4904 // (it is already handled by HIThemeDrawWindowFrame).
4905 if (!(widget && widget->isWindowModified() && tbw == kThemeWidgetCloseBox)) {
4906 HIThemeDrawTitleBarWidget(&titleBarRect, &wwdi, cg, kHIThemeOrientationNormal);
4907 p->paintEngine()->syncState();
4908 }
4909 }
4910 sc = sc << 1;
4911 tbw = tbw >> 1;
4912 }
4913 }
4914 p->paintEngine()->syncState();
4915 if (titlebar->subControls & SC_TitleBarLabel) {
4916 int iw = 0;
4917 if (!titlebar->icon.isNull()) {
4918 QCFType<HIShapeRef> titleRegion2;
4919 HIThemeGetWindowShape(&titleBarRect, &wdi, kWindowTitleProxyIconRgn,
4920 &titleRegion2);
4921 ptrHIShapeGetBounds(titleRegion2, &tmpRect);
4922 if (tmpRect.size.width != 1) {
4923 int iconExtent = proxy()->pixelMetric(PM_SmallIconSize);
4924 iw = titlebar->icon.actualSize(QSize(iconExtent, iconExtent)).width();
4925 }
4926 }
4927 if (!titlebar->text.isEmpty()) {
4928 p->save();
4929 QCFType<HIShapeRef> titleRegion3;
4930 HIThemeGetWindowShape(&titleBarRect, &wdi, kWindowTitleTextRgn, &titleRegion3);
4931 ptrHIShapeGetBounds(titleRegion3, &tmpRect);
4932 p->setClipRect(qt_qrectForHIRect(tmpRect));
4933 QRect br = p->clipRegion().boundingRect();
4934 int x = br.x(),
4935 y = br.y() + (titlebar->rect.height() / 2 - p->fontMetrics().height() / 2);
4936 if (br.width() <= (p->fontMetrics().width(titlebar->text) + iw * 2))
4937 x += iw;
4938 else
4939 x += br.width() / 2 - p->fontMetrics().width(titlebar->text) / 2;
4940 if (iw)
4941 p->drawPixmap(x - iw, y,
4942 titlebar->icon.pixmap(proxy()->pixelMetric(PM_SmallIconSize), QIcon::Normal));
4943 drawItemText(p, br, Qt::AlignCenter, opt->palette, tds == kThemeStateActive,
4944 titlebar->text, QPalette::Text);
4945 p->restore();
4946 }
4947 }
4948 }
4949 break;
4950 case CC_GroupBox:
4951 if (const QStyleOptionGroupBox *groupBox
4952 = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
4953
4954 QStyleOptionGroupBox groupBoxCopy(*groupBox);
4955 if ((widget && !widget->testAttribute(Qt::WA_SetFont))
4956 && QApplication::desktopSettingsAware())
4957 groupBoxCopy.subControls = groupBoxCopy.subControls & ~SC_GroupBoxLabel;
4958 QWindowsStyle::drawComplexControl(cc, &groupBoxCopy, p, widget);
4959 if (groupBoxCopy.subControls != groupBox->subControls) {
4960 bool checkable = groupBox->subControls & SC_GroupBoxCheckBox;
4961 p->save();
4962 CGContextSetShouldAntialias(cg, true);
4963 CGContextSetShouldSmoothFonts(cg, true);
4964 HIThemeTextInfo tti;
4965 tti.version = qt_mac_hitheme_version;
4966 tti.state = tds;
4967 QColor textColor = groupBox->palette.windowText().color();
4968 CGFloat colorComp[] = { textColor.redF(), textColor.greenF(),
4969 textColor.blueF(), textColor.alphaF() };
4970 CGContextSetFillColorSpace(cg, QCoreGraphicsPaintEngine::macGenericColorSpace());
4971 CGContextSetFillColor(cg, colorComp);
4972 tti.fontID = checkable ? kThemeSystemFont : kThemeSmallSystemFont;
4973 tti.horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
4974 tti.verticalFlushness = kHIThemeTextVerticalFlushCenter;
4975 tti.options = kHIThemeTextBoxOptionNone;
4976 tti.truncationPosition = kHIThemeTextTruncationNone;
4977 tti.truncationMaxLines = 1 + groupBox->text.count(QLatin1Char('\n'));
4978 QCFString groupText = qt_mac_removeMnemonics(groupBox->text);
4979 QRect r = proxy()->subControlRect(CC_GroupBox, groupBox, SC_GroupBoxLabel, widget);
4980 HIRect bounds = qt_hirectForQRect(r);
4981 HIThemeDrawTextBox(groupText, &bounds, &tti, cg, kHIThemeOrientationNormal);
4982 p->restore();
4983 }
4984 }
4985 break;
4986 case CC_ToolButton:
4987 if (const QStyleOptionToolButton *tb
4988 = qstyleoption_cast<const QStyleOptionToolButton *>(opt)) {
4989 if (widget && qobject_cast<QToolBar *>(widget->parentWidget())) {
4990 if (tb->subControls & SC_ToolButtonMenu) {
4991 QStyleOption arrowOpt(0);
4992 arrowOpt.rect = proxy()->subControlRect(cc, tb, SC_ToolButtonMenu, widget);
4993 arrowOpt.rect.setY(arrowOpt.rect.y() + arrowOpt.rect.height() / 2);
4994 arrowOpt.rect.setHeight(arrowOpt.rect.height() / 2);
4995 arrowOpt.state = tb->state;
4996 arrowOpt.palette = tb->palette;
4997 proxy()->drawPrimitive(PE_IndicatorArrowDown, &arrowOpt, p, widget);
4998 } else if ((tb->features & QStyleOptionToolButton::HasMenu)
4999 && (tb->toolButtonStyle != Qt::ToolButtonTextOnly && !tb->icon.isNull())) {
5000 drawToolbarButtonArrow(tb->rect, tds, cg);
5001 }
5002 if (tb->state & State_On) {
5003 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5) {
5004 static QPixmap pm(QLatin1String(":/trolltech/mac/style/images/leopard-unified-toolbar-on.png"));
5005 p->setRenderHint(QPainter::SmoothPixmapTransform);
5006 QStyleHelper::drawBorderPixmap(pm, p, tb->rect, 2, 2, 2, 2);
5007 } else {
5008 QPen oldPen = p->pen();
5009 p->setPen(QColor(0, 0, 0, 0x3a));
5010 p->fillRect(tb->rect.adjusted(1, 1, -1, -1), QColor(0, 0, 0, 0x12));
5011 p->drawLine(tb->rect.left() + 1, tb->rect.top(),
5012 tb->rect.right() - 1, tb->rect.top());
5013 p->drawLine(tb->rect.left() + 1, tb->rect.bottom(),
5014 tb->rect.right() - 1, tb->rect.bottom());
5015 p->drawLine(tb->rect.topLeft(), tb->rect.bottomLeft());
5016 p->drawLine(tb->rect.topRight(), tb->rect.bottomRight());
5017 p->setPen(oldPen);
5018 }
5019 }
5020 proxy()->drawControl(CE_ToolButtonLabel, opt, p, widget);
5021 } else {
5022 ThemeButtonKind bkind = kThemeBevelButton;
5023 switch (d->aquaSizeConstrain(opt, widget)) {
5024 case QAquaSizeUnknown:
5025 case QAquaSizeLarge:
5026 bkind = kThemeBevelButton;
5027 break;
5028 case QAquaSizeMini:
5029 case QAquaSizeSmall:
5030 bkind = kThemeSmallBevelButton;
5031 break;
5032 }
5033
5034 QRect button, menuarea;
5035 button = proxy()->subControlRect(cc, tb, SC_ToolButton, widget);
5036 menuarea = proxy()->subControlRect(cc, tb, SC_ToolButtonMenu, widget);
5037 State bflags = tb->state,
5038 mflags = tb->state;
5039 if (tb->subControls & SC_ToolButton)
5040 bflags |= State_Sunken;
5041 if (tb->subControls & SC_ToolButtonMenu)
5042 mflags |= State_Sunken;
5043
5044 if (tb->subControls & SC_ToolButton) {
5045 if (bflags & (State_Sunken | State_On | State_Raised)) {
5046 HIThemeButtonDrawInfo bdi;
5047 bdi.version = qt_mac_hitheme_version;
5048 bdi.state = tds;
5049 bdi.adornment = kThemeAdornmentNone;
5050 bdi.kind = bkind;
5051 bdi.value = kThemeButtonOff;
5052 if (tb->state & State_HasFocus)
5053 bdi.adornment = kThemeAdornmentFocus;
5054 if (tb->state & State_Sunken)
5055 bdi.state = kThemeStatePressed;
5056 if (tb->state & State_On)
5057 bdi.value = kThemeButtonOn;
5058
5059 QRect off_rct(0, 0, 0, 0);
5060 HIRect myRect, macRect;
5061 myRect = CGRectMake(tb->rect.x(), tb->rect.y(),
5062 tb->rect.width(), tb->rect.height());
5063 HIThemeGetButtonBackgroundBounds(&myRect, &bdi, &macRect);
5064 off_rct.setRect(int(myRect.origin.x - macRect.origin.x),
5065 int(myRect.origin.y - macRect.origin.y),
5066 int(macRect.size.width - myRect.size.width),
5067 int(macRect.size.height - myRect.size.height));
5068
5069 myRect = qt_hirectForQRect(button, off_rct);
5070 HIThemeDrawButton(&myRect, &bdi, cg, kHIThemeOrientationNormal, 0);
5071 }
5072 }
5073
5074 if (tb->subControls & SC_ToolButtonMenu) {
5075 HIThemeButtonDrawInfo bdi;
5076 bdi.version = qt_mac_hitheme_version;
5077 bdi.state = tds;
5078 bdi.value = kThemeButtonOff;
5079 bdi.adornment = kThemeAdornmentNone;
5080 bdi.kind = bkind;
5081 if (tb->state & State_HasFocus)
5082 bdi.adornment = kThemeAdornmentFocus;
5083 if (tb->state & (State_On | State_Sunken)
5084 || (tb->activeSubControls & SC_ToolButtonMenu))
5085 bdi.state = kThemeStatePressed;
5086 HIRect hirect = qt_hirectForQRect(menuarea);
5087 HIThemeDrawButton(&hirect, &bdi, cg, kHIThemeOrientationNormal, 0);
5088 QRect r(menuarea.x() + ((menuarea.width() / 2) - 3), menuarea.height() - 8, 8, 8);
5089 HIThemePopupArrowDrawInfo padi;
5090 padi.version = qt_mac_hitheme_version;
5091 padi.state = tds;
5092 padi.orientation = kThemeArrowDown;
5093 padi.size = kThemeArrow7pt;
5094 hirect = qt_hirectForQRect(r);
5095 HIThemeDrawPopupArrow(&hirect, &padi, cg, kHIThemeOrientationNormal);
5096 } else if (tb->features & QStyleOptionToolButton::HasMenu) {
5097 drawToolbarButtonArrow(tb->rect, tds, cg);
5098 }
5099 QRect buttonRect = proxy()->subControlRect(CC_ToolButton, tb, SC_ToolButton, widget);
5100 int fw = proxy()->pixelMetric(PM_DefaultFrameWidth, opt, widget);
5101 QStyleOptionToolButton label = *tb;
5102 label.rect = buttonRect.adjusted(fw, fw, -fw, -fw);
5103 proxy()->drawControl(CE_ToolButtonLabel, &label, p, widget);
5104 }
5105 }
5106 break;
5107 case CC_Dial:
5108 if (const QStyleOptionSlider *dial = qstyleoption_cast<const QStyleOptionSlider *>(opt))
5109 QStyleHelper::drawDial(dial, p);
5110 break;
5111 default:
5112 QWindowsStyle::drawComplexControl(cc, opt, p, widget);
5113 break;
5114 }
5115}
5116
5117QStyle::SubControl QMacStyle::hitTestComplexControl(ComplexControl cc,
5118 const QStyleOptionComplex *opt,
5119 const QPoint &pt, const QWidget *widget) const
5120{
5121 SubControl sc = QStyle::SC_None;
5122 switch (cc) {
5123 case CC_ComboBox:
5124 if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
5125 sc = QWindowsStyle::hitTestComplexControl(cc, cmb, pt, widget);
5126 if (!cmb->editable && sc != QStyle::SC_None)
5127 sc = SC_ComboBoxArrow; // A bit of a lie, but what we want
5128 }
5129 break;
5130 case CC_Slider:
5131 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
5132 HIThemeTrackDrawInfo tdi;
5133 d->getSliderInfo(cc, slider, &tdi, widget);
5134 ControlPartCode part;
5135 HIPoint pos = CGPointMake(pt.x(), pt.y());
5136 if (HIThemeHitTestTrack(&tdi, &pos, &part)) {
5137 if (part == kControlPageUpPart || part == kControlPageDownPart)
5138 sc = SC_SliderGroove;
5139 else
5140 sc = SC_SliderHandle;
5141 }
5142 }
5143 break;
5144 case CC_ScrollBar:
5145 if (const QStyleOptionSlider *sb = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
5146 HIScrollBarTrackInfo sbi;
5147 sbi.version = qt_mac_hitheme_version;
5148 if (!(sb->state & State_Active))
5149 sbi.enableState = kThemeTrackInactive;
5150 else if (sb->state & State_Enabled)
5151 sbi.enableState = kThemeTrackActive;
5152 else
5153 sbi.enableState = kThemeTrackDisabled;
5154
5155 // The arrow buttons are not drawn if the scroll bar is to short,
5156 // exclude them from the hit test.
5157 const int scrollBarLength = (sb->orientation == Qt::Horizontal)
5158 ? sb->rect.width() : sb->rect.height();
5159 if (scrollBarLength < scrollButtonsCutoffSize(scrollButtonsCutoff, widgetSizePolicy(widget)))
5160 sbi.enableState = kThemeTrackNothingToScroll;
5161
5162 sbi.viewsize = sb->pageStep;
5163 HIPoint pos = CGPointMake(pt.x(), pt.y());
5164
5165 HIRect macSBRect = qt_hirectForQRect(sb->rect);
5166 ControlPartCode part;
5167 bool reverseHorizontal = (sb->direction == Qt::RightToLeft
5168 && sb->orientation == Qt::Horizontal
5169 && (!sb->upsideDown ||
5170 (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4
5171 && sb->upsideDown)));
5172 if (HIThemeHitTestScrollBarArrows(&macSBRect, &sbi, sb->orientation == Qt::Horizontal,
5173 &pos, 0, &part)) {
5174 if (part == kControlUpButtonPart)
5175 sc = reverseHorizontal ? SC_ScrollBarAddLine : SC_ScrollBarSubLine;
5176 else if (part == kControlDownButtonPart)
5177 sc = reverseHorizontal ? SC_ScrollBarSubLine : SC_ScrollBarAddLine;
5178 } else {
5179 HIThemeTrackDrawInfo tdi;
5180 d->getSliderInfo(cc, sb, &tdi, widget);
5181 if(tdi.enableState == kThemeTrackInactive)
5182 tdi.enableState = kThemeTrackActive;
5183 if (HIThemeHitTestTrack(&tdi, &pos, &part)) {
5184 if (part == kControlPageUpPart)
5185 sc = reverseHorizontal ? SC_ScrollBarAddPage
5186 : SC_ScrollBarSubPage;
5187 else if (part == kControlPageDownPart)
5188 sc = reverseHorizontal ? SC_ScrollBarSubPage
5189 : SC_ScrollBarAddPage;
5190 else
5191 sc = SC_ScrollBarSlider;
5192 }
5193 }
5194 }
5195 break;
5196/*
5197 I don't know why, but we only get kWindowContentRgn here, which isn't what we want at all.
5198 It would be very nice if this would work.
5199 case QStyle::CC_TitleBar:
5200 if (const QStyleOptionTitleBar *tbar = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
5201 HIThemeWindowDrawInfo wdi;
5202 memset(&wdi, 0, sizeof(wdi));
5203 wdi.version = qt_mac_hitheme_version;
5204 wdi.state = kThemeStateActive;
5205 wdi.windowType = QtWinType;
5206 wdi.titleWidth = tbar->rect.width();
5207 wdi.titleHeight = tbar->rect.height();
5208 if (tbar->titleBarState)
5209 wdi.attributes |= kThemeWindowHasFullZoom | kThemeWindowHasCloseBox
5210 | kThemeWindowHasCollapseBox;
5211 else if (tbar->titleBarFlags & Qt::WindowSystemMenuHint)
5212 wdi.attributes |= kThemeWindowHasCloseBox;
5213 QRect tmpRect = tbar->rect;
5214 tmpRect.setHeight(tmpRect.height() + 100);
5215 HIRect hirect = qt_hirectForQRect(tmpRect);
5216 WindowRegionCode hit;
5217 HIPoint hipt = CGPointMake(pt.x(), pt.y());
5218 if (HIThemeGetWindowRegionHit(&hirect, &wdi, &hipt, &hit)) {
5219 switch (hit) {
5220 case kWindowCloseBoxRgn:
5221 sc = QStyle::SC_TitleBarCloseButton;
5222 break;
5223 case kWindowCollapseBoxRgn:
5224 sc = QStyle::SC_TitleBarMinButton;
5225 break;
5226 case kWindowZoomBoxRgn:
5227 sc = QStyle::SC_TitleBarMaxButton;
5228 break;
5229 case kWindowTitleTextRgn:
5230 sc = QStyle::SC_TitleBarLabel;
5231 break;
5232 default:
5233 qDebug("got something else %d", hit);
5234 break;
5235 }
5236 }
5237 }
5238 break;
5239*/
5240 default:
5241 sc = QWindowsStyle::hitTestComplexControl(cc, opt, pt, widget);
5242 break;
5243 }
5244 return sc;
5245}
5246
5247QRect QMacStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *opt, SubControl sc,
5248 const QWidget *widget) const
5249{
5250 QRect ret;
5251 switch (cc) {
5252 case CC_Slider:
5253 case CC_ScrollBar:
5254 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
5255 HIThemeTrackDrawInfo tdi;
5256 d->getSliderInfo(cc, slider, &tdi, widget);
5257 HIRect macRect;
5258 QCFType<HIShapeRef> shape;
5259 bool scrollBar = cc == CC_ScrollBar;
5260 if ((scrollBar && sc == SC_ScrollBarSlider)
5261 || (!scrollBar && sc == SC_SliderHandle)) {
5262 HIThemeGetTrackThumbShape(&tdi, &shape);
5263 ptrHIShapeGetBounds(shape, &macRect);
5264 } else if (!scrollBar && sc == SC_SliderGroove) {
5265 HIThemeGetTrackBounds(&tdi, &macRect);
5266 } else if (sc == SC_ScrollBarGroove) { // Only scroll bar parts available...
5267 HIThemeGetTrackDragRect(&tdi, &macRect);
5268 } else {
5269 ControlPartCode cpc;
5270 if (sc == SC_ScrollBarSubPage || sc == SC_ScrollBarAddPage) {
5271 cpc = sc == SC_ScrollBarSubPage ? kControlPageDownPart
5272 : kControlPageUpPart;
5273 } else {
5274 cpc = sc == SC_ScrollBarSubLine ? kControlUpButtonPart
5275 : kControlDownButtonPart;
5276 if (slider->direction == Qt::RightToLeft
5277 && slider->orientation == Qt::Horizontal) {
5278 if (cpc == kControlDownButtonPart)
5279 cpc = kControlUpButtonPart;
5280 else if (cpc == kControlUpButtonPart)
5281 cpc = kControlDownButtonPart;
5282 }
5283 }
5284 HIThemeGetTrackPartBounds(&tdi, cpc, &macRect);
5285 }
5286 ret = qt_qrectForHIRect(macRect);
5287
5288 // Tweak: the dark line between the sub/add line buttons belong to only one of the buttons
5289 // when doing hit-testing, but both of them have to repaint it. Extend the rect to cover
5290 // the line in the cases where HIThemeGetTrackPartBounds returns a rect that doesn't.
5291 if (slider->orientation == Qt::Horizontal) {
5292 if (slider->direction == Qt::LeftToRight && sc == SC_ScrollBarSubLine)
5293 ret.adjust(0, 0, 1, 0);
5294 else if (slider->direction == Qt::RightToLeft && sc == SC_ScrollBarAddLine)
5295 ret.adjust(-1, 0, 1, 0);
5296 } else if (sc == SC_ScrollBarAddLine) {
5297 ret.adjust(0, -1, 0, 1);
5298 }
5299 }
5300 break;
5301 case CC_TitleBar:
5302 if (const QStyleOptionTitleBar *titlebar
5303 = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
5304 HIThemeWindowDrawInfo wdi;
5305 memset(&wdi, 0, sizeof(wdi));
5306 wdi.version = qt_mac_hitheme_version;
5307 wdi.state = kThemeStateActive;
5308 wdi.windowType = QtWinType;
5309 wdi.titleHeight = titlebar->rect.height();
5310 wdi.titleWidth = titlebar->rect.width();
5311 wdi.attributes = kThemeWindowHasTitleText;
5312 if (titlebar->subControls & SC_TitleBarCloseButton)
5313 wdi.attributes |= kThemeWindowHasCloseBox;
5314 if (titlebar->subControls & SC_TitleBarMaxButton
5315 | SC_TitleBarNormalButton)
5316 wdi.attributes |= kThemeWindowHasFullZoom;
5317 if (titlebar->subControls & SC_TitleBarMinButton)
5318 wdi.attributes |= kThemeWindowHasCollapseBox;
5319 WindowRegionCode wrc = kWindowGlobalPortRgn;
5320
5321 if (sc == SC_TitleBarCloseButton)
5322 wrc = kWindowCloseBoxRgn;
5323 else if (sc == SC_TitleBarMinButton)
5324 wrc = kWindowCollapseBoxRgn;
5325 else if (sc == SC_TitleBarMaxButton)
5326 wrc = kWindowZoomBoxRgn;
5327 else if (sc == SC_TitleBarLabel)
5328 wrc = kWindowTitleTextRgn;
5329 else if (sc == SC_TitleBarSysMenu)
5330 ret.setRect(-1024, -1024, 10, proxy()->pixelMetric(PM_TitleBarHeight,
5331 titlebar, widget));
5332 if (wrc != kWindowGlobalPortRgn) {
5333 QCFType<HIShapeRef> region;
5334 QRect tmpRect = titlebar->rect;
5335 HIRect titleRect = qt_hirectForQRect(tmpRect);
5336 HIThemeGetWindowShape(&titleRect, &wdi, kWindowTitleBarRgn, &region);
5337 ptrHIShapeGetBounds(region, &titleRect);
5338 CFRelease(region);
5339 tmpRect.translate(tmpRect.x() - int(titleRect.origin.x),
5340 tmpRect.y() - int(titleRect.origin.y));
5341 titleRect = qt_hirectForQRect(tmpRect);
5342 HIThemeGetWindowShape(&titleRect, &wdi, wrc, &region);
5343 ptrHIShapeGetBounds(region, &titleRect);
5344 ret = qt_qrectForHIRect(titleRect);
5345 }
5346 }
5347 break;
5348 case CC_ComboBox:
5349 if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)) {
5350 HIThemeButtonDrawInfo bdi;
5351 d->initComboboxBdi(combo, &bdi, widget, d->getDrawState(opt->state));
5352
5353 switch (sc) {
5354 case SC_ComboBoxEditField:{
5355 ret = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
5356 // hack to posistion the edit feld correctly for QDateTimeEdits
5357 // in calendarPopup mode.
5358 if (qobject_cast<const QDateTimeEdit *>(widget)) {
5359 ret.moveTop(ret.top() - 2);
5360 ret.setHeight(ret.height() +1);
5361 }
5362 break; }
5363 case SC_ComboBoxArrow:{
5364 ret = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
5365 ret.setX(ret.x() + ret.width());
5366 ret.setWidth(combo->rect.right() - ret.right());
5367 break; }
5368 case SC_ComboBoxListBoxPopup:{
5369 if (combo->editable) {
5370 HIRect inner = QMacStylePrivate::comboboxInnerBounds(qt_hirectForQRect(combo->rect), bdi.kind);
5371 QRect editRect = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
5372 const int comboTop = combo->rect.top();
5373 ret = QRect(qRound(inner.origin.x),
5374 comboTop,
5375 qRound(inner.origin.x - combo->rect.left() + inner.size.width),
5376 editRect.bottom() - comboTop + 2);
5377 } else {
5378 QRect editRect = QMacStylePrivate::comboboxEditBounds(combo->rect, bdi);
5379 ret = QRect(combo->rect.x() + 4 - 11,
5380 combo->rect.y() + 1,
5381 editRect.width() + 10 + 11,
5382 1);
5383 }
5384 break; }
5385 default:
5386 break;
5387 }
5388 }
5389 break;
5390 case CC_GroupBox:
5391 if (const QStyleOptionGroupBox *groupBox = qstyleoption_cast<const QStyleOptionGroupBox *>(opt)) {
5392 bool checkable = groupBox->subControls & SC_GroupBoxCheckBox;
5393 bool flat = (groupBox->features & QStyleOptionFrameV2::Flat);
5394 bool hasNoText = !checkable && groupBox->text.isEmpty();
5395 switch (sc) {
5396 case SC_GroupBoxLabel:
5397 case SC_GroupBoxCheckBox: {
5398 // Cheat and use the smaller font if we need to
5399 bool checkable = groupBox->subControls & SC_GroupBoxCheckBox;
5400 bool fontIsSet = (widget && widget->testAttribute(Qt::WA_SetFont)
5401 || !QApplication::desktopSettingsAware());
5402 int tw;
5403 int h;
5404 int margin = flat || hasNoText ? 0 : 12;
5405 ret = groupBox->rect.adjusted(margin, 0, -margin, 0);
5406
5407 if (!fontIsSet) {
5408 HIThemeTextInfo tti;
5409 tti.version = qt_mac_hitheme_version;
5410 tti.state = kThemeStateActive;
5411 tti.fontID = checkable ? kThemeSystemFont : kThemeSmallSystemFont;
5412 tti.horizontalFlushness = kHIThemeTextHorizontalFlushCenter;
5413 tti.verticalFlushness = kHIThemeTextVerticalFlushCenter;
5414 tti.options = kHIThemeTextBoxOptionNone;
5415 tti.truncationPosition = kHIThemeTextTruncationNone;
5416 tti.truncationMaxLines = 1 + groupBox->text.count(QLatin1Char('\n'));
5417 CGFloat width;
5418 CGFloat height;
5419 QCFString groupText = qt_mac_removeMnemonics(groupBox->text);
5420 HIThemeGetTextDimensions(groupText, 0, &tti, &width, &height, 0);
5421 tw = int(width);
5422 h = int(height);
5423 } else {
5424 QFontMetrics fm = groupBox->fontMetrics;
5425 if (!checkable && !fontIsSet)
5426 fm = QFontMetrics(qt_app_fonts_hash()->value("QSmallFont", QFont()));
5427 h = fm.height();
5428 tw = fm.size(Qt::TextShowMnemonic, groupBox->text).width();
5429 }
5430 ret.setHeight(h);
5431
5432 QRect labelRect = alignedRect(groupBox->direction, groupBox->textAlignment,
5433 QSize(tw, h), ret);
5434 int indicatorWidth = proxy()->pixelMetric(PM_IndicatorWidth, opt, widget);
5435 bool rtl = groupBox->direction == Qt::RightToLeft;
5436 if (sc == SC_GroupBoxLabel) {
5437 if (checkable) {
5438 int newSum = indicatorWidth + 1;
5439 int newLeft = labelRect.left() + (rtl ? -newSum : newSum);
5440 labelRect.moveLeft(newLeft);
5441 } else if (flat) {
5442 int newLeft = labelRect.left() - (rtl ? 3 : -3);
5443 labelRect.moveLeft(newLeft);
5444 labelRect.moveTop(labelRect.top() + 3);
5445 } else {
5446 int newLeft = labelRect.left() - (rtl ? 3 : 2);
5447 labelRect.moveLeft(newLeft);
5448 labelRect.moveTop(labelRect.top() + 5);
5449 }
5450 ret = labelRect;
5451 }
5452
5453 if (sc == SC_GroupBoxCheckBox) {
5454 int left = rtl ? labelRect.right() - indicatorWidth : labelRect.left();
5455 ret.setRect(left, ret.top(),
5456 indicatorWidth, proxy()->pixelMetric(PM_IndicatorHeight, opt, widget));
5457 }
5458 break;
5459 }
5460 case SC_GroupBoxContents:
5461 case SC_GroupBoxFrame: {
5462 if (flat) {
5463 ret = QWindowsStyle::subControlRect(cc, groupBox, sc, widget);
5464 break;
5465 }
5466 QFontMetrics fm = groupBox->fontMetrics;
5467 bool checkable = groupBox->subControls & SC_GroupBoxCheckBox;
5468 int yOffset = 3;
5469 if (!checkable) {
5470 if (widget && !widget->testAttribute(Qt::WA_SetFont)
5471 && QApplication::desktopSettingsAware())
5472 fm = QFontMetrics(qt_app_fonts_hash()->value("QSmallFont", QFont()));
5473 yOffset = 5;
5474 if (hasNoText)
5475 yOffset = -fm.height();
5476 }
5477
5478 ret = opt->rect.adjusted(0, fm.height() + yOffset, 0, 0);
5479 if (sc == SC_GroupBoxContents)
5480 ret.adjust(3, 3, -3, -4); // guess
5481 }
5482 break;
5483 default:
5484 ret = QWindowsStyle::subControlRect(cc, groupBox, sc, widget);
5485 break;
5486 }
5487 }
5488 break;
5489 case CC_SpinBox:
5490 if (const QStyleOptionSpinBox *spin = qstyleoption_cast<const QStyleOptionSpinBox *>(opt)) {
5491 QAquaWidgetSize aquaSize = d->aquaSizeConstrain(spin, widget);
5492 int spinner_w;
5493 int spinBoxSep;
5494 int fw = proxy()->pixelMetric(PM_SpinBoxFrameWidth, spin, widget);
5495 switch (aquaSize) {
5496 default:
5497 case QAquaSizeUnknown:
5498 case QAquaSizeLarge:
5499 spinner_w = 14;
5500 spinBoxSep = 2;
5501 break;
5502 case QAquaSizeSmall:
5503 spinner_w = 12;
5504 spinBoxSep = 2;
5505 break;
5506 case QAquaSizeMini:
5507 spinner_w = 10;
5508 spinBoxSep = 1;
5509 break;
5510 }
5511
5512 switch (sc) {
5513 case SC_SpinBoxUp:
5514 case SC_SpinBoxDown: {
5515 if (spin->buttonSymbols == QAbstractSpinBox::NoButtons)
5516 break;
5517
5518 const int y = fw;
5519 const int x = spin->rect.width() - spinner_w;
5520 ret.setRect(x + spin->rect.x(), y + spin->rect.y(), spinner_w, spin->rect.height() - y * 2);
5521 HIThemeButtonDrawInfo bdi;
5522 bdi.version = qt_mac_hitheme_version;
5523 bdi.kind = kThemeIncDecButton;
5524 int hackTranslateX;
5525 switch (aquaSize) {
5526 default:
5527 case QAquaSizeUnknown:
5528 case QAquaSizeLarge:
5529 bdi.kind = kThemeIncDecButton;
5530 hackTranslateX = 0;
5531 break;
5532 case QAquaSizeSmall:
5533 bdi.kind = kThemeIncDecButtonSmall;
5534 hackTranslateX = -2;
5535 break;
5536 case QAquaSizeMini:
5537 bdi.kind = kThemeIncDecButtonMini;
5538 hackTranslateX = -1;
5539 break;
5540 }
5541 bdi.state = kThemeStateActive;
5542 bdi.value = kThemeButtonOff;
5543 bdi.adornment = kThemeAdornmentNone;
5544 HIRect hirect = qt_hirectForQRect(ret);
5545
5546 HIRect outRect;
5547 HIThemeGetButtonBackgroundBounds(&hirect, &bdi, &outRect);
5548 ret = qt_qrectForHIRect(outRect);
5549 switch (sc) {
5550 case SC_SpinBoxUp:
5551 ret.setHeight(ret.height() / 2);
5552 break;
5553 case SC_SpinBoxDown:
5554 ret.setY(ret.y() + ret.height() / 2);
5555 break;
5556 default:
5557 Q_ASSERT(0);
5558 break;
5559 }
5560 ret.translate(hackTranslateX, 0); // hack: position the buttons correctly (weird that we need this)
5561 ret = visualRect(spin->direction, spin->rect, ret);
5562 break;
5563 }
5564 case SC_SpinBoxEditField:
5565 if (spin->buttonSymbols == QAbstractSpinBox::NoButtons) {
5566 ret.setRect(fw, fw,
5567 spin->rect.width() - fw * 2,
5568 spin->rect.height() - fw * 2);
5569 } else {
5570 ret.setRect(fw, fw,
5571 spin->rect.width() - fw * 2 - spinBoxSep - spinner_w,
5572 spin->rect.height() - fw * 2);
5573 }
5574 ret = visualRect(spin->direction, spin->rect, ret);
5575 break;
5576 default:
5577 ret = QWindowsStyle::subControlRect(cc, spin, sc, widget);
5578 break;
5579 }
5580 }
5581 break;
5582 case CC_ToolButton:
5583 ret = QWindowsStyle::subControlRect(cc, opt, sc, widget);
5584 if (sc == SC_ToolButtonMenu && widget && !qobject_cast<QToolBar*>(widget->parentWidget())) {
5585 ret.adjust(-1, 0, 0, 0);
5586 }
5587 break;
5588 default:
5589 ret = QWindowsStyle::subControlRect(cc, opt, sc, widget);
5590 break;
5591 }
5592 return ret;
5593}
5594
5595QSize QMacStyle::sizeFromContents(ContentsType ct, const QStyleOption *opt,
5596 const QSize &csz, const QWidget *widget) const
5597{
5598 QSize sz(csz);
5599 bool useAquaGuideline = true;
5600
5601 switch (ct) {
5602 case QStyle::CT_SpinBox:
5603 // hack to work around horrible sizeHint() code in QAbstractSpinBox
5604 sz.setHeight(sz.height() - 3);
5605 break;
5606 case QStyle::CT_TabBarTab:
5607 if (const QStyleOptionTabV3 *tab = qstyleoption_cast<const QStyleOptionTabV3 *>(opt)) {
5608 const QAquaWidgetSize AquaSize = d->aquaSizeConstrain(opt, widget);
5609 const bool differentFont = (widget && widget->testAttribute(Qt::WA_SetFont))
5610 || !QApplication::desktopSettingsAware();
5611 ThemeTabDirection ttd = getTabDirection(tab->shape);
5612 bool vertTabs = ttd == kThemeTabWest || ttd == kThemeTabEast;
5613 if (vertTabs)
5614 sz.transpose();
5615 int defaultTabHeight;
5616 int defaultExtraSpace = proxy()->pixelMetric(PM_TabBarTabHSpace, tab, widget); // Remove spurious gcc warning (AFAIK)
5617 QFontMetrics fm = opt->fontMetrics;
5618 switch (AquaSize) {
5619 case QAquaSizeUnknown:
5620 case QAquaSizeLarge:
5621 if (tab->documentMode)
5622 defaultTabHeight = 23;
5623 else
5624 defaultTabHeight = 21;
5625 break;
5626 case QAquaSizeSmall:
5627 defaultTabHeight = 18;
5628 break;
5629 case QAquaSizeMini:
5630 defaultTabHeight = 16;
5631 break;
5632 }
5633 bool setWidth = false;
5634 if (differentFont || !tab->icon.isNull()) {
5635 sz.rheight() = qMax(defaultTabHeight, sz.height());
5636 } else {
5637 QSize textSize = fm.size(Qt::TextShowMnemonic, tab->text);
5638 sz.rheight() = qMax(defaultTabHeight, textSize.height());
5639 sz.rwidth() = textSize.width() + defaultExtraSpace;
5640 setWidth = true;
5641 }
5642
5643 if (vertTabs)
5644 sz.transpose();
5645
5646 int maxWidgetHeight = qMax(tab->leftButtonSize.height(), tab->rightButtonSize.height());
5647 int maxWidgetWidth = qMax(tab->leftButtonSize.width(), tab->rightButtonSize.width());
5648
5649 int widgetWidth = 0;
5650 int widgetHeight = 0;
5651 int padding = 0;
5652 if (tab->leftButtonSize.isValid()) {
5653 padding += 8;
5654 widgetWidth += tab->leftButtonSize.width();
5655 widgetHeight += tab->leftButtonSize.height();
5656 }
5657 if (tab->rightButtonSize.isValid()) {
5658 padding += 8;
5659 widgetWidth += tab->rightButtonSize.width();
5660 widgetHeight += tab->rightButtonSize.height();
5661 }
5662
5663 if (vertTabs) {
5664 sz.setHeight(sz.height() + widgetHeight + padding);
5665 sz.setWidth(qMax(sz.width(), maxWidgetWidth));
5666 } else {
5667 if (setWidth)
5668 sz.setWidth(sz.width() + widgetWidth + padding);
5669 sz.setHeight(qMax(sz.height(), maxWidgetHeight));
5670 }
5671 }
5672 break;
5673 case QStyle::CT_PushButton:
5674 // By default, we fit the contents inside a normal rounded push button.
5675 // Do this by add enough space around the contents so that rounded
5676 // borders (including highlighting when active) will show.
5677 sz.rwidth() += PushButtonLeftOffset + PushButtonRightOffset + 12;
5678 sz.rheight() += PushButtonTopOffset + PushButtonBottomOffset;
5679 break;
5680 case QStyle::CT_MenuItem:
5681 if (const QStyleOptionMenuItem *mi = qstyleoption_cast<const QStyleOptionMenuItem *>(opt)) {
5682 int maxpmw = mi->maxIconWidth;
5683 const QComboBox *comboBox = qobject_cast<const QComboBox *>(widget);
5684 int w = sz.width(),
5685 h = sz.height();
5686 if (mi->menuItemType == QStyleOptionMenuItem::Separator) {
5687 w = 10;
5688 SInt16 ash;
5689 GetThemeMenuSeparatorHeight(&ash);
5690 h = ash;
5691 } else {
5692 h = mi->fontMetrics.height() + 2;
5693 if (!mi->icon.isNull()) {
5694 if (comboBox) {
5695 const QSize &iconSize = comboBox->iconSize();
5696 h = qMax(h, iconSize.height() + 4);
5697 maxpmw = qMax(maxpmw, iconSize.width());
5698 } else {
5699 int iconExtent = proxy()->pixelMetric(PM_SmallIconSize);
5700 h = qMax(h, mi->icon.actualSize(QSize(iconExtent, iconExtent)).height() + 4);
5701 }
5702 }
5703 }
5704 if (mi->text.contains(QLatin1Char('\t')))
5705 w += 12;
5706 if (mi->menuItemType == QStyleOptionMenuItem::SubMenu)
5707 w += 20;
5708 if (maxpmw)
5709 w += maxpmw + 6;
5710 // add space for a check. All items have place for a check too.
5711 w += 20;
5712 if (comboBox && comboBox->isVisible()) {
5713 QStyleOptionComboBox cmb;
5714 cmb.initFrom(comboBox);
5715 cmb.editable = false;
5716 cmb.subControls = QStyle::SC_ComboBoxEditField;
5717 cmb.activeSubControls = QStyle::SC_None;
5718 w = qMax(w, subControlRect(QStyle::CC_ComboBox, &cmb,
5719 QStyle::SC_ComboBoxEditField,
5720 comboBox).width());
5721 } else {
5722 w += 12;
5723 }
5724 sz = QSize(w, h);
5725 }
5726 break;
5727 case CT_ToolButton:
5728 if (widget && qobject_cast<const QToolBar *>(widget->parentWidget())) {
5729 if (QMainWindow * mainWindow = qobject_cast<QMainWindow *>(widget->parent())) {
5730 if (mainWindow->unifiedTitleAndToolBarOnMac()) {
5731 sz.rwidth() += 4;
5732 if (sz.height() <= 32) {
5733 // Workaround strange HIToolBar bug when getting constraints.
5734 sz.rheight() += 1;
5735 }
5736 return sz;
5737 }
5738 }
5739 }
5740 sz.rwidth() += 10;
5741 sz.rheight() += 10;
5742 return sz;
5743 case CT_ComboBox:
5744 sz.rwidth() += 50;
5745 break;
5746 case CT_Menu: {
5747 QStyleHintReturnMask menuMask;
5748 QStyleOption myOption = *opt;
5749 myOption.rect.setSize(sz);
5750 if (proxy()->styleHint(SH_Menu_Mask, &myOption, widget, &menuMask)) {
5751 sz = menuMask.region.boundingRect().size();
5752 }
5753 break; }
5754 case CT_HeaderSection:{
5755 const QStyleOptionHeader *header = qstyleoption_cast<const QStyleOptionHeader *>(opt);
5756 sz = QWindowsStyle::sizeFromContents(ct, opt, csz, widget);
5757 if (header->text.contains(QLatin1Char('\n')))
5758 useAquaGuideline = false;
5759 break; }
5760 case CT_ScrollBar :
5761 // Make sure that the scroll bar is large enough to display the thumb indicator.
5762 if (const QStyleOptionSlider *slider = qstyleoption_cast<const QStyleOptionSlider *>(opt)) {
5763 const int minimumSize = scrollButtonsCutoffSize(thumbIndicatorCutoff, widgetSizePolicy(widget));
5764 if (slider->orientation == Qt::Horizontal)
5765 sz = sz.expandedTo(QSize(minimumSize, sz.height()));
5766 else
5767 sz = sz.expandedTo(QSize(sz.width(), minimumSize));
5768 }
5769 break;
5770 default:
5771 sz = QWindowsStyle::sizeFromContents(ct, opt, csz, widget);
5772 }
5773
5774 if (useAquaGuideline){
5775 QSize macsz;
5776 if (d->aquaSizeConstrain(opt, widget, ct, sz, &macsz) != QAquaSizeUnknown) {
5777 if (macsz.width() != -1)
5778 sz.setWidth(macsz.width());
5779 if (macsz.height() != -1)
5780 sz.setHeight(macsz.height());
5781 }
5782 }
5783
5784 // The sizes that Carbon and the guidelines gives us excludes the focus frame.
5785 // We compensate for this by adding some extra space here to make room for the frame when drawing:
5786 if (const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt)){
5787 QAquaWidgetSize widgetSize = d->aquaSizeConstrain(opt, widget);
5788 int bkind = 0;
5789 switch (widgetSize) {
5790 default:
5791 case QAquaSizeLarge:
5792 bkind = combo->editable ? kThemeComboBox : kThemePopupButton;
5793 break;
5794 case QAquaSizeSmall:
5795 bkind = combo->editable ? int(kThemeComboBoxSmall) : int(kThemePopupButtonSmall);
5796 break;
5797 case QAquaSizeMini:
5798 bkind = combo->editable ? kThemeComboBoxMini : kThemePopupButtonMini;
5799 break;
5800 }
5801 HIRect tmpRect = {{0, 0}, {0, 0}};
5802 HIRect diffRect = QMacStylePrivate::comboboxInnerBounds(tmpRect, bkind);
5803 sz.rwidth() -= qRound(diffRect.size.width);
5804 sz.rheight() -= qRound(diffRect.size.height);
5805 } else if (ct == CT_PushButton || ct == CT_ToolButton){
5806 ThemeButtonKind bkind;
5807 QAquaWidgetSize widgetSize = d->aquaSizeConstrain(opt, widget);
5808 switch (ct) {
5809 default:
5810 case CT_PushButton:
5811 if (const QStyleOptionButton *btn = qstyleoption_cast<const QStyleOptionButton *>(opt)) {
5812 if (btn->features & QStyleOptionButton::CommandLinkButton) {
5813 return QWindowsStyle::sizeFromContents(ct, opt, sz, widget);
5814 }
5815 }
5816
5817 switch (widgetSize) {
5818 default:
5819 case QAquaSizeLarge:
5820 bkind = kThemePushButton;
5821 break;
5822 case QAquaSizeSmall:
5823 bkind = kThemePushButtonSmall;
5824 break;
5825 case QAquaSizeMini:
5826 bkind = kThemePushButtonMini;
5827 break;
5828 }
5829 break;
5830 case CT_ToolButton:
5831 switch (widgetSize) {
5832 default:
5833 case QAquaSizeLarge:
5834 bkind = kThemeLargeBevelButton;
5835 break;
5836 case QAquaSizeMini:
5837 case QAquaSizeSmall:
5838 bkind = kThemeSmallBevelButton;
5839 }
5840 break;
5841 }
5842
5843 HIThemeButtonDrawInfo bdi;
5844 bdi.version = qt_mac_hitheme_version;
5845 bdi.state = kThemeStateActive;
5846 bdi.kind = bkind;
5847 bdi.value = kThemeButtonOff;
5848 bdi.adornment = kThemeAdornmentNone;
5849 HIRect macRect, myRect;
5850 myRect = CGRectMake(0, 0, sz.width(), sz.height());
5851 HIThemeGetButtonBackgroundBounds(&myRect, &bdi, &macRect);
5852 // Mini buttons only return their actual size in HIThemeGetButtonBackgroundBounds, so help them out a bit (guess),
5853 if (bkind == kThemePushButtonMini)
5854 macRect.size.height += 8.;
5855 else if (bkind == kThemePushButtonSmall)
5856 macRect.size.height -= 10;
5857 sz.setWidth(sz.width() + int(macRect.size.width - myRect.size.width));
5858 sz.setHeight(sz.height() + int(macRect.size.height - myRect.size.height));
5859 }
5860 return sz;
5861}
5862
5863void QMacStyle::drawItemText(QPainter *p, const QRect &r, int flags, const QPalette &pal,
5864 bool enabled, const QString &text, QPalette::ColorRole textRole) const
5865{
5866 if(flags & Qt::TextShowMnemonic)
5867 flags |= Qt::TextHideMnemonic;
5868 QWindowsStyle::drawItemText(p, r, flags, pal, enabled, text, textRole);
5869}
5870
5871bool QMacStyle::event(QEvent *e)
5872{