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

Last change on this file since 858 was 846, checked in by Dmitry A. Kuminov, 14 years ago

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

File size: 248.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 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 <qapplication.h>
60#include <qbitmap.h>
61#include <qcheckbox.h>
62#include <qcombobox.h>
63#include <qdialogbuttonbox.h>
64#include <qdockwidget.h>
65#include <qevent.h>
66#include <qfocusframe.h>
67#include <qformlayout.h>
68#include <qgroupbox.h>
69#include <qhash.h>
70#include <qheaderview.h>
71#include <qlayout.h>
72#include <qlineedit.h>
73#include <qlistview.h>
74#include <qmainwindow.h>
75#include <qmap.h>
76#include <qmenubar.h>
77#include <qpaintdevice.h>
78#include <qpainter.h>
79#include <qpixmapcache.h>
80#include <qpointer.h>
81#include <qprogressbar.h>
82#include <qpushbutton.h>
83#include <qradiobutton.h>
84#include <qrubberband.h>
85#include <qsizegrip.h>
86#include <qspinbox.h>
87#include <qsplitter.h>
88#include <qstyleoption.h>
89#include <qtextedit.h>
90#include <qtextstream.h>
91#include <qtoolbar.h>
92#include <qtoolbutton.h>
93#include <qtreeview.h>
94#include <qtableview.h>
95#include <qwizard.h>
96#include <qdebug.h>
97#include <qlibrary.h>
98#include <qdatetimeedit.h>
99#include <qmath.h>
100#include <QtGui/qgraphicsproxywidget.h>
101#include <QtGui/qgraphicsview.h>
102#include <private/qt_cocoa_helpers_mac_p.h>
103#include "qmacstyle_mac_p.h"
104#include <private/qstylehelper_p.h>
105
106QT_BEGIN_NAMESPACE
107
108// The following constants are used for adjusting the size
109// of push buttons so that they are drawn inside their bounds.
110const int QMacStylePrivate::PushButtonLeftOffset = 6;
111const int QMacStylePrivate::PushButtonTopOffset = 4;
112const int QMacStylePrivate::PushButtonRightOffset = 12;
113const int QMacStylePrivate::PushButtonBottomOffset = 12;
114const int QMacStylePrivate::MiniButtonH = 26;
115const int QMacStylePrivate::SmallButtonH = 30;
116const int QMacStylePrivate::BevelButtonW = 50;
117const int QMacStylePrivate::BevelButtonH = 22;
118const int QMacStylePrivate::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
134static const int DisclosureOffset = 4;
135
136// Resolve these at run-time, since the functions was moved in Leopard.
137typedef HIRect * (*PtrHIShapeGetBounds)(HIShapeRef, HIRect *);
138static PtrHIShapeGetBounds ptrHIShapeGetBounds = 0;
139
140static int closeButtonSize = 12;
141
142extern QRegion qt_mac_convert_mac_region(RgnHandle); //qregion_mac.cpp
143
144static bool isVerticalTabs(const QTabBar::Shape shape) {
145 return (shape == QTabBar::RoundedEast
146 || shape == QTabBar::TriangularEast
147 || shape == QTabBar::RoundedWest
148 || shape == QTabBar::TriangularWest);
149}
150
151void drawTabCloseButton(QPainter *p, bool hover, bool active, bool selected)
152{
153 // draw background circle
154 p->setRenderHints(QPainter::Antialiasing);
155 QRect rect(0, 0, closeButtonSize, closeButtonSize);
156 QColor background;
157 if (hover) {
158 background = QColor(124, 124, 124);
159 } else {
160 if (active) {
161 if (selected)
162 background = QColor(104, 104, 104);
163 else
164 background = QColor(83, 83, 83);
165 } else {
166 if (selected)
167 background = QColor(144, 144, 144);
168 else
169 background = QColor(114, 114, 114);
170 }
171 }
172 p->setPen(Qt::transparent);
173 p->setBrush(background);
174 p->drawEllipse(rect);
175
176 // draw cross
177 int min = 3;
178 int max = 9;
179 QPen crossPen;
180 crossPen.setColor(QColor(194, 194, 194));
181 crossPen.setWidthF(1.3);
182 crossPen.setCapStyle(Qt::FlatCap);
183 p->setPen(crossPen);
184 p->drawLine(min, min, max, max);
185 p->drawLine(min, max, max, min);
186}
187
188QRect rotateTabPainter(QPainter *p, QTabBar::Shape shape, QRect tabRect)
189{
190 if (isVerticalTabs(shape)) {
191 int newX, newY, newRot;
192 if (shape == QTabBar::RoundedEast
193 || shape == QTabBar::TriangularEast) {
194 newX = tabRect.width();
195 newY = tabRect.y();
196 newRot = 90;
197 } else {
198 newX = 0;
199 newY = tabRect.y() + tabRect.height();
200 newRot = -90;
201 }
202 tabRect.setRect(0, 0, tabRect.height(), tabRect.width());
203 QMatrix m;
204 m.translate(newX, newY);
205 m.rotate(newRot);
206 p->setMatrix(m, true);
207 }
208 return tabRect;
209}
210
211void drawTabShape(QPainter *p, const QStyleOptionTabV3 *tabOpt)
212{
213 QRect r = tabOpt->rect;
214 p->translate(tabOpt->rect.x(), tabOpt->rect.y());
215 r.moveLeft(0);
216 r.moveTop(0);
217 QRect tabRect = rotateTabPainter(p, tabOpt->shape, r);
218
219 int width = tabRect.width();
220 int height = 20;
221 bool active = (tabOpt->state & QStyle::State_Active);
222 bool selected = (tabOpt->state & QStyle::State_Selected);
223
224 if (selected) {
225 QRect rect(1, 0, width - 2, height);
226
227 // fill body
228 if (active) {
229 int d = (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) ? 16 : 0;
230 p->fillRect(rect, QColor(151 + d, 151 + d, 151 + d));
231 } else {
232 int d = (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) ? 9 : 0;
233 QLinearGradient gradient(rect.topLeft(), rect.bottomLeft());
234 gradient.setColorAt(0, QColor(207 + d, 207 + d, 207 + d));
235 gradient.setColorAt(0.5, QColor(206 + d, 206 + d, 206 + d));
236 gradient.setColorAt(1, QColor(201 + d, 201 + d, 201 + d));
237 p->fillRect(rect, gradient);
238 }
239
240 // draw border
241 QColor borderSides;
242 QColor borderBottom;
243 if (active) {
244 borderSides = QColor(88, 88, 88);
245 borderBottom = QColor(88, 88, 88);
246 } else {
247 borderSides = QColor(121, 121, 121);
248 borderBottom = QColor(116, 116, 116);
249 }
250
251 p->setPen(borderSides);
252
253 int bottom = height;
254 // left line
255 p->drawLine(0, 1, 0, bottom-2);
256 // right line
257 p->drawLine(width-1, 1, width-1, bottom-2);
258
259 // bottom line
260 if (active) {
261 p->setPen(QColor(168, 168, 168));
262 p->drawLine(3, bottom-1, width-3, bottom-1);
263 }
264 p->setPen(borderBottom);
265 p->drawLine(2, bottom, width-2, bottom);
266
267 int w = 3;
268 QRectF rectangleLeft(1, height - w, w, w);
269 QRectF rectangleRight(width - 2, height - 1, w, w);
270 int startAngle = 180 * 16;
271 int spanAngle = 90 * 16;
272 p->setRenderHint(QPainter::Antialiasing);
273 p->drawArc(rectangleLeft, startAngle, spanAngle);
274 p->drawArc(rectangleRight, startAngle, -spanAngle);
275 } else {
276 // when the mouse is over non selected tabs they get a new color
277 bool hover = (tabOpt->state & QStyle::State_MouseOver);
278 if (hover) {
279 QRect rect(1, 2, width - 1, height - 1);
280 p->fillRect(rect, QColor(110, 110, 110));
281 }
282
283 // seperator lines between tabs
284 bool west = (tabOpt->shape == QTabBar::RoundedWest || tabOpt->shape == QTabBar::TriangularWest);
285 bool drawOnRight = !west;
286 if ((!drawOnRight && tabOpt->selectedPosition != QStyleOptionTab::NextIsSelected)
287 || (drawOnRight && tabOpt->selectedPosition != QStyleOptionTab::NextIsSelected)) {
288 QColor borderColor;
289 QColor borderHighlightColor;
290 if (active) {
291 borderColor = QColor(64, 64, 64);
292 borderHighlightColor = QColor(140, 140, 140);
293 } else {
294 borderColor = QColor(135, 135, 135);
295 borderHighlightColor = QColor(178, 178, 178);
296 }
297
298 int x = drawOnRight ? width : 0;
299
300 // tab seperator line
301 p->setPen(borderColor);
302 p->drawLine(x, 2, x, height + 1);
303
304 // tab seperator highlight
305 p->setPen(borderHighlightColor);
306 p->drawLine(x-1, 2, x-1, height + 1);
307 p->drawLine(x+1, 2, x+1, height + 1);
308 }
309 }
310}
311
312void drawTabBase(QPainter *p, const QStyleOptionTabBarBaseV2 *tbb, const QWidget *w)
313{
314 QRect r = tbb->rect;
315 if (isVerticalTabs(tbb->shape)) {
316 r.setWidth(w->width());
317 } else {
318 r.setHeight(w->height());
319 }
320 QRect tabRect = rotateTabPainter(p, tbb->shape, r);
321 int width = tabRect.width();
322 int height = tabRect.height();
323 bool active = (tbb->state & QStyle::State_Active);
324
325 // top border lines
326 QColor borderHighlightTop;
327 QColor borderTop;
328 if (active) {
329 borderTop = QColor(64, 64, 64);
330 borderHighlightTop = QColor(174, 174, 174);
331 } else {
332 borderTop = QColor(135, 135, 135);
333 borderHighlightTop = QColor(207, 207, 207);
334 }
335 p->setPen(borderHighlightTop);
336 p->drawLine(tabRect.x(), 0, width, 0);
337 p->setPen(borderTop);
338 p->drawLine(tabRect.x(), 1, width, 1);
339
340 // center block
341 QRect centralRect(tabRect.x(), 2, width, height - 2);
342 if (active) {
343 QColor mainColor = QColor(120, 120, 120);
344 p->fillRect(centralRect, mainColor);
345 } else {
346 QLinearGradient gradient(centralRect.topLeft(), centralRect.bottomLeft());
347 gradient.setColorAt(0, QColor(165, 165, 165));
348 gradient.setColorAt(0.5, QColor(164, 164, 164));
349 gradient.setColorAt(1, QColor(158, 158, 158));
350 p->fillRect(centralRect, gradient);
351 }
352
353 // bottom border lines
354 QColor borderHighlightBottom;
355 QColor borderBottom;
356 if (active) {
357 borderHighlightBottom = QColor(153, 153, 153);
358 borderBottom = QColor(64, 64, 64);
359 } else {
360 borderHighlightBottom = QColor(177, 177, 177);
361 borderBottom = QColor(127, 127, 127);
362 }
363 p->setPen(borderHighlightBottom);
364 p->drawLine(tabRect.x(), height - 2, width, height - 2);
365 p->setPen(borderBottom);
366 p->drawLine(tabRect.x(), height - 1, width, height - 1);
367}
368
369static int getControlSize(const QStyleOption *option, const QWidget *widget)
370{
371 if (option) {
372 if (option->state & (QStyle::State_Small | QStyle::State_Mini))
373 return (option->state & QStyle::State_Mini) ? QAquaSizeMini : QAquaSizeSmall;
374 } else if (widget) {
375 switch (QMacStyle::widgetSizePolicy(widget)) {
376 case QMacStyle::SizeSmall:
377 return QAquaSizeSmall;
378 case QMacStyle::SizeMini:
379 return QAquaSizeMini;
380 default:
381 break;
382 }
383 }
384 return QAquaSizeLarge;
385}
386
387
388static inline bool isTreeView(const QWidget *widget)
389{
390 return (widget && widget->parentWidget() &&
391 (qobject_cast<const QTreeView *>(widget->parentWidget())
392#ifdef QT3_SUPPORT
393 || widget->parentWidget()->inherits("Q3ListView")
394#endif
395 ));
396}
397
398QString qt_mac_removeMnemonics(const QString &original)
399{
400 QString returnText(original.size(), 0);
401 int finalDest = 0;
402 int currPos = 0;
403 int l = original.length();
404 while (l) {
405 if (original.at(currPos) == QLatin1Char('&')
406 && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) {
407 ++currPos;
408 --l;
409 if (l == 0)
410 break;
411 }
412 returnText[finalDest] = original.at(currPos);
413 ++currPos;
414 ++finalDest;
415 --l;
416 }
417 returnText.truncate(finalDest);
418 return returnText;
419}
420
421static inline ThemeTabDirection getTabDirection(QTabBar::Shape shape)
422{
423 ThemeTabDirection ttd;
424 switch (shape) {
425 case QTabBar::RoundedSouth:
426 case QTabBar::TriangularSouth:
427 ttd = kThemeTabSouth;
428 break;
429 default: // Added to remove the warning, since all values are taken care of, really!
430 case QTabBar::RoundedNorth:
431 case QTabBar::TriangularNorth:
432 ttd = kThemeTabNorth;
433 break;
434 case QTabBar::RoundedWest:
435 case QTabBar::TriangularWest:
436 ttd = kThemeTabWest;
437 break;
438 case QTabBar::RoundedEast:
439 case QTabBar::TriangularEast:
440 ttd = kThemeTabEast;
441 break;
442 }
443 return ttd;
444}
445
446QT_BEGIN_INCLUDE_NAMESPACE
447#include "moc_qmacstyle_mac.cpp"
448#include "moc_qmacstyle_mac_p.cpp"
449QT_END_INCLUDE_NAMESPACE
450
451/*****************************************************************************
452 External functions
453 *****************************************************************************/
454extern CGContextRef qt_mac_cg_context(const QPaintDevice *); //qpaintdevice_mac.cpp
455extern QRegion qt_mac_convert_mac_region(HIShapeRef); //qregion_mac.cpp
456void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
457extern QPaintDevice *qt_mac_safe_pdev; //qapplication_mac.cpp
458
459/*****************************************************************************
460 QMacCGStyle globals
461 *****************************************************************************/
462const int qt_mac_hitheme_version = 0; //the HITheme version we speak
463const int macItemFrame = 2; // menu item frame width
464const int macItemHMargin = 3; // menu item hor text margin
465const int macItemVMargin = 2; // menu item ver text margin
466const int macRightBorder = 12; // right border on mac
467const ThemeWindowType QtWinType = kThemeDocumentWindow; // Window type we use for QTitleBar.
468QPixmap *qt_mac_backgroundPattern = 0; // stores the standard widget background.
469
470/*****************************************************************************
471 QMacCGStyle utility functions
472 *****************************************************************************/
473static inline int qt_mac_hitheme_tab_version()
474{
475 return 1;
476}
477
478static inline HIRect qt_hirectForQRect(const QRect &convertRect, const QRect &rect = QRect())
479{
480 return CGRectMake(convertRect.x() + rect.x(), convertRect.y() + rect.y(),
481 convertRect.width() - rect.width(), convertRect.height() - rect.height());
482}
483
484static inline const QRect qt_qrectForHIRect(const HIRect &hirect)
485{
486 return QRect(QPoint(int(hirect.origin.x), int(hirect.origin.y)),
487 QSize(int(hirect.size.width), int(hirect.size.height)));
488}
489
490inline bool qt_mac_is_metal(const QWidget *w)
491{
492 for (; w; w = w->parentWidget()) {
493 if (w->testAttribute(Qt::WA_MacBrushedMetal))
494 return true;
495 if (w->isWindow() && w->testAttribute(Qt::WA_WState_Created)) { // If not created will fall through to the opaque check and be fine anyway.
496 return macWindowIsTextured(qt_mac_window_for(w));
497 }
498 if (w->d_func()->isOpaque)
499 break;
500 }
501 return false;
502}
503
504static int qt_mac_aqua_get_metric(ThemeMetric met)
505{
506 SInt32 ret;
507 GetThemeMetric(met, &ret);
508 return ret;
509}
510
511static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QWidget *widg, QSize szHint,
512 QAquaWidgetSize sz)
513{
514 QSize ret(-1, -1);
515 if (sz != QAquaSizeSmall && sz != QAquaSizeLarge && sz != QAquaSizeMini) {
516 qDebug("Not sure how to return this...");
517 return ret;
518 }
519 if ((widg && widg->testAttribute(Qt::WA_SetFont)) || !QApplication::desktopSettingsAware()) {
520 // If you're using a custom font and it's bigger than the default font,
521 // then no constraints for you. If you are smaller, we can try to help you out
522 QFont font = qt_app_fonts_hash()->value(widg->metaObject()->className(), QFont());
523 if (widg->font().pointSize() > font.pointSize())
524 return ret;
525 }
526
527 if (ct == QStyle::CT_CustomBase && widg) {
528 if (qobject_cast<const QPushButton *>(widg))
529 ct = QStyle::CT_PushButton;
530 else if (qobject_cast<const QRadioButton *>(widg))
531 ct = QStyle::CT_RadioButton;
532 else if (qobject_cast<const QCheckBox *>(widg))
533 ct = QStyle::CT_CheckBox;
534 else if (qobject_cast<const QComboBox *>(widg))
535 ct = QStyle::CT_ComboBox;
536 else if (qobject_cast<const QToolButton *>(widg))
537 ct = QStyle::CT_ToolButton;
538 else if (qobject_cast<const QSlider *>(widg))
539 ct = QStyle::CT_Slider;
540 else if (qobject_cast<const QProgressBar *>(widg))
541 ct = QStyle::CT_ProgressBar;
542 else if (qobject_cast<const QLineEdit *>(widg))
543 ct = QStyle::CT_LineEdit;
544 else if (qobject_cast<const QHeaderView *>(widg)
545#ifdef QT3_SUPPORT
546 || widg->inherits("Q3Header")
547#endif
548 )
549 ct = QStyle::CT_HeaderSection;
550 else if (qobject_cast<const QMenuBar *>(widg)
551#ifdef QT3_SUPPORT
552 || widg->inherits("Q3MenuBar")
553#endif
554 )
555 ct = QStyle::CT_MenuBar;
556 else if (qobject_cast<const QSizeGrip *>(widg))
557 ct = QStyle::CT_SizeGrip;
558 else
559 return ret;
560 }
561
562 switch (ct) {
563 case QStyle::CT_PushButton: {
564 const QPushButton *psh = qobject_cast<const QPushButton *>(widg);
565 // If this comparison is false, then the widget was not a push button.
566 // This is bad and there's very little we can do since we were requested to find a
567 // sensible size for a widget that pretends to be a QPushButton but is not.
568 if(psh) {
569 QString buttonText = qt_mac_removeMnemonics(psh->text());
570 if (buttonText.contains(QLatin1Char('\n')))
571 ret = QSize(-1, -1);
572 else if (sz == QAquaSizeLarge)
573 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
574 else if (sz == QAquaSizeSmall)
575 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPushButtonHeight));
576 else if (sz == QAquaSizeMini)
577 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPushButtonHeight));
578
579 if (!psh->icon().isNull()){
580 // If the button got an icon, and the icon is larger than the
581 // button, we can't decide on a default size
582 ret.setWidth(-1);
583 if (ret.height() < psh->iconSize().height())
584 ret.setHeight(-1);
585 }
586 else if (buttonText == QLatin1String("OK") || buttonText == QLatin1String("Cancel")){
587 // Aqua Style guidelines restrict the size of OK and Cancel buttons to 68 pixels.
588 // However, this doesn't work for German, therefore only do it for English,
589 // I suppose it would be better to do some sort of lookups for languages
590 // that like to have really long words.
591 ret.setWidth(77 - 8);
592 }
593 } else {
594 // The only sensible thing to do is to return whatever the style suggests...
595 if (sz == QAquaSizeLarge)
596 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
597 else if (sz == QAquaSizeSmall)
598 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPushButtonHeight));
599 else if (sz == QAquaSizeMini)
600 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPushButtonHeight));
601 else
602 // Since there's no default size we return the large size...
603 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
604 }
605#if 0 //Not sure we are applying the rules correctly for RadioButtons/CheckBoxes --Sam
606 } else if (ct == QStyle::CT_RadioButton) {
607 QRadioButton *rdo = static_cast<QRadioButton *>(widg);
608 // Exception for case where multiline radio button text requires no size constrainment
609 if (rdo->text().find('\n') != -1)
610 return ret;
611 if (sz == QAquaSizeLarge)
612 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricRadioButtonHeight));
613 else if (sz == QAquaSizeSmall)
614 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallRadioButtonHeight));
615 else if (sz == QAquaSizeMini)
616 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniRadioButtonHeight));
617 } else if (ct == QStyle::CT_CheckBox) {
618 if (sz == QAquaSizeLarge)
619 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricCheckBoxHeight));
620 else if (sz == QAquaSizeSmall)
621 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallCheckBoxHeight));
622 else if (sz == QAquaSizeMini)
623 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniCheckBoxHeight));
624#endif
625 break;
626 }
627 case QStyle::CT_SizeGrip:
628 if (sz == QAquaSizeLarge || sz == QAquaSizeSmall) {
629 HIRect r;
630 HIPoint p = { 0, 0 };
631 HIThemeGrowBoxDrawInfo gbi;
632 gbi.version = 0;
633 gbi.state = kThemeStateActive;
634 gbi.kind = kHIThemeGrowBoxKindNormal;
635 gbi.direction = QApplication::isRightToLeft() ? kThemeGrowLeft | kThemeGrowDown
636 : kThemeGrowRight | kThemeGrowDown;
637 gbi.size = sz == QAquaSizeSmall ? kHIThemeGrowBoxSizeSmall : kHIThemeGrowBoxSizeNormal;
638 if (HIThemeGetGrowBoxBounds(&p, &gbi, &r) == noErr)
639 ret = QSize(r.size.width, r.size.height);
640 }
641 break;
642 case QStyle::CT_ComboBox:
643 switch (sz) {
644 case QAquaSizeLarge:
645 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPopupButtonHeight));
646 break;
647 case QAquaSizeSmall:
648 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPopupButtonHeight));
649 break;
650 case QAquaSizeMini:
651 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPopupButtonHeight));
652 break;
653 default:
654 break;
655 }
656 break;
657 case QStyle::CT_ToolButton:
658 if (sz == QAquaSizeSmall) {
659 int width = 0, height = 0;
660 if (szHint == QSize(-1, -1)) { //just 'guess'..
661 const QToolButton *bt = qobject_cast<const QToolButton *>(widg);
662 // If this conversion fails then the widget was not what it claimed to be.
663 if(bt) {
664 if (!bt->icon().isNull()) {
665 QSize iconSize = bt->iconSize();
666 QSize pmSize = bt->icon().actualSize(QSize(32, 32), QIcon::Normal);
667 width = qMax(width, qMax(iconSize.width(), pmSize.width()));
668 height = qMax(height, qMax(iconSize.height(), pmSize.height()));
669 }
670 if (!bt->text().isNull() && bt->toolButtonStyle() != Qt::ToolButtonIconOnly) {
671 int text_width = bt->fontMetrics().width(bt->text()),
672 text_height = bt->fontMetrics().height();
673 if (bt->toolButtonStyle() == Qt::ToolButtonTextUnderIcon) {
674 width = qMax(width, text_width);
675 height += text_height;
676 } else {
677 width += text_width;
678 width = qMax(height, text_height);
679 }
680 }
681 } else {
682 // Let's return the size hint...
683 width = szHint.width();
684 height = szHint.height();
685 }
686 } else {
687 width = szHint.width();
688 height = szHint.height();
689 }
690 width = qMax(20, width + 5); //border
691 height = qMax(20, height + 5); //border
692 ret = QSize(width, height);
693 }
694 break;
695 case QStyle::CT_Slider: {
696 int w = -1;
697 const QSlider *sld = qobject_cast<const QSlider *>(widg);
698 // If this conversion fails then the widget was not what it claimed to be.
699 if(sld) {
700 if (sz == QAquaSizeLarge) {
701 if (sld->orientation() == Qt::Horizontal) {
702 w = qt_mac_aqua_get_metric(kThemeMetricHSliderHeight);
703 if (sld->tickPosition() != QSlider::NoTicks)
704 w += qt_mac_aqua_get_metric(kThemeMetricHSliderTickHeight);
705 } else {
706 w = qt_mac_aqua_get_metric(kThemeMetricVSliderWidth);
707 if (sld->tickPosition() != QSlider::NoTicks)
708 w += qt_mac_aqua_get_metric(kThemeMetricVSliderTickWidth);
709 }
710 } else if (sz == QAquaSizeSmall) {
711 if (sld->orientation() == Qt::Horizontal) {
712 w = qt_mac_aqua_get_metric(kThemeMetricSmallHSliderHeight);
713 if (sld->tickPosition() != QSlider::NoTicks)
714 w += qt_mac_aqua_get_metric(kThemeMetricSmallHSliderTickHeight);
715 } else {
716 w = qt_mac_aqua_get_metric(kThemeMetricSmallVSliderWidth);
717 if (sld->tickPosition() != QSlider::NoTicks)
718 w += qt_mac_aqua_get_metric(kThemeMetricSmallVSliderTickWidth);
719 }
720 } else if (sz == QAquaSizeMini) {
721 if (sld->orientation() == Qt::Horizontal) {
722 w = qt_mac_aqua_get_metric(kThemeMetricMiniHSliderHeight);
723 if (sld->tickPosition() != QSlider::NoTicks)
724 w += qt_mac_aqua_get_metric(kThemeMetricMiniHSliderTickHeight);
725 } else {
726 w = qt_mac_aqua_get_metric(kThemeMetricMiniVSliderWidth);
727 if (sld->tickPosition() != QSlider::NoTicks)
728 w += qt_mac_aqua_get_metric(kThemeMetricMiniVSliderTickWidth);
729 }
730 }
731 } else {
732 // This is tricky, we were requested to find a size for a slider which is not
733 // a slider. We don't know if this is vertical or horizontal or if we need to
734 // have tick marks or not.
735 // For this case we will return an horizontal slider without tick marks.
736 w = qt_mac_aqua_get_metric(kThemeMetricHSliderHeight);
737 w += qt_mac_aqua_get_metric(kThemeMetricHSliderTickHeight);
738 }
739 if (sld->orientation() == Qt::Horizontal)
740 ret.setHeight(w);
741 else
742 ret.setWidth(w);
743 break;
744 }
745 case QStyle::CT_ProgressBar: {
746 int finalValue = -1;
747 Qt::Orientation orient = Qt::Horizontal;
748 if (const QProgressBar *pb = qobject_cast<const QProgressBar *>(widg))
749 orient = pb->orientation();
750
751 if (sz == QAquaSizeLarge)
752 finalValue = qt_mac_aqua_get_metric(kThemeMetricLargeProgressBarThickness)
753 + qt_mac_aqua_get_metric(kThemeMetricProgressBarShadowOutset);
754 else
755 finalValue = qt_mac_aqua_get_metric(kThemeMetricNormalProgressBarThickness)
756 + qt_mac_aqua_get_metric(kThemeMetricSmallProgressBarShadowOutset);
757 if (orient == Qt::Horizontal)
758 ret.setHeight(finalValue);
759 else
760 ret.setWidth(finalValue);
761 break;
762 }
763 case QStyle::CT_LineEdit:
764 if (!widg || !qobject_cast<QComboBox *>(widg->parentWidget())) {
765 //should I take into account the font dimentions of the lineedit? -Sam
766 if (sz == QAquaSizeLarge)
767 ret = QSize(-1, 22);
768 else
769 ret = QSize(-1, 19);
770 }
771 break;
772 case QStyle::CT_HeaderSection:
773 if (isTreeView(widg))
774 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricListHeaderHeight));
775 break;
776 case QStyle::CT_MenuBar:
777 if (sz == QAquaSizeLarge) {
778#ifndef QT_MAC_USE_COCOA
779 SInt16 size;
780 if (!GetThemeMenuBarHeight(&size))
781 ret = QSize(-1, size);
782#else
783 ret = QSize(-1, [[NSApp mainMenu] menuBarHeight]);
784 // In the qt_mac_set_native_menubar(false) case,
785 // we come it here with a zero-height main menu,
786 // preventing the in-window menu from displaying.
787 // Use 22 pixels for the height, by observation.
788 if (ret.height() <= 0)
789 ret.setHeight(22);
790#endif
791 }
792 break;
793 default:
794 break;
795 }
796 return ret;
797}
798
799
800#if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT)
801static QAquaWidgetSize qt_aqua_guess_size(const QWidget *widg, QSize large, QSize small, QSize mini)
802{
803 if (large == QSize(-1, -1)) {
804 if (small != QSize(-1, -1))
805 return QAquaSizeSmall;
806 if (mini != QSize(-1, -1))
807 return QAquaSizeMini;
808 return QAquaSizeUnknown;
809 } else if (small == QSize(-1, -1)) {
810 if (mini != QSize(-1, -1))
811 return QAquaSizeMini;
812 return QAquaSizeLarge;
813 } else if (mini == QSize(-1, -1)) {
814 return QAquaSizeLarge;
815 }
816
817#ifndef QT_NO_MAINWINDOW
818 if (qobject_cast<QDockWidget *>(widg->window()) || !qgetenv("QWIDGET_ALL_SMALL").isNull()) {
819 //if (small.width() != -1 || small.height() != -1)
820 return QAquaSizeSmall;
821 } else if (!qgetenv("QWIDGET_ALL_MINI").isNull()) {
822 return QAquaSizeMini;
823 }
824#endif
825
826#if 0
827 /* Figure out which size we're closer to, I just hacked this in, I haven't
828 tested it as it would probably look pretty strange to have some widgets
829 big and some widgets small in the same window?? -Sam */
830 int large_delta=0;
831 if (large.width() != -1) {
832 int delta = large.width() - widg->width();
833 large_delta += delta * delta;
834 }
835 if (large.height() != -1) {
836 int delta = large.height() - widg->height();
837 large_delta += delta * delta;
838 }
839 int small_delta=0;
840 if (small.width() != -1) {
841 int delta = small.width() - widg->width();
842 small_delta += delta * delta;
843 }
844 if (small.height() != -1) {
845 int delta = small.height() - widg->height();
846 small_delta += delta * delta;
847 }
848 int mini_delta=0;
849 if (mini.width() != -1) {
850 int delta = mini.width() - widg->width();
851 mini_delta += delta * delta;
852 }
853 if (mini.height() != -1) {
854 int delta = mini.height() - widg->height();
855 mini_delta += delta * delta;
856 }
857 if (mini_delta < small_delta && mini_delta < large_delta)
858 return QAquaSizeMini;
859 else if (small_delta < large_delta)
860 return QAquaSizeSmall;
861#endif
862 return QAquaSizeLarge;
863}
864#endif
865
866QAquaWidgetSize QMacStylePrivate::aquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
867 QStyle::ContentsType ct, QSize szHint, QSize *insz) const
868{
869#if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT)
870 if (option) {
871 if (option->state & QStyle::State_Small)
872 return QAquaSizeSmall;
873 if (option->state & QStyle::State_Mini)
874 return QAquaSizeMini;
875 }
876
877 if (!widg) {
878 if (insz)
879 *insz = QSize();
880 if (!qgetenv("QWIDGET_ALL_SMALL").isNull())
881 return QAquaSizeSmall;
882 if (!qgetenv("QWIDGET_ALL_MINI").isNull())
883 return QAquaSizeMini;
884 return QAquaSizeUnknown;
885 }
886 QSize large = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeLarge),
887 small = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeSmall),
888 mini = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeMini);
889 bool guess_size = false;
890 QAquaWidgetSize ret = QAquaSizeUnknown;
891 QMacStyle::WidgetSizePolicy wsp = q->widgetSizePolicy(widg);
892 if (wsp == QMacStyle::SizeDefault)
893 guess_size = true;
894 else if (wsp == QMacStyle::SizeMini)
895 ret = QAquaSizeMini;
896 else if (wsp == QMacStyle::SizeSmall)
897 ret = QAquaSizeSmall;
898 else if (wsp == QMacStyle::SizeLarge)
899 ret = QAquaSizeLarge;
900 if (guess_size)
901 ret = qt_aqua_guess_size(widg, large, small, mini);
902
903 QSize *sz = 0;
904 if (ret == QAquaSizeSmall)
905 sz = &small;
906 else if (ret == QAquaSizeLarge)
907 sz = &large;
908 else if (ret == QAquaSizeMini)
909 sz = &mini;
910 if (insz)
911 *insz = sz ? *sz : QSize(-1, -1);
912#ifdef DEBUG_SIZE_CONSTRAINT
913 if (sz) {
914 const char *size_desc = "Unknown";
915 if (sz == &small)
916 size_desc = "Small";
917 else if (sz == &large)
918 size_desc = "Large";
919 else if (sz == &mini)
920 size_desc = "Mini";
921 qDebug("%s - %s: %s taken (%d, %d) [%d, %d]",
922 widg ? widg->objectName().toLatin1().constData() : "*Unknown*",
923 widg ? widg->metaObject()->className() : "*Unknown*", size_desc, widg->width(), widg->height(),
924 sz->width(), sz->height());
925 }
926#endif
927 return ret;
928#else
929 if (insz)
930 *insz = QSize();
931 Q_UNUSED(widg);
932 Q_UNUSED(ct);
933 Q_UNUSED(szHint);
934 return QAquaSizeUnknown;
935#endif
936}
937
938/**
939 Returns the free space awailable for contents inside the
940 button (and not the size of the contents itself)
941*/
942HIRect QMacStylePrivate::pushButtonContentBounds(const QStyleOptionButton *btn,
943 const HIThemeButtonDrawInfo *bdi) const
944{
945 HIRect outerBounds = qt_hirectForQRect(btn->rect);
946 // Adjust the bounds to correct for
947 // carbon not calculating the content bounds fully correct
948 if (bdi->kind == kThemePushButton || bdi->kind == kThemePushButtonSmall){
949 outerBounds.origin.y += QMacStylePrivate::PushButtonTopOffset;
950 outerBounds.size.height -= QMacStylePrivate::PushButtonBottomOffset;
951 } else if (bdi->kind == kThemePushButtonMini) {
952 outerBounds.origin.y += QMacStylePrivate::PushButtonTopOffset;
953 }
954
955 HIRect contentBounds;
956 HIThemeGetButtonContentBounds(&outerBounds, bdi, &contentBounds);
957 return contentBounds;
958}
959
960/**
961 Calculates the size of the button contents.
962 This includes both the text and the icon.
963*/
964QSize QMacStylePrivate::pushButtonSizeFromContents(const QStyleOptionButton *btn) const
965{
966 QSize csz(0, 0);
967 QSize iconSize = btn->icon.isNull() ? QSize(0, 0)
968 : (btn->iconSize + QSize(QMacStylePrivate::PushButtonContentPadding, 0));
969 QRect textRect = btn->text.isEmpty() ? QRect(0, 0, 1, 1)
970 : btn->fontMetrics.boundingRect(QRect(), Qt::AlignCenter, btn->text);
971 csz.setWidth(iconSize.width() + textRect.width()
972 + ((btn->features & QStyleOptionButton::HasMenu)
973 ? q->proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, btn, 0) : 0));
974 csz.setHeight(qMax(iconSize.height(), textRect.height()));
975 return csz;
976}
977
978/**
979 Checks if the actual contents of btn fits inside the free content bounds of
980 'buttonKindToCheck'. Meant as a helper function for 'initHIThemePushButton'
981 for determining which button kind to use for drawing.
982*/
983bool QMacStylePrivate::contentFitsInPushButton(const QStyleOptionButton *btn,
984 HIThemeButtonDrawInfo *bdi,
985 ThemeButtonKind buttonKindToCheck) const
986{
987 ThemeButtonKind tmp = bdi->kind;
988 bdi->kind = buttonKindToCheck;
989 QSize contentSize = pushButtonSizeFromContents(btn);
990 QRect freeContentRect = qt_qrectForHIRect(pushButtonContentBounds(btn, bdi));
991 bdi->kind = tmp;
992 return freeContentRect.contains(QRect(freeContentRect.x(), freeContentRect.y(),
993 contentSize.width(), contentSize.height()));
994}
995
996/**
997 Creates a HIThemeButtonDrawInfo structure that specifies the correct button
998 kind and other details to use for drawing the given push button. Which
999 button kind depends on the size of the button, the size of the contents,
1000 explicit user style settings, etc.
1001*/
1002void QMacStylePrivate::initHIThemePushButton(const QStyleOptionButton *btn,
1003 const QWidget *widget,
1004 const ThemeDrawState tds,
1005 HIThemeButtonDrawInfo *bdi) const
1006{
1007 bool drawColorless = btn->palette.currentColorGroup() == QPalette::Active;
1008 ThemeDrawState tdsModified = tds;
1009 if (btn->state & QStyle::State_On)
1010 tdsModified = kThemeStatePressed;
1011 bdi->version = qt_mac_hitheme_version;
1012 bdi->state = tdsModified;
1013 bdi->value = kThemeButtonOff;
1014
1015 if (drawColorless && tdsModified == kThemeStateInactive)
1016 bdi->state = kThemeStateActive;
1017 if (btn->state & QStyle::State_HasFocus)
1018 bdi->adornment = kThemeAdornmentFocus;
1019 else
1020 bdi->adornment = kThemeAdornmentNone;
1021
1022
1023 if (btn->features & (QStyleOptionButton::Flat)) {
1024 bdi->kind = kThemeBevelButton;
1025 } else {
1026 switch (aquaSizeConstrain(btn, widget)) {
1027 case QAquaSizeSmall:
1028 bdi->kind = kThemePushButtonSmall;
1029 break;
1030 case QAquaSizeMini:
1031 bdi->kind = kThemePushButtonMini;
1032 break;
1033 case QAquaSizeLarge:
1034 // ... We should honor if the user is explicit about using the
1035 // large button. But right now Qt will specify the large button
1036 // as default rather than QAquaSizeUnknown.
1037 // So we treat it like QAquaSizeUnknown
1038 // to get the dynamic choosing of button kind.
1039 case QAquaSizeUnknown:
1040 // Choose the button kind that closest match the button rect, but at the
1041 // same time displays the button contents without clipping.
1042 bdi->kind = kThemeBevelButton;
1043 if (btn->rect.width() >= QMacStylePrivate::BevelButtonW && btn->rect.height() >= QMacStylePrivate::BevelButtonH){
1044 if (widget && widget->testAttribute(Qt::WA_MacVariableSize)) {
1045 if (btn->rect.height() <= QMacStylePrivate::MiniButtonH){
1046 if (contentFitsInPushButton(btn, bdi, kThemePushButtonMini))
1047 bdi->kind = kThemePushButtonMini;
1048 } else if (btn->rect.height() <= QMacStylePrivate::SmallButtonH){
1049 if (contentFitsInPushButton(btn, bdi, kThemePushButtonSmall))
1050 bdi->kind = kThemePushButtonSmall;
1051 } else if (contentFitsInPushButton(btn, bdi, kThemePushButton)) {
1052 bdi->kind = kThemePushButton;
1053 }
1054 } else {
1055 bdi->kind = kThemePushButton;
1056 }
1057 }
1058 }
1059 }
1060}
1061
1062bool qt_mac_buttonIsRenderedFlat(const QPushButton *pushButton, const QStyleOptionButton *option)
1063{
1064 QMacStyle *macStyle = qobject_cast<QMacStyle *>(pushButton->style());
1065 if (!macStyle)
1066 return false;
1067 HIThemeButtonDrawInfo bdi;
1068 macStyle->d->initHIThemePushButton(option, pushButton, kThemeStateActive, &bdi);
1069 return bdi.kind == kThemeBevelButton;
1070}
1071
1072/**
1073 Creates a HIThemeButtonDrawInfo structure that specifies the correct button
1074 kind and other details to use for drawing the given combobox. Which button
1075 kind depends on the size of the combo, wether or not it is editable,
1076 explicit user style settings, etc.
1077*/
1078void QMacStylePrivate::initComboboxBdi(const QStyleOptionComboBox *combo, HIThemeButtonDrawInfo *bdi,
1079 const QWidget *widget, const ThemeDrawState &tds)
1080{
1081 bdi->version = qt_mac_hitheme_version;
1082 bdi->adornment = kThemeAdornmentArrowLeftArrow;
1083 bdi->value = kThemeButtonOff;
1084 if (combo->state & QStyle::State_HasFocus)
1085 bdi->adornment = kThemeAdornmentFocus;
1086 bool drawColorless = combo->palette.currentColorGroup() == QPalette::Active && tds == kThemeStateInactive;
1087 if (combo->activeSubControls & QStyle::SC_ComboBoxArrow)
1088 bdi->state = kThemeStatePressed;
1089 else if (drawColorless)
1090 bdi->state = kThemeStateActive;
1091 else
1092 bdi->state = tds;
1093
1094 QAquaWidgetSize aSize = aquaSizeConstrain(combo, widget);
1095 switch (aSize) {
1096 case QAquaSizeMini:
1097 bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxMini)
1098 : ThemeButtonKind(kThemePopupButtonMini);
1099 break;
1100 case QAquaSizeSmall:
1101 bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxSmall)
1102 : ThemeButtonKind(kThemePopupButtonSmall);
1103 break;
1104 case QAquaSizeUnknown:
1105 case QAquaSizeLarge:
1106 // Unless the user explicitly specified large buttons, determine the
1107 // kind by looking at the combox size.
1108 // ... specifying small and mini-buttons it not a current feature of
1109 // Qt (e.g. QWidget::getAttribute(WA_ButtonSize)). But when it is, add
1110 // an extra check here before using the mini and small buttons.
1111 int h = combo->rect.size().height();
1112 if (combo->editable){
1113 if (h < 21)
1114 bdi->kind = kThemeComboBoxMini;
1115 else if (h < 26)
1116 bdi->kind = kThemeComboBoxSmall;
1117 else
1118 bdi->kind = kThemeComboBox;
1119 } else {
1120 // Even if we specify that we want the kThemePopupButton, Carbon
1121 // will use the kThemePopupButtonSmall if the size matches. So we
1122 // do the same size check explicit to have the size of the inner
1123 // text field be correct. Therefore, do this even if the user specifies
1124 // the use of LargeButtons explicit.
1125 if (h < 21)
1126 bdi->kind = kThemePopupButtonMini;
1127 else if (h < 26)
1128 bdi->kind = kThemePopupButtonSmall;
1129 else
1130 bdi->kind = kThemePopupButton;
1131 }
1132 break;
1133 }
1134}
1135
1136/**
1137 Carbon draws comboboxes (and other views) outside the rect given as argument. Use this function to obtain
1138 the corresponding inner rect for drawing the same combobox so that it stays inside the given outerBounds.
1139*/
1140HIRect QMacStylePrivate::comboboxInnerBounds(const HIRect &outerBounds, int buttonKind)
1141{
1142 HIRect innerBounds = outerBounds;
1143 // Carbon draw parts of the view outside the rect.
1144 // So make the rect a bit smaller to compensate
1145 // (I wish HIThemeGetButtonBackgroundBounds worked)
1146 switch (buttonKind){
1147 case kThemePopupButton:
1148 innerBounds.origin.x += 2;
1149 innerBounds.origin.y += 3;
1150 innerBounds.size.width -= 5;
1151 innerBounds.size.height -= 6;
1152 break;
1153 case kThemePopupButtonSmall:
1154 innerBounds.origin.x += 3;
1155 innerBounds.origin.y += 3;
1156 innerBounds.size.width -= 6;
1157 innerBounds.size.height -= 7;
1158 break;
1159 case kThemePopupButtonMini:
1160 innerBounds.origin.x += 2;
1161 innerBounds.origin.y += 2;
1162 innerBounds.size.width -= 5;
1163 innerBounds.size.height -= 6;
1164 break;
1165 case kThemeComboBox:
1166 innerBounds.origin.x += 3;
1167 innerBounds.origin.y += 3;
1168 innerBounds.size.width -= 6;
1169 innerBounds.size.height -= 6;
1170 break;
1171 case kThemeComboBoxSmall:
1172 innerBounds.origin.x += 3;
1173 innerBounds.origin.y += 3;
1174 innerBounds.size.width -= 7;
1175 innerBounds.size.height -= 8;
1176 break;
1177 case kThemeComboBoxMini:
1178 innerBounds.origin.x += 3;
1179 innerBounds.origin.y += 3;
1180 innerBounds.size.width -= 4;
1181 innerBounds.size.height -= 8;
1182 break;
1183 default:
1184 break;
1185 }
1186 return innerBounds;
1187}
1188
1189/**
1190 Inside a combobox Qt places a line edit widget. The size of this widget should depend on the kind
1191 of combobox we choose to draw. This function calculates and returns this size.
1192*/
1193QRect QMacStylePrivate::comboboxEditBounds(const QRect &outerBounds, const HIThemeButtonDrawInfo &bdi)
1194{
1195 QRect ret = outerBounds;
1196 switch (bdi.kind){
1197 case kThemeComboBox:
1198 ret.adjust(5, 8, -21, -4);
1199 break;
1200 case kThemeComboBoxSmall:
1201 ret.adjust(4, 5, -18, 0);
1202 ret.setHeight(16);
1203 break;
1204 case kThemeComboBoxMini:
1205 ret.adjust(4, 5, -16, 0);
1206 ret.setHeight(13);
1207 break;
1208 case kThemePopupButton:
1209 ret.adjust(10, 3, -23, -3);
1210 break;
1211 case kThemePopupButtonSmall:
1212 ret.adjust(9, 3, -20, -3);
1213 break;
1214 case kThemePopupButtonMini:
1215 ret.adjust(8, 3, -19, 0);
1216 ret.setHeight(13);
1217 break;
1218 }
1219 return ret;
1220}
1221
1222/**
1223 Carbon comboboxes don't scale (sight). If the size of the combo suggest a scaled version,
1224 create it manually by drawing a small Carbon combo onto a pixmap (use pixmap cache), chop
1225 it up, and copy it back onto the widget. Othervise, draw the combobox supplied by Carbon directly.
1226*/
1227void QMacStylePrivate::drawCombobox(const HIRect &outerBounds, const HIThemeButtonDrawInfo &bdi, QPainter *p)
1228{
1229 if (!(bdi.kind == kThemeComboBox && outerBounds.size.height > 28)){
1230 // We have an unscaled combobox, or popup-button; use Carbon directly.
1231 HIRect innerBounds = QMacStylePrivate::comboboxInnerBounds(outerBounds, bdi.kind);
1232 HIThemeDrawButton(&innerBounds, &bdi, QMacCGContext(p), kHIThemeOrientationNormal, 0);
1233 } else {
1234 QPixmap buffer;
1235 QString key = QString(QLatin1String("$qt_cbox%1-%2")).arg(int(bdi.state)).arg(int(bdi.adornment));
1236 if (!QPixmapCache::find(key, buffer)) {
1237 HIRect innerBoundsSmallCombo = {{3, 3}, {29, 25}};
1238 buffer = QPixmap(35, 28);
1239 buffer.fill(Qt::transparent);
1240 QPainter buffPainter(&buffer);
1241 HIThemeDrawButton(&innerBoundsSmallCombo, &bdi, QMacCGContext(&buffPainter), kHIThemeOrientationNormal, 0);
1242 buffPainter.end();
1243 QPixmapCache::insert(key, buffer);
1244 }
1245
1246 const int bwidth = 20;
1247 const int fwidth = 10;
1248 const int fheight = 10;
1249 int w = qRound(outerBounds.size.width);
1250 int h = qRound(outerBounds.size.height);
1251 int bstart = w - bwidth;
1252 int blower = fheight + 1;
1253 int flower = h - fheight;
1254 int sheight = flower - fheight;
1255 int center = qRound(outerBounds.size.height + outerBounds.origin.y) / 2;
1256
1257 // Draw upper and lower gap
1258 p->drawPixmap(fwidth, 0, bstart - fwidth, fheight, buffer, fwidth, 0, 1, fheight);
1259 p->drawPixmap(fwidth, flower, bstart - fwidth, fheight, buffer, fwidth, buffer.height() - fheight, 1, fheight);
1260 // Draw left and right gap. Right gap is drawn top and bottom separatly
1261 p->drawPixmap(0, fheight, fwidth, sheight, buffer, 0, fheight, fwidth, 1);
1262 p->drawPixmap(bstart, fheight, bwidth, center - fheight, buffer, buffer.width() - bwidth, fheight - 1, bwidth, 1);
1263 p->drawPixmap(bstart, center, bwidth, sheight / 2, buffer, buffer.width() - bwidth, fheight + 6, bwidth, 1);
1264 // Draw arrow
1265 p->drawPixmap(bstart, center - 4, bwidth - 3, 6, buffer, buffer.width() - bwidth, fheight, bwidth - 3, 6);
1266 // Draw corners
1267 p->drawPixmap(0, 0, fwidth, fheight, buffer, 0, 0, fwidth, fheight);
1268 p->drawPixmap(bstart, 0, bwidth, fheight, buffer, buffer.width() - bwidth, 0, bwidth, fheight);
1269 p->drawPixmap(0, flower, fwidth, fheight, buffer, 0, buffer.height() - fheight, fwidth, fheight);
1270 p->drawPixmap(bstart, h - blower, bwidth, blower, buffer, buffer.width() - bwidth, buffer.height() - blower, bwidth, blower);
1271 }
1272}
1273
1274/**
1275 Carbon tableheaders don't scale (sight). So create it manually by drawing a small Carbon header
1276 onto a pixmap (use pixmap cache), chop it up, and copy it back onto the widget.
1277*/
1278void QMacStylePrivate::drawTableHeader(const HIRect &outerBounds,
1279 bool drawTopBorder, bool drawLeftBorder, const HIThemeButtonDrawInfo &bdi, QPainter *p)
1280{
1281 static SInt32 headerHeight = 0;
1282 static OSStatus err = GetThemeMetric(kThemeMetricListHeaderHeight, &headerHeight);
1283 Q_UNUSED(err);
1284
1285 QPixmap buffer;
1286 QString key = QString(QLatin1String("$qt_tableh%1-%2-%3")).arg(int(bdi.state)).arg(int(bdi.adornment)).arg(int(bdi.value));
1287 if (!QPixmapCache::find(key, buffer)) {
1288 HIRect headerNormalRect = {{0., 0.}, {16., CGFloat(headerHeight)}};
1289 buffer = QPixmap(headerNormalRect.size.width, headerNormalRect.size.height);
1290 buffer.fill(Qt::transparent);
1291 QPainter buffPainter(&buffer);
1292 HIThemeDrawButton(&headerNormalRect, &bdi, QMacCGContext(&buffPainter), kHIThemeOrientationNormal, 0);
1293 buffPainter.end();
1294 QPixmapCache::insert(key, buffer);
1295 }
1296 const int buttonw = qRound(outerBounds.size.width);
1297 const int buttonh = qRound(outerBounds.size.height);
1298 const int framew = 1;
1299 const int frameh_n = 4;
1300 const int frameh_s = 3;
1301 const int transh = buffer.height() - frameh_n - frameh_s;
1302 int center = buttonh - frameh_s - int(transh / 2.0f) + 1; // Align bottom;
1303
1304 int skipTopBorder = 0;
1305 if (!drawTopBorder)
1306 skipTopBorder = 1;
1307
1308 p->translate(outerBounds.origin.x, outerBounds.origin.y);
1309
1310 p->drawPixmap(QRect(QRect(0, -skipTopBorder, buttonw - framew , frameh_n)), buffer, QRect(framew, 0, 1, frameh_n));
1311 p->drawPixmap(QRect(0, buttonh - frameh_s, buttonw - framew, frameh_s), buffer, QRect(framew, buffer.height() - frameh_s, 1, frameh_s));
1312 // Draw upper and lower center blocks
1313 p->drawPixmap(QRect(0, frameh_n - skipTopBorder, buttonw - framew, center - frameh_n + skipTopBorder), buffer, QRect(framew, frameh_n, 1, 1));
1314 p->drawPixmap(QRect(0, center, buttonw - framew, buttonh - center - frameh_s), buffer, QRect(framew, buffer.height() - frameh_s, 1, 1));
1315 // Draw right center block borders
1316 p->drawPixmap(QRect(buttonw - framew, frameh_n - skipTopBorder, framew, center - frameh_n), buffer, QRect(buffer.width() - framew, frameh_n, framew, 1));
1317 p->drawPixmap(QRect(buttonw - framew, center, framew, buttonh - center - 1), buffer, QRect(buffer.width() - framew, buffer.height() - frameh_s, framew, 1));
1318 // Draw right corners
1319 p->drawPixmap(QRect(buttonw - framew, -skipTopBorder, framew, frameh_n), buffer, QRect(buffer.width() - framew, 0, framew, frameh_n));
1320 p->drawPixmap(QRect(buttonw - framew, buttonh - frameh_s, framew, frameh_s), buffer, QRect(buffer.width() - framew, buffer.height() - frameh_s, framew, frameh_s));
1321 // Draw center transition block
1322 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));
1323 // Draw right center transition block border
1324 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));
1325 if (drawLeftBorder){
1326 // Draw left center block borders
1327 p->drawPixmap(QRect(0, frameh_n - skipTopBorder, framew, center - frameh_n + skipTopBorder), buffer, QRect(0, frameh_n, framew, 1));
1328 p->drawPixmap(QRect(0, center, framew, buttonh - center - 1), buffer, QRect(0, buffer.height() - frameh_s, framew, 1));
1329 // Draw left corners
1330 p->drawPixmap(QRect(0, -skipTopBorder, framew, frameh_n), buffer, QRect(0, 0, framew, frameh_n));
1331 p->drawPixmap(QRect(0, buttonh - frameh_s, framew, frameh_s), buffer, QRect(0, buffer.height() - frameh_s, framew, frameh_s));
1332 // Draw left center transition block border
1333 p->drawPixmap(QRect(0, center - qRound(transh / 2.0f), framew, buffer.height() - frameh_n - frameh_s), buffer, QRect(0, frameh_n + 1, framew, transh));
1334 }
1335
1336 p->translate(-outerBounds.origin.x, -outerBounds.origin.y);
1337}
1338
1339/*
1340 Returns cutoff sizes for scroll bars.
1341 thumbIndicatorCutoff is the smallest size where the thumb indicator is drawn.
1342 scrollButtonsCutoff is the smallest size where the up/down buttons is drawn.
1343*/
1344enum ScrollBarCutoffType { thumbIndicatorCutoff = 0, scrollButtonsCutoff = 1 };
1345static int scrollButtonsCutoffSize(ScrollBarCutoffType cutoffType, QMacStyle::WidgetSizePolicy widgetSize)
1346{
1347 // Mini scroll bars do not exist as of version 10.4.
1348 if (widgetSize == QMacStyle::SizeMini)
1349 return 0;
1350
1351 const int sizeIndex = (widgetSize == QMacStyle::SizeSmall) ? 1 : 0;
1352 static const int sizeTable[2][2] = { { 61, 56 }, { 49, 44 } };
1353 return sizeTable[sizeIndex][cutoffType];
1354}
1355
1356void QMacStylePrivate::getSliderInfo(QStyle::ComplexControl cc, const QStyleOptionSlider *slider,
1357 HIThemeTrackDrawInfo *tdi, const QWidget *needToRemoveMe)
1358{
1359 memset(tdi, 0, sizeof(HIThemeTrackDrawInfo)); // We don't get it all for some reason or another...
1360 tdi->version = qt_mac_hitheme_version;
1361 tdi->reserved = 0;
1362 tdi->filler1 = 0;
1363 bool isScrollbar = (cc == QStyle::CC_ScrollBar);
1364 switch (aquaSizeConstrain(0, needToRemoveMe)) {
1365 case QAquaSizeUnknown:
1366 case QAquaSizeLarge:
1367 if (isScrollbar)
1368 tdi->kind = kThemeMediumScrollBar;
1369 else
1370 tdi->kind = kThemeMediumSlider;
1371 break;
1372 case QAquaSizeMini:
1373 if (isScrollbar)
1374 tdi->kind = kThemeSmallScrollBar; // should be kThemeMiniScrollBar, but not implemented
1375 else
1376 tdi->kind = kThemeMiniSlider;
1377 break;
1378 case QAquaSizeSmall:
1379 if (isScrollbar)
1380 tdi->kind = kThemeSmallScrollBar;
1381 else
1382 tdi->kind = kThemeSmallSlider;
1383 break;
1384 }
1385 tdi->bounds = qt_hirectForQRect(slider->rect);
1386 tdi->min = slider->minimum;
1387 tdi->max = slider->maximum;
1388 tdi->value = slider->sliderPosition;
1389 tdi->attributes = kThemeTrackShowThumb;
1390 if (slider->upsideDown)
1391 tdi->attributes |= kThemeTrackRightToLeft;
1392 if (slider->orientation == Qt::Horizontal) {
1393 tdi->attributes |= kThemeTrackHorizontal;
1394 if (isScrollbar && slider->direction == Qt::RightToLeft) {
1395 if (!slider->upsideDown)
1396 tdi->attributes |= kThemeTrackRightToLeft;
1397 else
1398 tdi->attributes &= ~kThemeTrackRightToLeft;
1399 }
1400 }
1401
1402 // Tiger broke reverse scroll bars so put them back and "fake it"
1403 if (isScrollbar && (tdi->attributes & kThemeTrackRightToLeft)) {
1404 tdi->attributes &= ~kThemeTrackRightToLeft;
1405 tdi->value = tdi->max - slider->sliderPosition;
1406 }
1407
1408 tdi->enableState = (slider->state & QStyle::State_Enabled) ? kThemeTrackActive
1409 : kThemeTrackDisabled;
1410 if (!(slider->state & QStyle::State_Active))
1411 tdi->enableState = kThemeTrackInactive;
1412 if (!isScrollbar) {
1413 if (slider->state & QStyle::QStyle::State_HasFocus)
1414 tdi->attributes |= kThemeTrackHasFocus;
1415 if (slider->tickPosition == QSlider::NoTicks || slider->tickPosition == QSlider::TicksBothSides)
1416 tdi->trackInfo.slider.thumbDir = kThemeThumbPlain;
1417 else if (slider->tickPosition == QSlider::TicksAbove)
1418 tdi->trackInfo.slider.thumbDir = kThemeThumbUpward;
1419 else
1420 tdi->trackInfo.slider.thumbDir = kThemeThumbDownward;
1421 } else {
1422 tdi->trackInfo.scrollbar.viewsize = slider->pageStep;
1423 }
1424}
1425#endif
1426
1427QMacStylePrivate::QMacStylePrivate(QMacStyle *style)
1428 : timerID(-1), progressFrame(0), q(style), mouseDown(false)
1429{
1430 defaultButtonStart = CFAbsoluteTimeGetCurrent();
1431 memset(&buttonState, 0, sizeof(ButtonState));
1432
1433 if (ptrHIShapeGetBounds == 0) {
1434 QLibrary library(QLatin1String("/System/Library/Frameworks/Carbon.framework/Carbon"));
1435 library.setLoadHints(QLibrary::ExportExternalSymbolsHint);
1436 ptrHIShapeGetBounds = reinterpret_cast<PtrHIShapeGetBounds>(library.resolve("HIShapeGetBounds"));
1437 }
1438
1439}
1440
1441bool QMacStylePrivate::animatable(QMacStylePrivate::Animates as, const QWidget *w) const
1442{
1443 if (!w)
1444 return false;
1445
1446 if (as == AquaPushButton) {
1447 QPushButton *pb = const_cast<QPushButton *>(static_cast<const QPushButton *>(w));
1448 if (w->window()->isActiveWindow() && pb && !mouseDown) {
1449 if (static_cast<const QPushButton *>(w) != defaultButton) {
1450 // Changed on its own, update the value.
1451 const_cast<QMacStylePrivate *>(this)->stopAnimate(as, defaultButton);
1452 const_cast<QMacStylePrivate *>(this)->startAnimate(as, pb);
1453 }
1454 return true;
1455 }
1456 } else if (as == AquaProgressBar) {
1457 if (progressBars.contains((const_cast<QWidget *>(w))))
1458 return true;
1459 }
1460 return false;
1461}
1462
1463void QMacStylePrivate::stopAnimate(QMacStylePrivate::Animates as, QWidget *w)
1464{
1465 if (as == AquaPushButton && defaultButton) {
1466 QPushButton *tmp = defaultButton;
1467 defaultButton = 0;
1468 tmp->update();
1469 } else if (as == AquaProgressBar) {
1470 progressBars.removeAll(w);
1471 }
1472}
1473
1474void QMacStylePrivate::startAnimate(QMacStylePrivate::Animates as, QWidget *w)
1475{
1476 if (as == AquaPushButton)
1477 defaultButton = static_cast<QPushButton *>(w);
1478 else if (as == AquaProgressBar)
1479 progressBars.append(w);
1480 startAnimationTimer();
1481}
1482
1483void QMacStylePrivate::startAnimationTimer()
1484{
1485 if ((defaultButton || !progressBars.isEmpty()) && timerID <= -1)
1486 timerID = startTimer(animateSpeed(AquaListViewItemOpen));
1487}
1488
1489bool QMacStylePrivate::addWidget(QWidget *w)
1490{
1491 //already knew of it
1492 if (static_cast<QPushButton*>(w) == defaultButton
1493 || progressBars.contains(static_cast<QProgressBar*>(w)))
1494 return false;
1495
1496 if (QPushButton *btn = qobject_cast<QPushButton *>(w)) {
1497 btn->installEventFilter(this);
1498 if (btn->isDefault() || (btn->autoDefault() && btn->hasFocus()))
1499 startAnimate(AquaPushButton, btn);
1500 return true;
1501 } else {
1502 bool isProgressBar = (qobject_cast<QProgressBar *>(w)
1503#ifdef QT3_SUPPORT
1504 || w->inherits("Q3ProgressBar")
1505#endif
1506 );
1507 if (isProgressBar) {
1508 w->installEventFilter(this);
1509 startAnimate(AquaProgressBar, w);
1510 return true;
1511 }
1512 }
1513 if (w->isWindow()) {
1514 w->installEventFilter(this);
1515 return true;
1516 }
1517 return false;
1518}
1519
1520void QMacStylePrivate::removeWidget(QWidget *w)
1521{
1522 QPushButton *btn = qobject_cast<QPushButton *>(w);
1523 if (btn && btn == defaultButton) {
1524 stopAnimate(AquaPushButton, btn);
1525 } else if (qobject_cast<QProgressBar *>(w)
1526#ifdef QT3_SUPPORT
1527 || w->inherits("Q3ProgressBar")
1528#endif
1529 ) {
1530 stopAnimate(AquaProgressBar, w);
1531 }
1532}
1533
1534ThemeDrawState QMacStylePrivate::getDrawState(QStyle::State flags)
1535{
1536 ThemeDrawState tds = kThemeStateActive;
1537 if (flags & QStyle::State_Sunken) {
1538 tds = kThemeStatePressed;
1539 } else if (flags & QStyle::State_Active) {
1540 if (!(flags & QStyle::State_Enabled))
1541 tds = kThemeStateUnavailable;
1542 } else {
1543 if (flags & QStyle::State_Enabled)
1544 tds = kThemeStateInactive;
1545 else
1546 tds = kThemeStateUnavailableInactive;
1547 }
1548 return tds;
1549}
1550
1551void QMacStylePrivate::timerEvent(QTimerEvent *)
1552{
1553 int animated = 0;
1554 if (defaultButton && defaultButton->isEnabled() && defaultButton->window()->isActiveWindow()
1555 && defaultButton->isVisibleTo(0) && (defaultButton->isDefault()
1556 || (defaultButton->autoDefault() && defaultButton->hasFocus()))
1557 && doAnimate(AquaPushButton)) {
1558 ++animated;
1559 defaultButton->update();
1560 }
1561 if (!progressBars.isEmpty()) {
1562 int i = 0;
1563 while (i < progressBars.size()) {
1564 QWidget *maybeProgress = progressBars.at(i);
1565 if (!maybeProgress) {
1566 progressBars.removeAt(i);
1567 } else {
1568 if (QProgressBar *pb = qobject_cast<QProgressBar *>(maybeProgress)) {
1569 if (pb->maximum() == 0 || pb->value() > 0
1570 && pb->value() < pb->maximum()) {
1571 if (doAnimate(AquaProgressBar))
1572 pb->update();
1573 }
1574 }
1575#ifdef QT3_SUPPORT
1576 else {
1577 // Watch me now...
1578 QVariant progress = maybeProgress->property("progress");
1579 QVariant totalSteps = maybeProgress->property("totalSteps");
1580 if (progress.isValid() && totalSteps.isValid()) {
1581 int intProgress = progress.toInt();
1582 int intTotalSteps = totalSteps.toInt();