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

Last change on this file since 808 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));