Remove QWorkspace.
[qt:rb-qtbase.git] / src / widgets / styles / qmacstyle_mac.mm
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
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 <Cocoa/Cocoa.h>
48
49 #include "qmacstyle_mac.h"
50 #include "qmacstyle_mac_p.h"
51 #include "qmacstylepixmaps_mac_p.h"
52
53 #define QMAC_QAQUASTYLE_SIZE_CONSTRAIN
54 //#define DEBUG_SIZE_CONSTRAINT
55
56 #include <private/qcore_mac_p.h>
57 #include <private/qcombobox_p.h>
58 #include <private/qpainter_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 <QtWidgets/qgraphicsproxywidget.h>
101 #include <QtWidgets/qgraphicsview.h>
102 #include <private/qstylehelper_p.h>
103 #include <QtGui/QPlatformFontDatabase>
104
105 QT_BEGIN_NAMESPACE
106
107 // The following constants are used for adjusting the size
108 // of push buttons so that they are drawn inside their bounds.
109 const int QMacStylePrivate::PushButtonLeftOffset = 6;
110 const int QMacStylePrivate::PushButtonTopOffset = 4;
111 const int QMacStylePrivate::PushButtonRightOffset = 12;
112 const int QMacStylePrivate::PushButtonBottomOffset = 12;
113 const int QMacStylePrivate::MiniButtonH = 26;
114 const int QMacStylePrivate::SmallButtonH = 30;
115 const int QMacStylePrivate::BevelButtonW = 50;
116 const int QMacStylePrivate::BevelButtonH = 22;
117 const int QMacStylePrivate::PushButtonContentPadding = 6;
118
119 // These colors specify the titlebar gradient colors on
120 // Leopard. Ideally we should get them from the system.
121 static const QColor titlebarGradientActiveBegin(220, 220, 220);
122 static const QColor titlebarGradientActiveEnd(151, 151, 151);
123 static const QColor titlebarSeparatorLineActive(111, 111, 111);
124 static const QColor titlebarGradientInactiveBegin(241, 241, 241);
125 static const QColor titlebarGradientInactiveEnd(207, 207, 207);
126 static const QColor titlebarSeparatorLineInactive(131, 131, 131);
127
128 // Gradient colors used for the dock widget title bar and
129 // non-unifed tool bar bacground.
130 static const QColor mainWindowGradientBegin(240, 240, 240);
131 static const QColor mainWindowGradientEnd(200, 200, 200);
132
133 static const int DisclosureOffset = 4;
134
135 // Resolve these at run-time, since the functions was moved in Leopard.
136 typedef HIRect * (*PtrHIShapeGetBounds)(HIShapeRef, HIRect *);
137 static PtrHIShapeGetBounds ptrHIShapeGetBounds = 0;
138
139 static int closeButtonSize = 12;
140 static bool isVerticalTabs(const QTabBar::Shape shape) {
141     return (shape == QTabBar::RoundedEast
142                 || shape == QTabBar::TriangularEast
143                 || shape == QTabBar::RoundedWest
144                 || shape == QTabBar::TriangularWest);
145 }
146
147 void drawTabCloseButton(QPainter *p, bool hover, bool active, bool selected)
148 {
149     // draw background circle
150     p->setRenderHints(QPainter::Antialiasing);
151     QRect rect(0, 0, closeButtonSize, closeButtonSize);
152     QColor background;
153     if (hover) {
154         background = QColor(124, 124, 124);
155     } else {
156         if (active) {
157             if (selected)
158                 background = QColor(104, 104, 104);
159             else
160                 background = QColor(83, 83, 83);
161         } else {
162             if (selected)
163                 background = QColor(144, 144, 144);
164             else
165                 background = QColor(114, 114, 114);
166         }
167     }
168     p->setPen(Qt::transparent);
169     p->setBrush(background);
170     p->drawEllipse(rect);
171
172     // draw cross
173     int min = 3;
174     int max = 9;
175     QPen crossPen;
176     crossPen.setColor(QColor(194, 194, 194));
177     crossPen.setWidthF(1.3);
178     crossPen.setCapStyle(Qt::FlatCap);
179     p->setPen(crossPen);
180     p->drawLine(min, min, max, max);
181     p->drawLine(min, max, max, min);
182 }
183
184 QRect rotateTabPainter(QPainter *p, QTabBar::Shape shape, QRect tabRect)
185 {
186     if (isVerticalTabs(shape)) {
187         int newX, newY, newRot;
188         if (shape == QTabBar::RoundedEast
189             || shape == QTabBar::TriangularEast) {
190             newX = tabRect.width();
191             newY = tabRect.y();
192             newRot = 90;
193         } else {
194             newX = 0;
195             newY = tabRect.y() + tabRect.height();
196             newRot = -90;
197         }
198         tabRect.setRect(0, 0, tabRect.height(), tabRect.width());
199         QMatrix m;
200         m.translate(newX, newY);
201         m.rotate(newRot);
202         p->setMatrix(m, true);
203     }
204     return tabRect;
205 }
206
207 void drawTabShape(QPainter *p, const QStyleOptionTabV3 *tabOpt)
208 {
209     QRect r = tabOpt->rect;
210     p->translate(tabOpt->rect.x(), tabOpt->rect.y());
211     r.moveLeft(0);
212     r.moveTop(0);
213     QRect tabRect = rotateTabPainter(p, tabOpt->shape, r);
214
215     int width = tabRect.width();
216     int height = 20;
217     bool active = (tabOpt->state & QStyle::State_Active);
218     bool selected = (tabOpt->state & QStyle::State_Selected);
219
220     if (selected) {
221         QRect rect(1, 0, width - 2, height);
222
223         // fill body
224         if (active) {
225             int d = (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) ? 16 : 0;
226             p->fillRect(rect, QColor(151 + d, 151 + d, 151 + d));
227         } else {
228             int d = (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) ? 9 : 0;
229             QLinearGradient gradient(rect.topLeft(), rect.bottomLeft());
230             gradient.setColorAt(0, QColor(207 + d, 207 + d, 207 + d));
231             gradient.setColorAt(0.5, QColor(206 + d, 206 + d, 206 + d));
232             gradient.setColorAt(1, QColor(201 + d, 201 + d, 201 + d));
233             p->fillRect(rect, gradient);
234         }
235
236         // draw border
237         QColor borderSides;
238         QColor borderBottom;
239         if (active) {
240             borderSides = QColor(88, 88, 88);
241             borderBottom = QColor(88, 88, 88);
242         } else {
243             borderSides = QColor(121, 121, 121);
244             borderBottom = QColor(116, 116, 116);
245         }
246
247         p->setPen(borderSides);
248
249         int bottom = height;
250         // left line
251         p->drawLine(0, 1, 0, bottom-2);
252         // right line
253         p->drawLine(width-1, 1, width-1, bottom-2);
254
255         // bottom line
256         if (active) {
257             p->setPen(QColor(168, 168, 168));
258             p->drawLine(3, bottom-1, width-3, bottom-1);
259         }
260         p->setPen(borderBottom);
261         p->drawLine(2, bottom, width-2, bottom);
262
263         int w = 3;
264         QRectF rectangleLeft(1, height - w, w, w);
265         QRectF rectangleRight(width - 2, height - 1, w, w);
266         int startAngle = 180 * 16;
267         int spanAngle = 90 * 16;
268         p->setRenderHint(QPainter::Antialiasing);
269         p->drawArc(rectangleLeft, startAngle, spanAngle);
270         p->drawArc(rectangleRight, startAngle, -spanAngle);
271     } else {
272         // when the mouse is over non selected tabs they get a new color
273         bool hover = (tabOpt->state & QStyle::State_MouseOver);
274         if (hover) {
275             QRect rect(1, 2, width - 1, height - 1);
276             p->fillRect(rect, QColor(110, 110, 110));
277         }
278
279         // seperator lines between tabs
280         bool west = (tabOpt->shape == QTabBar::RoundedWest || tabOpt->shape == QTabBar::TriangularWest);
281         bool drawOnRight = !west;
282         if ((!drawOnRight && tabOpt->selectedPosition != QStyleOptionTab::NextIsSelected)
283             || (drawOnRight && tabOpt->selectedPosition != QStyleOptionTab::NextIsSelected)) {
284             QColor borderColor;
285             QColor borderHighlightColor;
286             if (active) {
287                 borderColor = QColor(64, 64, 64);
288                 borderHighlightColor = QColor(140, 140, 140);
289             } else {
290                 borderColor = QColor(135, 135, 135);
291                 borderHighlightColor = QColor(178, 178, 178);
292             }
293
294             int x = drawOnRight ? width : 0;
295
296             // tab seperator line
297             p->setPen(borderColor);
298             p->drawLine(x, 2, x, height + 1);
299
300             // tab seperator highlight
301             p->setPen(borderHighlightColor);
302             p->drawLine(x-1, 2, x-1, height + 1);
303             p->drawLine(x+1, 2, x+1, height + 1);
304         }
305     }
306 }
307
308 void drawTabBase(QPainter *p, const QStyleOptionTabBarBaseV2 *tbb, const QWidget *w)
309 {
310     QRect r = tbb->rect;
311     if (isVerticalTabs(tbb->shape)) {
312         r.setWidth(w->width());
313     } else {
314         r.setHeight(w->height());
315     }
316     QRect tabRect = rotateTabPainter(p, tbb->shape, r);
317     int width = tabRect.width();
318     int height = tabRect.height();
319     bool active = (tbb->state & QStyle::State_Active);
320
321     // top border lines
322     QColor borderHighlightTop;
323     QColor borderTop;
324     if (active) {
325         borderTop = QColor(64, 64, 64);
326         borderHighlightTop = QColor(174, 174, 174);
327     } else {
328         borderTop = QColor(135, 135, 135);
329         borderHighlightTop = QColor(207, 207, 207);
330     }
331     p->setPen(borderHighlightTop);
332     p->drawLine(tabRect.x(), 0, width, 0);
333     p->setPen(borderTop);
334     p->drawLine(tabRect.x(), 1, width, 1);
335
336     // center block
337     QRect centralRect(tabRect.x(), 2, width, height - 2);
338     if (active) {
339         QColor mainColor = QColor(120, 120, 120);
340         p->fillRect(centralRect, mainColor);
341     } else {
342         QLinearGradient gradient(centralRect.topLeft(), centralRect.bottomLeft());
343         gradient.setColorAt(0, QColor(165, 165, 165));
344         gradient.setColorAt(0.5, QColor(164, 164, 164));
345         gradient.setColorAt(1, QColor(158, 158, 158));
346         p->fillRect(centralRect, gradient);
347     }
348
349     // bottom border lines
350     QColor borderHighlightBottom;
351     QColor borderBottom;
352     if (active) {
353         borderHighlightBottom = QColor(153, 153, 153);
354         borderBottom = QColor(64, 64, 64);
355     } else {
356         borderHighlightBottom = QColor(177, 177, 177);
357         borderBottom = QColor(127, 127, 127);
358     }
359     p->setPen(borderHighlightBottom);
360     p->drawLine(tabRect.x(), height - 2, width, height - 2);
361     p->setPen(borderBottom);
362     p->drawLine(tabRect.x(), height - 1, width, height - 1);
363 }
364
365 static int getControlSize(const QStyleOption *option, const QWidget *widget)
366 {
367     if (option) {
368         if (option->state & (QStyle::State_Small | QStyle::State_Mini))
369             return (option->state & QStyle::State_Mini) ? QAquaSizeMini : QAquaSizeSmall;
370     } else if (widget) {
371         switch (QMacStyle::widgetSizePolicy(widget)) {
372         case QMacStyle::SizeSmall:
373             return QAquaSizeSmall;
374         case QMacStyle::SizeMini:
375             return QAquaSizeMini;
376         default:
377             break;
378         }
379     }
380     return QAquaSizeLarge;
381 }
382
383
384 static inline bool isTreeView(const QWidget *widget)
385 {
386     return (widget && widget->parentWidget() &&
387             (qobject_cast<const QTreeView *>(widget->parentWidget())
388              ));
389 }
390
391 static inline ThemeTabDirection getTabDirection(QTabBar::Shape shape)
392 {
393     ThemeTabDirection ttd;
394     switch (shape) {
395     case QTabBar::RoundedSouth:
396     case QTabBar::TriangularSouth:
397         ttd = kThemeTabSouth;
398         break;
399     default:  // Added to remove the warning, since all values are taken care of, really!
400     case QTabBar::RoundedNorth:
401     case QTabBar::TriangularNorth:
402         ttd = kThemeTabNorth;
403         break;
404     case QTabBar::RoundedWest:
405     case QTabBar::TriangularWest:
406         ttd = kThemeTabWest;
407         break;
408     case QTabBar::RoundedEast:
409     case QTabBar::TriangularEast:
410         ttd = kThemeTabEast;
411         break;
412     }
413     return ttd;
414 }
415
416 static QString qt_mac_removeMnemonics(const QString &original)
417 {
418     QString returnText(original.size(), 0);
419     int finalDest = 0;
420     int currPos = 0;
421     int l = original.length();
422     while (l) {
423         if (original.at(currPos) == QLatin1Char('&')
424             && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) {
425             ++currPos;
426             --l;
427             if (l == 0)
428                 break;
429         }
430         returnText[finalDest] = original.at(currPos);
431         ++currPos;
432         ++finalDest;
433         --l;
434     }
435     returnText.truncate(finalDest);
436     return returnText;
437 }
438
439 class QMacCGContext
440 {
441     CGContextRef context;
442 public:
443     QMacCGContext(QPainter *p); //qpaintengine_mac.cpp
444     inline QMacCGContext() { context = 0; }
445     inline QMacCGContext(const QPaintDevice *pdev) {
446         extern CGContextRef qt_mac_cg_context(const QPaintDevice *);
447         context = qt_mac_cg_context(pdev);
448     }
449     inline QMacCGContext(CGContextRef cg, bool takeOwnership=false) {
450         context = cg;
451         if (!takeOwnership)
452             CGContextRetain(context);
453     }
454     inline QMacCGContext(const QMacCGContext &copy) : context(0) { *this = copy; }
455     inline ~QMacCGContext() {
456         if (context)
457             CGContextRelease(context);
458     }
459     inline bool isNull() const { return context; }
460     inline operator CGContextRef() { return context; }
461     inline QMacCGContext &operator=(const QMacCGContext &copy) {
462         if (context)
463             CGContextRelease(context);
464         context = copy.context;
465         CGContextRetain(context);
466         return *this;
467     }
468     inline QMacCGContext &operator=(CGContextRef cg) {
469         if (context)
470             CGContextRelease(context);
471         context = cg;
472         CGContextRetain(context); //we do not take ownership
473         return *this;
474     }
475 };
476
477 static QColor qcolorFromCGColor(CGColorRef cgcolor)
478 {
479     QColor pc;
480     CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(cgcolor));
481     const CGFloat *components = CGColorGetComponents(cgcolor);
482     if (model == kCGColorSpaceModelRGB) {
483         pc.setRgbF(components[0], components[1], components[2], components[3]);
484     } else if (model == kCGColorSpaceModelCMYK) {
485         pc.setCmykF(components[0], components[1], components[2], components[3]);
486     } else if (model == kCGColorSpaceModelMonochrome) {
487         pc.setRgbF(components[0], components[0], components[0], components[1]);
488     } else {
489         // Colorspace we can't deal with.
490         qWarning("Qt: qcolorFromCGColor: cannot convert from colorspace model: %d", model);
491         Q_ASSERT(false);
492     }
493     return pc;
494 }
495
496 static inline QColor leopardBrush(ThemeBrush brush)
497 {
498     QCFType<CGColorRef> cgClr = 0;
499     HIThemeBrushCreateCGColor(brush, &cgClr);
500     return qcolorFromCGColor(cgClr);
501 }
502
503 QColor qcolorForTheme(ThemeBrush brush)
504 {
505     return leopardBrush(brush);
506 }
507
508 OSStatus qt_mac_shape2QRegionHelper(int inMessage, HIShapeRef, const CGRect *inRect, void *inRefcon)
509 {
510     QRegion *region = static_cast<QRegion *>(inRefcon);
511     if (!region)
512         return paramErr;
513
514     switch (inMessage) {
515     case kHIShapeEnumerateRect:
516         *region += QRect(inRect->origin.x, inRect->origin.y,
517                          inRect->size.width, inRect->size.height);
518         break;
519     case kHIShapeEnumerateInit:
520         // Assume the region is already setup correctly
521     case kHIShapeEnumerateTerminate:
522     default:
523         break;
524     }
525     return noErr;
526 }
527
528
529 /*!
530     \internal
531      Create's a mutable shape, it's the caller's responsibility to release.
532      WARNING: this function clamps the coordinates to SHRT_MIN/MAX on 10.4 and below.
533 */
534 HIMutableShapeRef qt_mac_toHIMutableShape(const QRegion &region)
535 {
536     HIMutableShapeRef shape = HIShapeCreateMutable();
537     if (region.rectCount() < 2 ) {
538         QRect qtRect = region.boundingRect();
539         CGRect cgRect = CGRectMake(qtRect.x(), qtRect.y(), qtRect.width(), qtRect.height());
540         HIShapeUnionWithRect(shape, &cgRect);
541     } else {
542         foreach (const QRect &qtRect, region.rects()) {
543             CGRect cgRect = CGRectMake(qtRect.x(), qtRect.y(), qtRect.width(), qtRect.height());
544             HIShapeUnionWithRect(shape, &cgRect);
545         }
546     }
547     return shape;
548 }
549
550 QRegion qt_mac_fromHIShapeRef(HIShapeRef shape)
551 {
552     QRegion returnRegion;
553     //returnRegion.detach();
554     HIShapeEnumerate(shape, kHIShapeParseFromTopLeft, qt_mac_shape2QRegionHelper, &returnRegion);
555     return returnRegion;
556 }
557
558 CGColorSpaceRef m_genericColorSpace = 0;
559 QHash<CGDirectDisplayID, CGColorSpaceRef> m_displayColorSpaceHash;
560 bool m_postRoutineRegistered = false;
561
562 CGColorSpaceRef qt_mac_displayColorSpace(const QWidget *widget);
563 CGColorSpaceRef qt_mac_genericColorSpace()
564 {
565 #if 0
566     if (!m_genericColorSpace) {
567 #if MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4
568         if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
569             m_genericColorSpace = CGColorSpaceCreateWithName(kCGColorSpaceGenericRGB);
570         } else
571 #endif
572         {
573             m_genericColorSpace = CGColorSpaceCreateDeviceRGB();
574         }
575         if (!m_postRoutineRegistered) {
576             m_postRoutineRegistered = true;
577             qAddPostRoutine(QCoreGraphicsPaintEngine::cleanUpMacColorSpaces);
578         }
579     }
580     return m_genericColorSpace;
581 #else
582     // Just return the main display colorspace for the moment.
583     return qt_mac_displayColorSpace(0);
584 #endif
585 }
586
587 /*
588     Ideally, we should pass the widget in here, and use CGGetDisplaysWithRect() etc.
589     to support multiple displays correctly.
590 */
591 CGColorSpaceRef qt_mac_displayColorSpace(const QWidget *widget)
592 {
593     CGColorSpaceRef colorSpace;
594
595     CGDirectDisplayID displayID;
596     CMProfileRef displayProfile = 0;
597     if (widget == 0) {
598         displayID = CGMainDisplayID();
599     } else {
600         displayID = CGMainDisplayID();
601         /*
602         ### get correct display
603         const QRect &qrect = widget->window()->geometry();
604         CGRect rect = CGRectMake(qrect.x(), qrect.y(), qrect.width(), qrect.height());
605         CGDisplayCount throwAway;
606         CGDisplayErr dErr = CGGetDisplaysWithRect(rect, 1, &displayID, &throwAway);
607         if (dErr != kCGErrorSuccess)
608             return macDisplayColorSpace(0); // fall back on main display
609         */
610     }
611     if ((colorSpace = m_displayColorSpaceHash.value(displayID)))
612         return colorSpace;
613
614     CMError err = CMGetProfileByAVID((CMDisplayIDType)displayID, &displayProfile);
615     if (err == noErr) {
616         colorSpace = CGColorSpaceCreateWithPlatformColorSpace(displayProfile);
617     } else if (widget) {
618         return qt_mac_displayColorSpace(0); // fall back on main display
619     }
620
621     if (colorSpace == 0)
622         colorSpace = CGColorSpaceCreateDeviceRGB();
623
624     m_displayColorSpaceHash.insert(displayID, colorSpace);
625     CMCloseProfile(displayProfile);
626     if (!m_postRoutineRegistered) {
627         m_postRoutineRegistered = true;
628         void qt_mac_cleanUpMacColorSpaces();
629         qAddPostRoutine(qt_mac_cleanUpMacColorSpaces);
630     }
631     return colorSpace;
632 }
633
634 void qt_mac_cleanUpMacColorSpaces()
635 {
636     if (m_genericColorSpace) {
637         CFRelease(m_genericColorSpace);
638         m_genericColorSpace = 0;
639     }
640     QHash<CGDirectDisplayID, CGColorSpaceRef>::const_iterator it = m_displayColorSpaceHash.constBegin();
641     while (it != m_displayColorSpaceHash.constEnd()) {
642         if (it.value())
643             CFRelease(it.value());
644         ++it;
645     }
646     m_displayColorSpaceHash.clear();
647 }
648
649 bool qt_macWindowIsTextured(const QWidget *window)
650 {
651     NSWindow *nswindow = static_cast<NSWindow*>(
652         QApplication::platformNativeInterface()->nativeResourceForWindow("NSWindow", window->windowHandle()));
653     if (!nswindow)
654         return false;
655     return ([nswindow styleMask] & NSTexturedBackgroundWindowMask) ? true : false;
656 }
657
658 /*****************************************************************************
659   QMacCGStyle globals
660  *****************************************************************************/
661 const int qt_mac_hitheme_version = 0; //the HITheme version we speak
662 const int macItemFrame         = 2;    // menu item frame width
663 const int macItemHMargin       = 3;    // menu item hor text margin
664 const int macItemVMargin       = 2;    // menu item ver text margin
665 const int macRightBorder       = 12;   // right border on mac
666 const ThemeWindowType QtWinType = kThemeDocumentWindow; // Window type we use for QTitleBar.
667 QPixmap *qt_mac_backgroundPattern = 0; // stores the standard widget background.
668
669 /*****************************************************************************
670   QMacCGStyle utility functions
671  *****************************************************************************/
672 static inline int qt_mac_hitheme_tab_version()
673 {
674     return 1;
675 }
676
677 static inline HIRect qt_hirectForQRect(const QRect &convertRect, const QRect &rect = QRect())
678 {
679     return CGRectMake(convertRect.x() + rect.x(), convertRect.y() + rect.y(),
680                       convertRect.width() - rect.width(), convertRect.height() - rect.height());
681 }
682
683 static inline const QRect qt_qrectForHIRect(const HIRect &hirect)
684 {
685     return QRect(QPoint(int(hirect.origin.x), int(hirect.origin.y)),
686                  QSize(int(hirect.size.width), int(hirect.size.height)));
687 }
688
689 inline bool qt_mac_is_metal(const QWidget *w)
690 {
691     for (; w; w = w->parentWidget()) {
692         if (w->testAttribute(Qt::WA_MacBrushedMetal))
693             return true;
694         if (w->isWindow() && w->testAttribute(Qt::WA_WState_Created)) {  // If not created will fall through to the opaque check and be fine anyway.
695             return qt_macWindowIsTextured(w);
696         }
697         if (w->d_func()->isOpaque)
698             break;
699     }
700     return false;
701 }
702
703 static int qt_mac_aqua_get_metric(ThemeMetric met)
704 {
705     SInt32 ret;
706     GetThemeMetric(met, &ret);
707     return ret;
708 }
709
710 static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QWidget *widg, QSize szHint,
711                                     QAquaWidgetSize sz)
712 {
713     QSize ret(-1, -1);
714     if (sz != QAquaSizeSmall && sz != QAquaSizeLarge && sz != QAquaSizeMini) {
715         qDebug("Not sure how to return this...");
716         return ret;
717     }
718     if ((widg && widg->testAttribute(Qt::WA_SetFont)) || !QApplication::desktopSettingsAware()) {
719         // If you're using a custom font and it's bigger than the default font,
720         // then no constraints for you. If you are smaller, we can try to help you out
721         QFont font = qt_app_fonts_hash()->value(widg->metaObject()->className(), QFont());
722         if (widg->font().pointSize() > font.pointSize())
723             return ret;
724     }
725
726     if (ct == QStyle::CT_CustomBase && widg) {
727         if (qobject_cast<const QPushButton *>(widg))
728             ct = QStyle::CT_PushButton;
729         else if (qobject_cast<const QRadioButton *>(widg))
730             ct = QStyle::CT_RadioButton;
731         else if (qobject_cast<const QCheckBox *>(widg))
732             ct = QStyle::CT_CheckBox;
733         else if (qobject_cast<const QComboBox *>(widg))
734             ct = QStyle::CT_ComboBox;
735         else if (qobject_cast<const QToolButton *>(widg))
736             ct = QStyle::CT_ToolButton;
737         else if (qobject_cast<const QSlider *>(widg))
738             ct = QStyle::CT_Slider;
739         else if (qobject_cast<const QProgressBar *>(widg))
740             ct = QStyle::CT_ProgressBar;
741         else if (qobject_cast<const QLineEdit *>(widg))
742             ct = QStyle::CT_LineEdit;
743         else if (qobject_cast<const QHeaderView *>(widg))
744             ct = QStyle::CT_HeaderSection;
745         else if (qobject_cast<const QMenuBar *>(widg))
746             ct = QStyle::CT_MenuBar;
747         else if (qobject_cast<const QSizeGrip *>(widg))
748             ct = QStyle::CT_SizeGrip;
749         else
750             return ret;
751     }
752
753     switch (ct) {
754     case QStyle::CT_PushButton: {
755         const QPushButton *psh = qobject_cast<const QPushButton *>(widg);
756         // If this comparison is false, then the widget was not a push button.
757         // This is bad and there's very little we can do since we were requested to find a
758         // sensible size for a widget that pretends to be a QPushButton but is not.
759         if(psh) {
760             QString buttonText = qt_mac_removeMnemonics(psh->text());
761             if (buttonText.contains(QLatin1Char('\n')))
762                 ret = QSize(-1, -1);
763             else if (sz == QAquaSizeLarge)
764                 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
765             else if (sz == QAquaSizeSmall)
766                 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPushButtonHeight));
767             else if (sz == QAquaSizeMini)
768                 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPushButtonHeight));
769
770             if (!psh->icon().isNull()){
771                 // If the button got an icon, and the icon is larger than the
772                 // button, we can't decide on a default size
773                 ret.setWidth(-1);
774                 if (ret.height() < psh->iconSize().height())
775                     ret.setHeight(-1);
776             }
777             else if (buttonText == QLatin1String("OK") || buttonText == QLatin1String("Cancel")){
778                 // Aqua Style guidelines restrict the size of OK and Cancel buttons to 68 pixels.
779                 // However, this doesn't work for German, therefore only do it for English,
780                 // I suppose it would be better to do some sort of lookups for languages
781                 // that like to have really long words.
782                 ret.setWidth(77 - 8);
783             }
784         } else {
785             // The only sensible thing to do is to return whatever the style suggests...
786             if (sz == QAquaSizeLarge)
787                 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
788             else if (sz == QAquaSizeSmall)
789                 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPushButtonHeight));
790             else if (sz == QAquaSizeMini)
791                 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPushButtonHeight));
792             else
793                 // Since there's no default size we return the large size...
794                 ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPushButtonHeight));
795          }
796 #if 0 //Not sure we are applying the rules correctly for RadioButtons/CheckBoxes --Sam
797     } else if (ct == QStyle::CT_RadioButton) {
798         QRadioButton *rdo = static_cast<QRadioButton *>(widg);
799         // Exception for case where multiline radio button text requires no size constrainment
800         if (rdo->text().find('\n') != -1)
801             return ret;
802         if (sz == QAquaSizeLarge)
803             ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricRadioButtonHeight));
804         else if (sz == QAquaSizeSmall)
805             ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallRadioButtonHeight));
806         else if (sz == QAquaSizeMini)
807             ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniRadioButtonHeight));
808     } else if (ct == QStyle::CT_CheckBox) {
809         if (sz == QAquaSizeLarge)
810             ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricCheckBoxHeight));
811         else if (sz == QAquaSizeSmall)
812             ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallCheckBoxHeight));
813         else if (sz == QAquaSizeMini)
814             ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniCheckBoxHeight));
815 #endif
816         break;
817     }
818     case QStyle::CT_SizeGrip:
819         if (sz == QAquaSizeLarge || sz == QAquaSizeSmall) {
820             HIRect r;
821             HIPoint p = { 0, 0 };
822             HIThemeGrowBoxDrawInfo gbi;
823             gbi.version = 0;
824             gbi.state = kThemeStateActive;
825             gbi.kind = kHIThemeGrowBoxKindNormal;
826             gbi.direction = QApplication::isRightToLeft() ? kThemeGrowLeft | kThemeGrowDown
827                                                           : kThemeGrowRight | kThemeGrowDown;
828             gbi.size = sz == QAquaSizeSmall ? kHIThemeGrowBoxSizeSmall : kHIThemeGrowBoxSizeNormal;
829             if (HIThemeGetGrowBoxBounds(&p, &gbi, &r) == noErr)
830                 ret = QSize(r.size.width, r.size.height);
831         }
832         break;
833     case QStyle::CT_ComboBox:
834         switch (sz) {
835         case QAquaSizeLarge:
836             ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricPopupButtonHeight));
837             break;
838         case QAquaSizeSmall:
839             ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricSmallPopupButtonHeight));
840             break;
841         case QAquaSizeMini:
842             ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricMiniPopupButtonHeight));
843             break;
844         default:
845             break;
846         }
847         break;
848     case QStyle::CT_ToolButton:
849         if (sz == QAquaSizeSmall) {
850             int width = 0, height = 0;
851             if (szHint == QSize(-1, -1)) { //just 'guess'..
852                 const QToolButton *bt = qobject_cast<const QToolButton *>(widg);
853                 // If this conversion fails then the widget was not what it claimed to be.
854                 if(bt) {
855                     if (!bt->icon().isNull()) {
856                         QSize iconSize = bt->iconSize();
857                         QSize pmSize = bt->icon().actualSize(QSize(32, 32), QIcon::Normal);
858                         width = qMax(width, qMax(iconSize.width(), pmSize.width()));
859                         height = qMax(height, qMax(iconSize.height(), pmSize.height()));
860                     }
861                     if (!bt->text().isNull() && bt->toolButtonStyle() != Qt::ToolButtonIconOnly) {
862                         int text_width = bt->fontMetrics().width(bt->text()),
863                            text_height = bt->fontMetrics().height();
864                         if (bt->toolButtonStyle() == Qt::ToolButtonTextUnderIcon) {
865                             width = qMax(width, text_width);
866                             height += text_height;
867                         } else {
868                             width += text_width;
869                             width = qMax(height, text_height);
870                         }
871                     }
872                 } else {
873                     // Let's return the size hint...
874                     width = szHint.width();
875                     height = szHint.height();
876                 }
877             } else {
878                 width = szHint.width();
879                 height = szHint.height();
880             }
881             width =  qMax(20, width +  5); //border
882             height = qMax(20, height + 5); //border
883             ret = QSize(width, height);
884         }
885         break;
886     case QStyle::CT_Slider: {
887         int w = -1;
888         const QSlider *sld = qobject_cast<const QSlider *>(widg);
889         // If this conversion fails then the widget was not what it claimed to be.
890         if(sld) {
891             if (sz == QAquaSizeLarge) {
892                 if (sld->orientation() == Qt::Horizontal) {
893                     w = qt_mac_aqua_get_metric(kThemeMetricHSliderHeight);
894                     if (sld->tickPosition() != QSlider::NoTicks)
895                         w += qt_mac_aqua_get_metric(kThemeMetricHSliderTickHeight);
896                 } else {
897                     w = qt_mac_aqua_get_metric(kThemeMetricVSliderWidth);
898                     if (sld->tickPosition() != QSlider::NoTicks)
899                         w += qt_mac_aqua_get_metric(kThemeMetricVSliderTickWidth);
900                 }
901             } else if (sz == QAquaSizeSmall) {
902                 if (sld->orientation() == Qt::Horizontal) {
903                     w = qt_mac_aqua_get_metric(kThemeMetricSmallHSliderHeight);
904                     if (sld->tickPosition() != QSlider::NoTicks)
905                         w += qt_mac_aqua_get_metric(kThemeMetricSmallHSliderTickHeight);
906                 } else {
907                     w = qt_mac_aqua_get_metric(kThemeMetricSmallVSliderWidth);
908                     if (sld->tickPosition() != QSlider::NoTicks)
909                         w += qt_mac_aqua_get_metric(kThemeMetricSmallVSliderTickWidth);
910                 }
911             } else if (sz == QAquaSizeMini) {
912                 if (sld->orientation() == Qt::Horizontal) {
913                     w = qt_mac_aqua_get_metric(kThemeMetricMiniHSliderHeight);
914                     if (sld->tickPosition() != QSlider::NoTicks)
915                         w += qt_mac_aqua_get_metric(kThemeMetricMiniHSliderTickHeight);
916                 } else {
917                     w = qt_mac_aqua_get_metric(kThemeMetricMiniVSliderWidth);
918                     if (sld->tickPosition() != QSlider::NoTicks)
919                         w += qt_mac_aqua_get_metric(kThemeMetricMiniVSliderTickWidth);
920                 }
921             }
922         } else {
923             // This is tricky, we were requested to find a size for a slider which is not
924             // a slider. We don't know if this is vertical or horizontal or if we need to
925             // have tick marks or not.
926             // For this case we will return an horizontal slider without tick marks.
927             w = qt_mac_aqua_get_metric(kThemeMetricHSliderHeight);
928             w += qt_mac_aqua_get_metric(kThemeMetricHSliderTickHeight);
929         }
930         if (sld->orientation() == Qt::Horizontal)
931             ret.setHeight(w);
932         else
933             ret.setWidth(w);
934         break;
935     }
936     case QStyle::CT_ProgressBar: {
937         int finalValue = -1;
938         Qt::Orientation orient = Qt::Horizontal;
939         if (const QProgressBar *pb = qobject_cast<const QProgressBar *>(widg))
940             orient = pb->orientation();
941
942         if (sz == QAquaSizeLarge)
943             finalValue = qt_mac_aqua_get_metric(kThemeMetricLargeProgressBarThickness)
944                             + qt_mac_aqua_get_metric(kThemeMetricProgressBarShadowOutset);
945         else
946             finalValue = qt_mac_aqua_get_metric(kThemeMetricNormalProgressBarThickness)
947                             + qt_mac_aqua_get_metric(kThemeMetricSmallProgressBarShadowOutset);
948         if (orient == Qt::Horizontal)
949             ret.setHeight(finalValue);
950         else
951             ret.setWidth(finalValue);
952         break;
953     }
954     case QStyle::CT_LineEdit:
955         if (!widg || !qobject_cast<QComboBox *>(widg->parentWidget())) {
956             //should I take into account the font dimentions of the lineedit? -Sam
957             if (sz == QAquaSizeLarge)
958                 ret = QSize(-1, 22);
959             else
960                 ret = QSize(-1, 19);
961         }
962         break;
963     case QStyle::CT_HeaderSection:
964         if (isTreeView(widg))
965            ret = QSize(-1, qt_mac_aqua_get_metric(kThemeMetricListHeaderHeight));
966         break;
967     case QStyle::CT_MenuBar:
968         if (sz == QAquaSizeLarge) {
969             ret = QSize(-1, [[NSApp mainMenu] menuBarHeight]);
970             // In the qt_mac_set_native_menubar(false) case,
971             // we come it here with a zero-height main menu,
972             // preventing the in-window menu from displaying.
973             // Use 22 pixels for the height, by observation.
974             if (ret.height() <= 0)
975                 ret.setHeight(22);
976         }
977         break;
978     default:
979         break;
980     }
981     return ret;
982 }
983
984
985 #if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT)
986 static QAquaWidgetSize qt_aqua_guess_size(const QWidget *widg, QSize large, QSize small, QSize mini)
987 {
988     if (large == QSize(-1, -1)) {
989         if (small != QSize(-1, -1))
990             return QAquaSizeSmall;
991         if (mini != QSize(-1, -1))
992             return QAquaSizeMini;
993         return QAquaSizeUnknown;
994     } else if (small == QSize(-1, -1)) {
995         if (mini != QSize(-1, -1))
996             return QAquaSizeMini;
997         return QAquaSizeLarge;
998     } else if (mini == QSize(-1, -1)) {
999         return QAquaSizeLarge;
1000     }
1001
1002 #ifndef QT_NO_MAINWINDOW
1003     if (qobject_cast<QDockWidget *>(widg->window()) || !qgetenv("QWIDGET_ALL_SMALL").isNull()) {
1004         //if (small.width() != -1 || small.height() != -1)
1005         return QAquaSizeSmall;
1006     } else if (!qgetenv("QWIDGET_ALL_MINI").isNull()) {
1007         return QAquaSizeMini;
1008     }
1009 #endif
1010
1011 #if 0
1012     /* Figure out which size we're closer to, I just hacked this in, I haven't
1013        tested it as it would probably look pretty strange to have some widgets
1014        big and some widgets small in the same window?? -Sam */
1015     int large_delta=0;
1016     if (large.width() != -1) {
1017         int delta = large.width() - widg->width();
1018         large_delta += delta * delta;
1019     }
1020     if (large.height() != -1) {
1021         int delta = large.height() - widg->height();
1022         large_delta += delta * delta;
1023     }
1024     int small_delta=0;
1025     if (small.width() != -1) {
1026         int delta = small.width() - widg->width();
1027         small_delta += delta * delta;
1028     }
1029     if (small.height() != -1) {
1030         int delta = small.height() - widg->height();
1031         small_delta += delta * delta;
1032     }
1033     int mini_delta=0;
1034     if (mini.width() != -1) {
1035         int delta = mini.width() - widg->width();
1036         mini_delta += delta * delta;
1037     }
1038     if (mini.height() != -1) {
1039         int delta = mini.height() - widg->height();
1040         mini_delta += delta * delta;
1041     }
1042     if (mini_delta < small_delta && mini_delta < large_delta)
1043         return QAquaSizeMini;
1044     else if (small_delta < large_delta)
1045         return QAquaSizeSmall;
1046 #endif
1047     return QAquaSizeLarge;
1048 }
1049 #endif
1050
1051 QAquaWidgetSize QMacStylePrivate::aquaSizeConstrain(const QStyleOption *option, const QWidget *widg,
1052                                        QStyle::ContentsType ct, QSize szHint, QSize *insz) const
1053 {
1054 #if defined(QMAC_QAQUASTYLE_SIZE_CONSTRAIN) || defined(DEBUG_SIZE_CONSTRAINT)
1055     if (option) {
1056         if (option->state & QStyle::State_Small)
1057             return QAquaSizeSmall;
1058         if (option->state & QStyle::State_Mini)
1059             return QAquaSizeMini;
1060     }
1061
1062     if (!widg) {
1063         if (insz)
1064             *insz = QSize();
1065         if (!qgetenv("QWIDGET_ALL_SMALL").isNull())
1066             return QAquaSizeSmall;
1067         if (!qgetenv("QWIDGET_ALL_MINI").isNull())
1068             return QAquaSizeMini;
1069         return QAquaSizeUnknown;
1070     }
1071     QSize large = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeLarge),
1072           small = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeSmall),
1073           mini  = qt_aqua_get_known_size(ct, widg, szHint, QAquaSizeMini);
1074     bool guess_size = false;
1075     QAquaWidgetSize ret = QAquaSizeUnknown;
1076     QMacStyle::WidgetSizePolicy wsp = q->widgetSizePolicy(widg);
1077     if (wsp == QMacStyle::SizeDefault)
1078         guess_size = true;
1079     else if (wsp == QMacStyle::SizeMini)
1080         ret = QAquaSizeMini;
1081     else if (wsp == QMacStyle::SizeSmall)
1082         ret = QAquaSizeSmall;
1083     else if (wsp == QMacStyle::SizeLarge)
1084         ret = QAquaSizeLarge;
1085     if (guess_size)
1086         ret = qt_aqua_guess_size(widg, large, small, mini);
1087
1088     QSize *sz = 0;
1089     if (ret == QAquaSizeSmall)
1090         sz = &small;
1091     else if (ret == QAquaSizeLarge)
1092         sz = &large;
1093     else if (ret == QAquaSizeMini)
1094         sz = &mini;
1095     if (insz)
1096         *insz = sz ? *sz : QSize(-1, -1);
1097 #ifdef DEBUG_SIZE_CONSTRAINT
1098     if (sz) {
1099         const char *size_desc = "Unknown";
1100         if (sz == &small)
1101             size_desc = "Small";
1102         else if (sz == &large)
1103             size_desc = "Large";
1104         else if (sz == &mini)
1105             size_desc = "Mini";
1106         qDebug("%s - %s: %s taken (%d, %d) [%d, %d]",
1107                widg ? widg->objectName().toLatin1().constData() : "*Unknown*",
1108                widg ? widg->metaObject()->className() : "*Unknown*", size_desc, widg->width(), widg->height(),
1109                sz->width(), sz->height());
1110     }
1111 #endif
1112     return ret;
1113 #else
1114     if (insz)
1115         *insz = QSize();
1116     Q_UNUSED(widg);
1117     Q_UNUSED(ct);
1118     Q_UNUSED(szHint);
1119     return QAquaSizeUnknown;
1120 #endif
1121 }
1122
1123 /**
1124     Returns the free space awailable for contents inside the
1125     button (and not the size of the contents itself)
1126 */
1127 HIRect QMacStylePrivate::pushButtonContentBounds(const QStyleOptionButton *btn,
1128                                                  const HIThemeButtonDrawInfo *bdi) const
1129 {
1130     HIRect outerBounds = qt_hirectForQRect(btn->rect);
1131     // Adjust the bounds to correct for
1132     // carbon not calculating the content bounds fully correct
1133     if (bdi->kind == kThemePushButton || bdi->kind == kThemePushButtonSmall){
1134         outerBounds.origin.y += QMacStylePrivate::PushButtonTopOffset;
1135         outerBounds.size.height -= QMacStylePrivate::PushButtonBottomOffset;
1136     } else if (bdi->kind == kThemePushButtonMini) {
1137         outerBounds.origin.y += QMacStylePrivate::PushButtonTopOffset;
1138     }
1139
1140     HIRect contentBounds;
1141     HIThemeGetButtonContentBounds(&outerBounds, bdi, &contentBounds);
1142     return contentBounds;
1143 }
1144
1145 /**
1146     Calculates the size of the button contents.
1147     This includes both the text and the icon.
1148 */
1149 QSize QMacStylePrivate::pushButtonSizeFromContents(const QStyleOptionButton *btn) const
1150 {
1151     QSize csz(0, 0);
1152     QSize iconSize = btn->icon.isNull() ? QSize(0, 0)
1153                 : (btn->iconSize + QSize(QMacStylePrivate::PushButtonContentPadding, 0));
1154     QRect textRect = btn->text.isEmpty() ? QRect(0, 0, 1, 1)
1155                 : btn->fontMetrics.boundingRect(QRect(), Qt::AlignCenter, btn->text);
1156     csz.setWidth(iconSize.width() + textRect.width()
1157              + ((btn->features & QStyleOptionButton::HasMenu)
1158                             ? q->proxy()->pixelMetric(QStyle::PM_MenuButtonIndicator, btn, 0) : 0));
1159     csz.setHeight(qMax(iconSize.height(), textRect.height()));
1160     return csz;
1161 }
1162
1163 /**
1164     Checks if the actual contents of btn fits inside the free content bounds of
1165     'buttonKindToCheck'. Meant as a helper function for 'initHIThemePushButton'
1166     for determining which button kind to use for drawing.
1167 */
1168 bool QMacStylePrivate::contentFitsInPushButton(const QStyleOptionButton *btn,
1169                                                HIThemeButtonDrawInfo *bdi,
1170                                                ThemeButtonKind buttonKindToCheck) const
1171 {
1172     ThemeButtonKind tmp = bdi->kind;
1173     bdi->kind = buttonKindToCheck;
1174     QSize contentSize = pushButtonSizeFromContents(btn);
1175     QRect freeContentRect = qt_qrectForHIRect(pushButtonContentBounds(btn, bdi));
1176     bdi->kind = tmp;
1177     return freeContentRect.contains(QRect(freeContentRect.x(), freeContentRect.y(),
1178                                     contentSize.width(), contentSize.height()));
1179 }
1180
1181 /**
1182     Creates a HIThemeButtonDrawInfo structure that specifies the correct button
1183     kind and other details to use for drawing the given push button. Which
1184     button kind depends on the size of the button, the size of the contents,
1185     explicit user style settings, etc.
1186 */
1187 void QMacStylePrivate::initHIThemePushButton(const QStyleOptionButton *btn,
1188                                              const QWidget *widget,
1189                                              const ThemeDrawState tds,
1190                                              HIThemeButtonDrawInfo *bdi) const
1191 {
1192     bool drawColorless = btn->palette.currentColorGroup() == QPalette::Active;
1193     ThemeDrawState tdsModified = tds;
1194     if (btn->state & QStyle::State_On)
1195         tdsModified = kThemeStatePressed;
1196     bdi->version = qt_mac_hitheme_version;
1197     bdi->state = tdsModified;
1198     bdi->value = kThemeButtonOff;
1199
1200     if (drawColorless && tdsModified == kThemeStateInactive)
1201         bdi->state = kThemeStateActive;
1202     if (btn->state & QStyle::State_HasFocus)
1203         bdi->adornment = kThemeAdornmentFocus;
1204     else
1205         bdi->adornment = kThemeAdornmentNone;
1206
1207
1208     if (btn->features & (QStyleOptionButton::Flat)) {
1209         bdi->kind = kThemeBevelButton;
1210     } else {
1211         switch (aquaSizeConstrain(btn, widget)) {
1212         case QAquaSizeSmall:
1213             bdi->kind = kThemePushButtonSmall;
1214             break;
1215         case QAquaSizeMini:
1216             bdi->kind = kThemePushButtonMini;
1217             break;
1218         case QAquaSizeLarge:
1219             // ... We should honor if the user is explicit about using the
1220             // large button. But right now Qt will specify the large button
1221             // as default rather than QAquaSizeUnknown.
1222             // So we treat it like QAquaSizeUnknown
1223             // to get the dynamic choosing of button kind.
1224         case QAquaSizeUnknown:
1225             // Choose the button kind that closest match the button rect, but at the
1226             // same time displays the button contents without clipping.
1227             bdi->kind = kThemeBevelButton;
1228             if (btn->rect.width() >= QMacStylePrivate::BevelButtonW && btn->rect.height() >= QMacStylePrivate::BevelButtonH){
1229                 if (widget && widget->testAttribute(Qt::WA_MacVariableSize)) {
1230                     if (btn->rect.height() <= QMacStylePrivate::MiniButtonH){
1231                         if (contentFitsInPushButton(btn, bdi, kThemePushButtonMini))
1232                             bdi->kind = kThemePushButtonMini;
1233                     } else if (btn->rect.height() <= QMacStylePrivate::SmallButtonH){
1234                         if (contentFitsInPushButton(btn, bdi, kThemePushButtonSmall))
1235                             bdi->kind = kThemePushButtonSmall;
1236                     } else if (contentFitsInPushButton(btn, bdi, kThemePushButton)) {
1237                         bdi->kind = kThemePushButton;
1238                     }
1239                 } else {
1240                     bdi->kind = kThemePushButton;
1241                 }
1242             }
1243         }
1244     }
1245 }
1246
1247 bool qt_mac_buttonIsRenderedFlat(const QPushButton *pushButton, const QStyleOptionButton *option)
1248 {
1249     QMacStyle *macStyle = qobject_cast<QMacStyle *>(pushButton->style());
1250     if (!macStyle)
1251         return false;
1252     HIThemeButtonDrawInfo bdi;
1253     macStyle->d->initHIThemePushButton(option, pushButton, kThemeStateActive, &bdi);
1254     return bdi.kind == kThemeBevelButton;
1255 }
1256
1257 /**
1258     Creates a HIThemeButtonDrawInfo structure that specifies the correct button
1259     kind and other details to use for drawing the given combobox. Which button
1260     kind depends on the size of the combo, wether or not it is editable,
1261     explicit user style settings, etc.
1262 */
1263 void QMacStylePrivate::initComboboxBdi(const QStyleOptionComboBox *combo, HIThemeButtonDrawInfo *bdi,
1264                                     const QWidget *widget, const ThemeDrawState &tds)
1265 {
1266     bdi->version = qt_mac_hitheme_version;
1267     bdi->adornment = kThemeAdornmentArrowLeftArrow;
1268     bdi->value = kThemeButtonOff;
1269     if (combo->state & QStyle::State_HasFocus)
1270         bdi->adornment = kThemeAdornmentFocus;
1271     bool drawColorless = combo->palette.currentColorGroup() == QPalette::Active && tds == kThemeStateInactive;
1272     if (combo->activeSubControls & QStyle::SC_ComboBoxArrow)
1273         bdi->state = kThemeStatePressed;
1274     else if (drawColorless)
1275         bdi->state = kThemeStateActive;
1276     else
1277         bdi->state = tds;
1278
1279     QAquaWidgetSize aSize = aquaSizeConstrain(combo, widget);
1280     switch (aSize) {
1281     case QAquaSizeMini:
1282         bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxMini)
1283             : ThemeButtonKind(kThemePopupButtonMini);
1284         break;
1285     case QAquaSizeSmall:
1286         bdi->kind = combo->editable ? ThemeButtonKind(kThemeComboBoxSmall)
1287             : ThemeButtonKind(kThemePopupButtonSmall);
1288         break;
1289     case QAquaSizeUnknown:
1290     case QAquaSizeLarge:
1291         // Unless the user explicitly specified large buttons, determine the
1292         // kind by looking at the combox size.
1293         // ... specifying small and mini-buttons it not a current feature of
1294         // Qt (e.g. QWidget::getAttribute(WA_ButtonSize)). But when it is, add
1295         // an extra check here before using the mini and small buttons.
1296         int h = combo->rect.size().height();
1297         if (combo->editable){
1298             if (h < 21)
1299                 bdi->kind = kThemeComboBoxMini;
1300             else if (h < 26)
1301                 bdi->kind = kThemeComboBoxSmall;
1302             else
1303                 bdi->kind = kThemeComboBox;
1304         } else {
1305             // Even if we specify that we want the kThemePopupButton, Carbon
1306             // will use the kThemePopupButtonSmall if the size matches. So we
1307             // do the same size check explicit to have the size of the inner
1308             // text field be correct. Therefore, do this even if the user specifies
1309             // the use of LargeButtons explicit.
1310             if (h < 21)
1311                 bdi->kind = kThemePopupButtonMini;
1312             else if (h < 26)
1313                 bdi->kind = kThemePopupButtonSmall;
1314             else
1315                 bdi->kind = kThemePopupButton;
1316         }
1317         break;
1318     }
1319 }
1320
1321 /**
1322     Carbon draws comboboxes (and other views) outside the rect given as argument. Use this function to obtain
1323     the corresponding inner rect for drawing the same combobox so that it stays inside the given outerBounds.
1324 */
1325 HIRect QMacStylePrivate::comboboxInnerBounds(const HIRect &outerBounds, int buttonKind)
1326 {
1327     HIRect innerBounds = outerBounds;
1328     // Carbon draw parts of the view outside the rect.
1329     // So make the rect a bit smaller to compensate
1330     // (I wish HIThemeGetButtonBackgroundBounds worked)
1331     switch (buttonKind){
1332     case kThemePopupButton:
1333         innerBounds.origin.x += 2;
1334         innerBounds.origin.y += 3;
1335         innerBounds.size.width -= 5;
1336         innerBounds.size.height -= 6;
1337         break;
1338     case kThemePopupButtonSmall:
1339         innerBounds.origin.x += 3;
1340         innerBounds.origin.y += 3;
1341         innerBounds.size.width -= 6;
1342         innerBounds.size.height -= 7;
1343         break;
1344     case kThemePopupButtonMini:
1345         innerBounds.origin.x += 2;
1346         innerBounds.origin.y += 2;
1347         innerBounds.size.width -= 5;
1348         innerBounds.size.height -= 6;
1349         break;
1350     case kThemeComboBox:
1351         innerBounds.origin.x += 3;
1352         innerBounds.origin.y += 3;
1353         innerBounds.size.width -= 6;
1354         innerBounds.size.height -= 6;
1355         break;
1356     case kThemeComboBoxSmall:
1357         innerBounds.origin.x += 3;
1358         innerBounds.origin.y += 3;
1359         innerBounds.size.width -= 7;
1360         innerBounds.size.height -= 8;
1361         break;
1362     case kThemeComboBoxMini:
1363         innerBounds.origin.x += 3;
1364         innerBounds.origin.y += 3;
1365         innerBounds.size.width -= 4;
1366         innerBounds.size.height -= 8;
1367         break;
1368     default:
1369         break;
1370     }
1371     return innerBounds;
1372 }
1373
1374 /**
1375     Inside a combobox Qt places a line edit widget. The size of this widget should depend on the kind
1376     of combobox we choose to draw. This function calculates and returns this size.
1377 */
1378 QRect QMacStylePrivate::comboboxEditBounds(const QRect &outerBounds, const HIThemeButtonDrawInfo &bdi)
1379 {
1380     QRect ret = outerBounds;
1381     switch (bdi.kind){
1382     case kThemeComboBox:
1383         ret.adjust(5, 8, -21, -4);
1384         break;
1385     case kThemeComboBoxSmall:
1386         ret.adjust(4, 5, -18, 0);
1387         ret.setHeight(16);
1388         break;
1389     case kThemeComboBoxMini:
1390         ret.adjust(4, 5, -16, 0);
1391         ret.setHeight(13);
1392         break;
1393     case kThemePopupButton:
1394         ret.adjust(10, 3, -23, -3);
1395         break;
1396     case kThemePopupButtonSmall:
1397         ret.adjust(9, 3, -20, -3);
1398         break;
1399     case kThemePopupButtonMini:
1400         ret.adjust(8, 3, -19, 0);
1401         ret.setHeight(13);
1402         break;
1403     }
1404     return ret;
1405 }
1406
1407 /**
1408     Carbon comboboxes don't scale (sight). If the size of the combo suggest a scaled version,
1409     create it manually by drawing a small Carbon combo onto a pixmap (use pixmap cache), chop
1410     it up, and copy it back onto the widget. Othervise, draw the combobox supplied by Carbon directly.
1411 */
1412 void QMacStylePrivate::drawCombobox(const HIRect &outerBounds, const HIThemeButtonDrawInfo &bdi, QPainter *p)
1413 {
1414     if (!(bdi.kind == kThemeComboBox && outerBounds.size.height > 28)){
1415         // We have an unscaled combobox, or popup-button; use Carbon directly.
1416         HIRect innerBounds = QMacStylePrivate::comboboxInnerBounds(outerBounds, bdi.kind);
1417         HIThemeDrawButton(&innerBounds, &bdi, QMacCGContext(p), kHIThemeOrientationNormal, 0);
1418     } else {
1419         QPixmap buffer;
1420         QString key = QString(QLatin1String("$qt_cbox%1-%2")).arg(int(bdi.state)).arg(int(bdi.adornment));
1421         if (!QPixmapCache::find(key, buffer)) {
1422             HIRect innerBoundsSmallCombo = {{3, 3}, {29, 25}};
1423             buffer = QPixmap(35, 28);
1424             buffer.fill(Qt::transparent);
1425             QPainter buffPainter(&buffer);
1426             HIThemeDrawButton(&innerBoundsSmallCombo, &bdi, QMacCGContext(&buffPainter), kHIThemeOrientationNormal, 0);
1427             buffPainter.end();
1428             QPixmapCache::insert(key, buffer);
1429         }
1430
1431         const int bwidth = 20;
1432         const int fwidth = 10;
1433         const int fheight = 10;
1434         int w = qRound(outerBounds.size.width);
1435         int h = qRound(outerBounds.size.height);
1436         int bstart = w - bwidth;
1437         int blower = fheight + 1;
1438         int flower = h - fheight;
1439         int sheight = flower - fheight;
1440         int center = qRound(outerBounds.size.height + outerBounds.origin.y) / 2;
1441
1442         // Draw upper and lower gap
1443         p->drawPixmap(fwidth, 0, bstart - fwidth, fheight, buffer, fwidth, 0, 1, fheight);
1444         p->drawPixmap(fwidth, flower, bstart - fwidth, fheight, buffer, fwidth, buffer.height() - fheight, 1, fheight);
1445         // Draw left and right gap. Right gap is drawn top and bottom separatly
1446         p->drawPixmap(0, fheight, fwidth, sheight, buffer, 0, fheight, fwidth, 1);
1447         p->drawPixmap(bstart, fheight, bwidth, center - fheight, buffer, buffer.width() - bwidth, fheight - 1, bwidth, 1);
1448         p->drawPixmap(bstart, center, bwidth, sheight / 2, buffer, buffer.width() - bwidth, fheight + 6, bwidth, 1);
1449         // Draw arrow
1450         p->drawPixmap(bstart, center - 4, bwidth - 3, 6, buffer, buffer.width() - bwidth, fheight, bwidth - 3, 6);
1451         // Draw corners
1452         p->drawPixmap(0, 0, fwidth, fheight, buffer, 0, 0, fwidth, fheight);
1453         p->drawPixmap(bstart, 0, bwidth, fheight, buffer, buffer.width() - bwidth, 0, bwidth, fheight);
1454         p->drawPixmap(0, flower, fwidth, fheight, buffer, 0, buffer.height() - fheight, fwidth, fheight);
1455         p->drawPixmap(bstart, h - blower, bwidth, blower, buffer, buffer.width() - bwidth, buffer.height() - blower, bwidth, blower);
1456     }
1457 }
1458
1459 /**
1460     Carbon tableheaders don't scale (sight). So create it manually by drawing a small Carbon header
1461     onto a pixmap (use pixmap cache), chop it up, and copy it back onto the widget.
1462 */
1463 void QMacStylePrivate::drawTableHeader(const HIRect &outerBounds,
1464     bool drawTopBorder, bool drawLeftBorder, const HIThemeButtonDrawInfo &bdi, QPainter *p)
1465 {
1466     static SInt32 headerHeight = 0;
1467     static OSStatus err = GetThemeMetric(kThemeMetricListHeaderHeight, &headerHeight);
1468     Q_UNUSED(err);
1469
1470     QPixmap buffer;
1471     QString key = QString(QLatin1String("$qt_tableh%1-%2-%3")).arg(int(bdi.state)).arg(int(bdi.adornment)).arg(int(bdi.value));
1472     if (!QPixmapCache::find(key, buffer)) {
1473         HIRect headerNormalRect = {{0., 0.}, {16., CGFloat(headerHeight)}};
1474         buffer = QPixmap(headerNormalRect.size.width, headerNormalRect.size.height);
1475         buffer.fill(Qt::transparent);
1476         QPainter buffPainter(&buffer);
1477         HIThemeDrawButton(&headerNormalRect, &bdi, QMacCGContext(&buffPainter), kHIThemeOrientationNormal, 0);
1478         buffPainter.end();
1479         QPixmapCache::insert(key, buffer);
1480     }
1481     const int buttonw = qRound(outerBounds.size.width);
1482     const int buttonh = qRound(outerBounds.size.height);
1483     const int framew = 1;
1484     const int frameh_n = 4;
1485     const int frameh_s = 3;
1486     const int transh = buffer.height() - frameh_n - frameh_s;
1487     int center = buttonh - frameh_s - int(transh / 2.0f) + 1; // Align bottom;
1488
1489     int skipTopBorder = 0;
1490     if (!drawTopBorder)
1491         skipTopBorder = 1;
1492
1493     p->translate(outerBounds.origin.x, outerBounds.origin.y);
1494
1495     p->drawPixmap(QRect(QRect(0, -skipTopBorder, buttonw - framew , frameh_n)), buffer, QRect(framew, 0, 1, frameh_n));
1496     p->drawPixmap(QRect(0, buttonh - frameh_s, buttonw - framew, frameh_s), buffer, QRect(framew, buffer.height() - frameh_s, 1, frameh_s));
1497     // Draw upper and lower center blocks
1498     p->drawPixmap(QRect(0, frameh_n - skipTopBorder, buttonw - framew, center - frameh_n + skipTopBorder), buffer, QRect(framew, frameh_n, 1, 1));
1499     p->drawPixmap(QRect(0, center, buttonw - framew, buttonh - center - frameh_s), buffer, QRect(framew, buffer.height() - frameh_s, 1, 1));
1500     // Draw right center block borders
1501     p->drawPixmap(QRect(buttonw - framew, frameh_n - skipTopBorder, framew, center - frameh_n), buffer, QRect(buffer.width() - framew, frameh_n, framew, 1));
1502     p->drawPixmap(QRect(buttonw - framew, center, framew, buttonh - center - 1), buffer, QRect(buffer.width() - framew, buffer.height() - frameh_s, framew, 1));
1503     // Draw right corners
1504     p->drawPixmap(QRect(buttonw - framew, -skipTopBorder, framew, frameh_n), buffer, QRect(buffer.width() - framew, 0, framew, frameh_n));
1505     p->drawPixmap(QRect(buttonw - framew, buttonh - frameh_s, framew, frameh_s), buffer, QRect(buffer.width() - framew, buffer.height() - frameh_s, framew, frameh_s));
1506     // Draw center transition block
1507     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));
1508     // Draw right center transition block border
1509     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));
1510     if (drawLeftBorder){
1511         // Draw left center block borders
1512         p->drawPixmap(QRect(0, frameh_n - skipTopBorder, framew, center - frameh_n + skipTopBorder), buffer, QRect(0, frameh_n, framew, 1));
1513         p->drawPixmap(QRect(0, center, framew, buttonh - center - 1), buffer, QRect(0, buffer.height() - frameh_s, framew, 1));
1514         // Draw left corners
1515         p->drawPixmap(QRect(0, -skipTopBorder, framew, frameh_n), buffer, QRect(0, 0, framew, frameh_n));
1516         p->drawPixmap(QRect(0, buttonh - frameh_s, framew, frameh_s), buffer, QRect(0, buffer.height() - frameh_s, framew, frameh_s));
1517         // Draw left center transition block border
1518         p->drawPixmap(QRect(0, center - qRound(transh / 2.0f), framew, buffer.height() - frameh_n - frameh_s), buffer, QRect(0, frameh_n + 1, framew, transh));
1519     }
1520
1521     p->translate(-outerBounds.origin.x, -outerBounds.origin.y);
1522 }
1523
1524 /*
1525     Returns cutoff sizes for scroll bars.
1526     thumbIndicatorCutoff is the smallest size where the thumb indicator is drawn.
1527     scrollButtonsCutoff is the smallest size where the up/down buttons is drawn.
1528 */
1529 enum ScrollBarCutoffType { thumbIndicatorCutoff = 0, scrollButtonsCutoff = 1 };
1530 static int scrollButtonsCutoffSize(ScrollBarCutoffType cutoffType, QMacStyle::WidgetSizePolicy widgetSize)
1531 {
1532     // Mini scroll bars do not exist as of version 10.4.
1533     if (widgetSize ==  QMacStyle::SizeMini)
1534         return 0;
1535
1536     const int sizeIndex = (widgetSize == QMacStyle::SizeSmall) ? 1 : 0;
1537     static const int sizeTable[2][2] = { { 61, 56 }, { 49, 44 } };
1538     return sizeTable[sizeIndex][cutoffType];
1539 }
1540
1541 void QMacStylePrivate::getSliderInfo(QStyle::ComplexControl cc, const QStyleOptionSlider *slider,
1542                           HIThemeTrackDrawInfo *tdi, const QWidget *needToRemoveMe)
1543 {
1544     memset(tdi, 0, sizeof(HIThemeTrackDrawInfo)); // We don't get it all for some reason or another...
1545     tdi->version = qt_mac_hitheme_version;
1546     tdi->reserved = 0;
1547     tdi->filler1 = 0;
1548     bool isScrollbar = (cc == QStyle::CC_ScrollBar);
1549     switch (aquaSizeConstrain(0, needToRemoveMe)) {
1550     case QAquaSizeUnknown:
1551     case QAquaSizeLarge:
1552         if (isScrollbar)
1553             tdi->kind = kThemeMediumScrollBar;
1554         else
1555             tdi->kind = kThemeMediumSlider;
1556         break;
1557     case QAquaSizeMini:
1558         if (isScrollbar)
1559             tdi->kind = kThemeSmallScrollBar; // should be kThemeMiniScrollBar, but not implemented
1560         else
1561             tdi->kind = kThemeMiniSlider;
1562         break;
1563     case QAquaSizeSmall:
1564         if (isScrollbar)
1565             tdi->kind = kThemeSmallScrollBar;
1566         else
1567             tdi->kind = kThemeSmallSlider;
1568         break;
1569     }
1570     tdi->bounds = qt_hirectForQRect(slider->rect);
1571     tdi->min = slider->minimum;
1572     tdi->max = slider->maximum;
1573     tdi->value = slider->sliderPosition;
1574     tdi->attributes = kThemeTrackShowThumb;
1575     if (slider->upsideDown)
1576         tdi->attributes |= kThemeTrackRightToLeft;
1577     if (slider->orientation == Qt::Horizontal) {
1578         tdi->attributes |= kThemeTrackHorizontal;
1579         if (isScrollbar && slider->direction == Qt::RightToLeft) {
1580             if (!slider->upsideDown)
1581                 tdi->attributes |= kThemeTrackRightToLeft;
1582             else
1583                 tdi->attributes &= ~kThemeTrackRightToLeft;
1584         }
1585     }
1586
1587     // Tiger broke reverse scroll bars so put them back and "fake it"
1588     if (isScrollbar && (tdi->attributes & kThemeTrackRightToLeft)) {
1589         tdi->attributes &= ~kThemeTrackRightToLeft;
1590         tdi->value = tdi->max - slider->sliderPosition;
1591     }
1592
1593     tdi->enableState = (slider->state & QStyle::State_Enabled) ? kThemeTrackActive
1594                                                              : kThemeTrackDisabled;
1595     if (!(slider->state & QStyle::State_Active))
1596         tdi->enableState = kThemeTrackInactive;
1597     if (!isScrollbar) {
1598         if (slider->state & QStyle::QStyle::State_HasFocus)
1599             tdi->attributes |= kThemeTrackHasFocus;
1600         if (slider->tickPosition == QSlider::NoTicks || slider->tickPosition == QSlider::TicksBothSides)
1601             tdi->trackInfo.slider.thumbDir = kThemeThumbPlain;
1602         else if (slider->tickPosition == QSlider::TicksAbove)
1603             tdi->trackInfo.slider.thumbDir = kThemeThumbUpward;
1604         else
1605             tdi->trackInfo.slider.thumbDir = kThemeThumbDownward;
1606     } else {
1607         tdi->trackInfo.scrollbar.viewsize = slider->pageStep;
1608     }
1609 }
1610
1611 QMacStylePrivate::QMacStylePrivate(QMacStyle *style)
1612     : timerID(-1), progressFrame(0), q(style), mouseDown(false)
1613 {
1614     defaultButtonStart = CFAbsoluteTimeGetCurrent();
1615     memset(&buttonState, 0, sizeof(ButtonState));
1616
1617     if (ptrHIShapeGetBounds == 0) {
1618         QLibrary library(QLatin1String("/System/Library/Frameworks/Carbon.framework/Carbon"));
1619         library.setLoadHints(QLibrary::ExportExternalSymbolsHint);
1620                 ptrHIShapeGetBounds = reinterpret_cast<PtrHIShapeGetBounds>(library.resolve("HIShapeGetBounds"));
1621     }
1622
1623 }
1624
1625 bool QMacStylePrivate::animatable(QMacStylePrivate::Animates as, const QWidget *w) const
1626 {
1627     if (!w)
1628         return false;
1629
1630     if (as == AquaPushButton) {
1631         QPushButton *pb = const_cast<QPushButton *>(static_cast<const QPushButton *>(w));
1632         if (w->window()->isActiveWindow() && pb && !mouseDown) {
1633             if (static_cast<const QPushButton *>(w) != defaultButton) {
1634                 // Changed on its own, update the value.
1635                 const_cast<QMacStylePrivate *>(this)->stopAnimate(as, defaultButton);
1636                 const_cast<QMacStylePrivate *>(this)->startAnimate(as, pb);
1637             }
1638             return true;
1639         }
1640     } else if (as == AquaProgressBar) {
1641         if (progressBars.contains((const_cast<QWidget *>(w))))
1642             return true;
1643     }
1644     return false;
1645 }
1646
1647 void QMacStylePrivate::stopAnimate(QMacStylePrivate::Animates as, QWidget *w)
1648 {
1649     if (as == AquaPushButton && defaultButton) {
1650         QPushButton *tmp = defaultButton;
1651         defaultButton = 0;
1652         tmp->update();
1653     } else if (as == AquaProgressBar) {
1654         progressBars.removeAll(w);
1655     }
1656 }
1657
1658 void QMacStylePrivate::startAnimate(QMacStylePrivate::Animates as, QWidget *w)
1659 {
1660     if (as == AquaPushButton)
1661         defaultButton = static_cast<QPushButton *>(w);
1662     else if (as == AquaProgressBar)
1663         progressBars.append(w);
1664     startAnimationTimer();
1665 }
1666
1667 void QMacStylePrivate::startAnimationTimer()
1668 {
1669     if ((defaultButton || !progressBars.isEmpty()) && timerID <= -1)
1670         timerID = startTimer(animateSpeed(AquaListViewItemOpen));
1671 }
1672
1673 bool QMacStylePrivate::addWidget(QWidget *w)
1674 {
1675     //already knew of it
1676     if (static_cast<QPushButton*>(w) == defaultButton
1677             || progressBars.contains(static_cast<QProgressBar*>(w)))
1678         return false;
1679
1680     if (QPushButton *btn = qobject_cast<QPushButton *>(w)) {
1681         btn->installEventFilter(this);
1682         if (btn->isDefault() || (btn->autoDefault() && btn->hasFocus()))
1683             startAnimate(AquaPushButton, btn);
1684         return true;
1685     } else {
1686         bool isProgressBar = (qobject_cast<QProgressBar *>(w));
1687         if (isProgressBar) {
1688             w->installEventFilter(this);
1689             startAnimate(AquaProgressBar, w);
1690             return true;
1691         }
1692     }
1693     if (w->isWindow()) {
1694         w->installEventFilter(this);
1695         return true;
1696     }
1697     return false;
1698 }
1699
1700 void QMacStylePrivate::removeWidget(QWidget *w)
1701 {
1702     QPushButton *btn = qobject_cast<QPushButton *>(w);
1703     if (btn && btn == defaultButton) {
1704         stopAnimate(AquaPushButton, btn);
1705     } else if (qobject_cast<QProgressBar *>(w)) {
1706         stopAnimate(AquaProgressBar, w);
1707     }
1708 }
1709
1710 ThemeDrawState QMacStylePrivate::getDrawState(QStyle::State flags)
1711 {
1712     ThemeDrawState tds = kThemeStateActive;
1713     if (flags & QStyle::State_Sunken) {
1714         tds = kThemeStatePressed;
1715     } else if (flags & QStyle::State_Active) {
1716         if (!(flags & QStyle::State_Enabled))
1717             tds = kThemeStateUnavailable;
1718     } else {
1719         if (flags & QStyle::State_Enabled)
1720             tds = kThemeStateInactive;
1721         else
1722             tds = kThemeStateUnavailableInactive;
1723     }
1724     return tds;
1725 }
1726
1727 void QMacStylePrivate::timerEvent(QTimerEvent *)
1728 {
1729     int animated = 0;
1730     if (defaultButton && defaultButton->isEnabled() && defaultButton->window()->isActiveWindow()
1731         && defaultButton->isVisibleTo(0) && (defaultButton->isDefault()
1732         || (defaultButton->autoDefault() && defaultButton->hasFocus()))
1733         && doAnimate(AquaPushButton)) {
1734         ++animated;
1735         defaultButton->update();
1736     }
1737     if (!progressBars.isEmpty()) {
1738         int i = 0;
1739         while (i < progressBars.size()) {
1740             QWidget *maybeProgress = progressBars.at(i);
1741             if (!maybeProgress) {
1742                 progressBars.removeAt(i);
1743             } else {
1744                 if (QProgressBar *pb = qobject_cast<QProgressBar *>(maybeProgress)) {
1745                     if (pb->maximum() == 0 || (pb->value() > 0 && pb->value() < pb->maximum())) {
1746                         if (doAnimate(AquaProgressBar))
1747                             pb->update();
1748                     }
1749                 }
1750                 ++i;
1751             }
1752         }
1753         if (i > 0) {
1754             ++progressFrame;
1755             animated += i;
1756         }
1757     }
1758     if (animated <= 0) {
1759         killTimer(timerID);
1760         timerID = -1;
1761     }
1762 }
1763
1764 bool QMacStylePrivate::eventFilter(QObject *o, QEvent *e)
1765 {
1766     //animate
1767     if (QProgressBar *pb = qobject_cast<QProgressBar *>(o)) {
1768         switch (e->type()) {
1769         default:
1770             break;
1771         case QEvent::Show:
1772             if (!progressBars.contains(pb))
1773                 startAnimate(AquaProgressBar, pb);
1774             break;
1775         case QEvent::Destroy:
1776         case QEvent::Hide:
1777             progressBars.removeAll(pb);
1778         }
1779     } else if (QPushButton *btn = qobject_cast<QPushButton *>(o)) {
1780         switch (e->type()) {
1781         default:
1782             break;
1783         case QEvent::FocusIn:
1784             if (btn->autoDefault())
1785                 startAnimate(AquaPushButton, btn);
1786             break;
1787         case QEvent::Destroy:
1788         case QEvent::Hide:
1789             if (btn == defaultButton)
1790                 stopAnimate(AquaPushButton, btn);
1791             break;
1792         case QEvent::MouseButtonPress:
1793             // It is very confusing to keep the button pulsing, so just stop the animation.
1794             if (static_cast<QMouseEvent *>(e)->button() == Qt::LeftButton)
1795                 mouseDown = true;
1796             stopAnimate(AquaPushButton, btn);
1797             break;
1798         case QEvent::MouseButtonRelease:
1799             if (static_cast<QMouseEvent *>(e)->button() == Qt::LeftButton)
1800                 mouseDown = false;
1801             // fall through
1802         case QEvent::FocusOut:
1803         case QEvent::Show:
1804         case QEvent::WindowActivate: {
1805             QList<QPushButton *> list = btn->window()->findChildren<QPushButton *>();
1806             for (int i = 0; i < list.size(); ++i) {
1807                 QPushButton *pBtn = list.at(i);
1808                 if ((e->type() == QEvent::FocusOut
1809                      && (pBtn->isDefault() || (pBtn->autoDefault() && pBtn->hasFocus()))
1810                      && pBtn != btn)
1811                     || ((e->type() == QEvent::Show || e->type() == QEvent::MouseButtonRelease
1812                          || e->type() == QEvent::WindowActivate)
1813                         && pBtn->isDefault())) {
1814                     if (pBtn->window()->isActiveWindow()) {
1815                         startAnimate(AquaPushButton, pBtn);
1816                     }
1817                     break;
1818                 }
1819             }
1820             break; }
1821         }
1822     }
1823     return false;
1824 }
1825
1826 bool QMacStylePrivate::doAnimate(QMacStylePrivate::Animates as)
1827 {
1828     if (as == AquaPushButton) {
1829     } else if (as == AquaProgressBar) {
1830         // something for later...
1831     } else if (as == AquaListViewItemOpen) {
1832         // To be revived later...
1833     }
1834     return true;
1835 }
1836
1837 void QMacStylePrivate::drawColorlessButton(const HIRect &macRect, HIThemeButtonDrawInfo *bdi,
1838                                            QPainter *p, const QStyleOption *opt) const
1839 {
1840     int xoff = 0,
1841         yoff = 0,
1842         extraWidth = 0,
1843         extraHeight = 0,
1844         finalyoff = 0;
1845
1846     const QStyleOptionComboBox *combo = qstyleoption_cast<const QStyleOptionComboBox *>(opt);
1847     int width = int(macRect.size.width) + extraWidth;
1848     int height = int(macRect.size.height) + extraHeight;
1849
1850     if (width <= 0 || height <= 0)
1851         return;   // nothing to draw
1852
1853     QString key = QLatin1String("$qt_mac_style_ctb_") + QString::number(bdi->kind) + QLatin1Char('_')
1854                   + QString::number(bdi->value) + QLatin1Char('_') + QString::number(width)
1855                   + QLatin1Char('_') + QString::number(height);
1856     QPixmap pm;
1857     if (!QPixmapCache::find(key, pm)) {
1858         QPixmap activePixmap(width, height);
1859         activePixmap.fill(Qt::transparent);
1860         {
1861             if (combo){
1862                 // Carbon combos don't scale. Therefore we draw it
1863                 // ourselves, if a scaled version is needed.
1864                 QPainter tmpPainter(&activePixmap);
1865                 QMacStylePrivate::drawCombobox(macRect, *bdi, &tmpPainter);
1866             }
1867             else {
1868                 QMacCGContext cg(&activePixmap);
1869                 HIRect newRect = CGRectMake(xoff, yoff, macRect.size.width, macRect.size.height);
1870                 HIThemeDrawButton(&newRect, bdi, cg, kHIThemeOrientationNormal, 0);
1871             }
1872         }
1873
1874         if (!combo && bdi->value == kThemeButtonOff) {
1875             pm = activePixmap;
1876         } else if (combo) {
1877             QImage image = activePixmap.toImage();
1878
1879             for (int y = 0; y < height; ++y) {
1880                 QRgb *scanLine = reinterpret_cast<QRgb *>(image.scanLine(y));
1881
1882                 for (int x = 0; x < width; ++x) {
1883                     QRgb &pixel = scanLine[x];
1884
1885                     int darkest = qRed(pixel);
1886                     int mid = qGreen(pixel);
1887                     int lightest = qBlue(pixel);
1888
1889                     if (darkest > mid)
1890                         qSwap(darkest, mid);
1891                     if (mid > lightest)
1892                         qSwap(mid, lightest);
1893                     if (darkest > mid)
1894                         qSwap(darkest, mid);
1895
1896                     int gray = (mid + 2 * lightest) / 3;
1897                     pixel = qRgba(gray, gray, gray, qAlpha(pixel));
1898                 }
1899             }
1900             pm = QPixmap::fromImage(image);
1901         } else {
1902             QImage activeImage = activePixmap.toImage();
1903             QImage colorlessImage;
1904             {
1905                 QPixmap colorlessPixmap(width, height);
1906                 colorlessPixmap.fill(Qt::transparent);
1907
1908                 QMacCGContext cg(&colorlessPixmap);
1909                 HIRect newRect = CGRectMake(xoff, yoff, macRect.size.width, macRect.size.height);
1910                 int oldValue = bdi->value;
1911                 bdi->value = kThemeButtonOff;
1912                 HIThemeDrawButton(&newRect, bdi, cg, kHIThemeOrientationNormal, 0);
1913                 bdi->value = oldValue;
1914                 colorlessImage = colorlessPixmap.toImage();
1915             }
1916
1917             for (int y = 0; y < height; ++y) {
1918                 QRgb *colorlessScanLine = reinterpret_cast<QRgb *>(colorlessImage.scanLine(y));
1919                 const QRgb *activeScanLine = reinterpret_cast<const QRgb *>(activeImage.scanLine(y));
1920
1921                 for (int x = 0; x < width; ++x) {
1922                     QRgb &colorlessPixel = colorlessScanLine[x];
1923                     QRgb activePixel = activeScanLine[x];
1924
1925                     if (activePixel != colorlessPixel) {
1926                         int max = qMax(qMax(qRed(activePixel), qGreen(activePixel)),
1927                                        qBlue(activePixel));
1928                         QRgb newPixel = qRgba(max, max, max, qAlpha(activePixel));
1929                         if (qGray(newPixel) < qGray(colorlessPixel)
1930                                 || qAlpha(newPixel) > qAlpha(colorlessPixel))
1931                             colorlessPixel = newPixel;
1932                     }
1933                 }
1934             }
1935             pm = QPixmap::fromImage(colorlessImage);
1936         }
1937         QPixmapCache::insert(key, pm);
1938     }
1939     p->drawPixmap(int(macRect.origin.x), int(macRect.origin.y) + finalyoff, width, height, pm);
1940 }
1941
1942 QMacStyle::QMacStyle()
1943     : QWindowsStyle()
1944 {
1945     d = new QMacStylePrivate(this);
1946 }
1947
1948 QMacStyle::~QMacStyle()
1949 {
1950     delete qt_mac_backgroundPattern;
1951     qt_mac_backgroundPattern = 0;
1952     delete d;
1953 }
1954
1955 /*! \internal
1956     Generates the standard widget background pattern.
1957 */
1958 QPixmap QMacStylePrivate::generateBackgroundPattern() const
1959 {
1960     QPixmap px(4, 4);
1961     QMacCGContext cg(&px);
1962     HIThemeSetFill(kThemeBrushDialogBackgroundActive, 0, cg, kHIThemeOrientationNormal);
1963     const CGRect cgRect = CGRectMake(0, 0, px.width(), px.height());
1964     CGContextFillRect(cg, cgRect);
1965     return px;
1966 }
1967
1968 /*! \internal
1969     Fills the given \a rect with the pattern stored in \a brush. As an optimization,
1970     HIThemeSetFill us used directly if we are filling with the standard background.
1971 */
1972 void qt_mac_fill_background(QPainter *painter, const QRegion &rgn, const QBrush &brush)
1973 {
1974     QPoint dummy;
1975     const QPaintDevice *target = painter->device();
1976     const QPaintDevice *redirected = QPainter::redirected(target, &dummy);
1977     //const bool usePainter = redirected && redirected != target;
1978
1979 #if 0
1980     if (!usePainter && qt_mac_backgroundPattern
1981         && qt_mac_backgroundPattern->cacheKey() == brush.texture().cacheKey()) {
1982
1983         painter->setClipRegion(rgn);
1984
1985         QCFType<CGContextRef> cg = qt_mac_cg_context(target);
1986         CGContextSaveGState(cg);
1987         HIThemeSetFill(kThemeBrushDialogBackgroundActive, 0, cg, kHIThemeOrientationInverted);
1988
1989         const QVector<QRect> &rects = rgn.rects();
1990         for (int i = 0; i < rects.size(); ++i) {
1991             const QRect rect(rects.at(i));
1992             // Anchor the pattern to the top so it stays put when the window is resized.
1993             CGContextSetPatternPhase(cg, CGSizeMake(rect.width(), rect.height()));
1994             CGRect mac_rect = CGRectMake(rect.x(), rect.y(), rect.width(), rect.height());
1995             CGContextFillRect(cg, mac_rect);
1996         }
1997
1998         CGContextRestoreGState(cg);
1999     } else {
2000 #endif
2001         const QRect rect(rgn.boundingRect());
2002         painter->setClipRegion(rgn);
2003         painter->drawTiledPixmap(rect, brush.texture(), rect.topLeft());
2004 //    }
2005 }
2006
2007 void QMacStyle::polish(QPalette &pal)
2008 {
2009     if (!qt_mac_backgroundPattern) {
2010         if (!qApp)
2011             return;
2012         qt_mac_backgroundPattern = new QPixmap(d->generateBackgroundPattern());
2013     }
2014
2015     QColor pc(Qt::black);
2016     pc = qcolorForTheme(kThemeBrushDialogBackgroundActive);
2017     QBrush background(pc, *qt_mac_backgroundPattern);
2018     pal.setBrush(QPalette::All, QPalette::Window, background);
2019     pal.setBrush(QPalette::All, QPalette::Button, background);
2020
2021     QCFString theme;
2022     const OSErr err = CopyThemeIdentifier(&theme);
2023     if (err == noErr && CFStringCompare(theme, kThemeAppearanceAquaGraphite, 0) == kCFCompareEqualTo) {
2024         pal.setBrush(QPalette::All, QPalette::AlternateBase, QColor(240, 240, 240));
2025     } else {
2026         pal.setBrush(QPalette::All, QPalette::AlternateBase, QColor(237, 243, 254));
2027     }
2028 }
2029
2030 void QMacStyle::polish(QApplication *)
2031 {
2032 }
2033
2034 void QMacStyle::unpolish(QApplication *)
2035 {
2036 }
2037
2038 void QMacStyle::polish(QWidget* w)
2039 {
2040     d->addWidget(w);
2041     if (qt_mac_is_metal(w) && !w->testAttribute(Qt::WA_SetPalette)) {
2042         // Set a clear brush so that the metal shines through.
2043         QPalette pal = w->palette();
2044         QBrush background(Qt::transparent);
2045         pal.setBrush(QPalette::All, QPalette::Window, background);
2046         pal.setBrush(QPalette::All, QPalette::Button, background);
2047         w->setPalette(pal);
2048         w->setAttribute(Qt::WA_SetPalette, false);
2049     }
2050
2051     if (qobject_cast<QMenu*>(w) || qobject_cast<QComboBoxPrivateContainer *>(w)) {
2052         w->setWindowOpacity(QSysInfo::MacintoshVersion >= QSysInfo::MV_10_5 ? 0.985 : 0.94);
2053         if (!w->testAttribute(Qt::WA_SetPalette)) {
2054             QPixmap px(64, 64);
2055             px.fill(Qt::white);
2056             HIThemeMenuDrawInfo mtinfo;
2057             mtinfo.version = qt_mac_hitheme_version;
2058             mtinfo.menuType = kThemeMenuTypePopUp;
2059             HIRect rect = CGRectMake(0, 0, px.width(), px.height());
2060             // ###
2061             //HIThemeDrawMenuBackground(&rect, &mtinfo, QCFType<CGContextRef>(qt_mac_cg_context(&px)),
2062             //                          kHIThemeOrientationNormal);
2063             QPalette pal = w->palette();
2064             QBrush background(px);
2065             pal.setBrush(QPalette::All, QPalette::Window, background);
2066             pal.setBrush(QPalette::All, QPalette::Button, background);
2067             w->setPalette(pal);
2068             w->setAttribute(Qt::WA_SetPalette, false);
2069         }
2070     }
2071
2072     if (QTabBar *tb = qobject_cast<QTabBar*>(w)) {
2073         if (tb->documentMode()) {
2074             w->setAttribute(Qt::WA_Hover);
2075             w->setFont(qt_app_fonts_hash()->value("QSmallFont", QFont()));
2076             QPalette p = w->palette();
2077             p.setColor(QPalette::WindowText, QColor(17, 17, 17));
2078             w->setPalette(p);
2079         }
2080     }
2081
2082     QWindowsStyle::polish(w);
2083
2084     if (QRubberBand *rubber = qobject_cast<QRubberBand*>(w)) {
2085         rubber->setWindowOpacity(0.25);
2086         rubber->setAttribute(Qt::WA_PaintOnScreen, false);
2087         rubber->setAttribute(Qt::WA_NoSystemBackground, false);
2088     }
2089 }
2090
2091 void QMacStyle::unpolish(QWidget* w)
2092 {
2093     d->removeWidget(w);
2094     if ((qobject_cast<QMenu*>(w) || qt_mac_is_metal(w)) && !w->testAttribute(Qt::WA_SetPalette)) {
2095         QPalette pal = qApp->palette(w);
2096         w->setPalette(pal);
2097         w->setAttribute(Qt::WA_SetPalette, false);
2098         w->setWindowOpacity(1.0);
2099     }
2100
2101     if (QComboBox *combo = qobject_cast<QComboBox *>(w)) {
2102         if (!combo->isEditable()) {
2103             if (QWidget *widget = combo->findChild<QComboBoxPrivateContainer *>())
2104                 widget->setWindowOpacity(1.0);
2105         }
2106     }
2107
2108     if (QRubberBand *rubber = qobject_cast<QRubberBand*>(w)) {
2109         rubber->setWindowOpacity(1.0);
2110         rubber->setAttribute(Qt::WA_PaintOnScreen, true);
2111         rubber->setAttribute(Qt::WA_NoSystemBackground, true);
2112     }
2113
2114     if (QFocusFrame *frame = qobject_cast<QFocusFrame *>(w))
2115         frame->setAttribute(Qt::WA_NoSystemBackground, true);
2116
2117     QWindowsStyle::unpolish(w);
2118 }
2119
2120 int QMacStyle::pixelMetric(PixelMetric metric, const QStyleOption *opt, const QWidget *widget) const
2121 {
2122     int controlSize = getControlSize(opt, widget);
2123     SInt32 ret = 0;
2124
2125     switch (metric) {
2126     case PM_TabCloseIndicatorWidth:
2127     case PM_TabCloseIndicatorHeight:
2128         ret = closeButtonSize;
2129         break;
2130     case PM_ToolBarIconSize:
2131         ret = proxy()->pixelMetric(PM_LargeIconSize);
2132         break;
2133     case PM_FocusFrameVMargin:
2134     case PM_FocusFrameHMargin:
2135         GetThemeMetric(kThemeMetricFocusRectOutset, &ret);
2136         break;
2137     case PM_DialogButtonsSeparator:
2138         ret = -5;
2139         break;
2140     case PM_DialogButtonsButtonHeight: {
2141         QSize sz;
2142         ret = d->aquaSizeConstrain(opt, 0, QStyle::CT_PushButton, QSize(-1, -1), &sz);
2143         if (sz == QSize(-1, -1))
2144             ret = 32;
2145         else
2146             ret = sz.height();
2147         break; }
2148     case PM_DialogButtonsButtonWidth: {
2149         QSize sz;
2150         ret = d->aquaSizeConstrain(opt, 0, QStyle::CT_PushButton, QSize(-1, -1), &sz);
2151         if (sz == QSize(-1, -1))
2152             ret = 70;
2153         else
2154             ret = sz.width();
2155         break; }
2156
2157     case PM_MenuBarHMargin:
2158         ret = 8;
2159         break;
2160
2161     case PM_MenuBarVMargin:
2162         ret = 0;
2163         break;
2164
2165     case QStyle::PM_MenuDesktopFrameWidth:
2166         ret = 5;
2167         break;
2168
2169     case PM_CheckBoxLabelSpacing:
2170     case PM_RadioButtonLabelSpacing:
2171         ret = 2;
2172         break;
2173     case PM_MenuScrollerHeight:
2174 #if 0
2175         SInt16 ash, asw;
2176         GetThemeMenuItemExtra(kThemeMenuItemScrollUpArrow, &ash, &asw);
2177         ret = ash;
2178 #else
2179         ret = 15; // I hate having magic numbers in here...
2180 #endif
2181         break;
2182     case PM_DefaultFrameWidth:
2183 #ifndef QT_NO_MAINWINDOW
2184         if (widget && (widget->isWindow() || !widget->parentWidget()
2185                 || (qobject_cast<const QMainWindow*>(widget->parentWidget())
2186                    && static_cast<QMainWindow *>(widget->parentWidget())->centralWidget() == widget))
2187                 && qobject_cast<const QAbstractScrollArea *>(widget))
2188             ret = 0;
2189         else
2190 #endif
2191         // The combo box popup has no frame.
2192         if (qstyleoption_cast<const QStyleOptionComboBox *>(opt) != 0)
2193             ret = 0;
2194         // Frame of mac style line edits is two pixels on top and one on the bottom
2195         else if (qobject_cast<const QLineEdit *>(widget) != 0)
2196             ret = 2;
2197         else
2198             ret = 1;
2199         break;
2200     case PM_MaximumDragDistance:
2201         ret = -1;
2202         break;
2203     case PM_ScrollBarSliderMin:
2204         ret = 24;
2205         break;
2206     case PM_SpinBoxFrameWidth:
2207         GetThemeMetric(kThemeMetricEditTextFrameOutset, &ret);
2208         switch (d->aquaSizeConstrain(opt, widget)) {
2209         default:
2210             ret += 2;
2211             break;
2212         case QAquaSizeMini:
2213             ret += 1;
2214             break;
2215         }
2216         break;
2217     case PM_ButtonShiftHorizontal:
2218     case PM_ButtonShiftVertical:
2219         ret = 0;
2220         break;
2221     case PM_SliderLength:
2222         ret = 17;
2223         break;
2224     case PM_ButtonDefaultIndicator:
2225         ret = 0;
2226         break;
2227     case PM_TitleBarHeight:
2228         if (const QStyleOptionTitleBar *tb = qstyleoption_cast<const QStyleOptionTitleBar *>(opt)) {
2229             HIThemeWindowDrawInfo wdi;
2230             wdi.version = qt_mac_hitheme_version;
2231             wdi.state = kThemeStateActive;
2232             wdi.windowType = QtWinType;
2233             if (tb->titleBarState)
2234                 wdi.attributes = kThemeWindowHasFullZoom | kThemeWindowHasCloseBox
2235                                   | kThemeWindowHasCollapseBox;
2236             else if (tb->titleBarFlags & Qt::WindowSystemMenuHint)
2237                 wdi.attributes = kThemeWindowHasCloseBox;
2238             else
2239                 wdi.attributes = 0;
2240             wdi.titleHeight = tb->rect.height();
2241             wdi.titleWidth = tb->rect.width();
2242             QCFType<HIShapeRef> region;
2243             HIRect hirect = qt_hirectForQRect(tb->rect);
2244             if (hirect.size.width <= 0)
2245                 hirect.size.width = 100;
2246             if (hirect.size.height <= 0)
2247                 hirect.size.height = 30;
2248
2249             HIThemeGetWindowShape(&hirect, &wdi, kWindowTitleBarRgn, &region);
2250             HIRect rect;
2251             ptrHIShapeGetBounds(region, &rect);
2252             ret = int(rect.size.height);
2253             ret += 4;
2254         }
2255         break;
2256     case PM_TabBarTabVSpace:
2257         ret = 4;
2258         break;
2259     case PM_TabBarTabShiftHorizontal:
2260     case PM_TabBarTabShiftVertical:
2261         ret = 0;
2262         break;
2263     case PM_TabBarBaseHeight:
2264         ret = 0;
2265         break;
2266     case PM_TabBarTabOverlap:
2267         ret = 0;
2268         break;
2269     case PM_TabBarBaseOverlap:
2270         switch (d->aquaSizeConstrain(opt, widget)) {
2271         case QAquaSizeUnknown:
2272         case QAquaSizeLarge:
2273             ret = 11;
2274             break;
2275         case QAquaSizeSmall:
2276             ret = 8;
2277             break;
2278         case QAquaSizeMini:
2279             ret = 7;
2280             break;
2281         }
2282         break;
2283     case PM_ScrollBarExtent: {
2284         switch (d->aquaSizeConstrain(opt, widget)) {
2285         case QAquaSizeUnknown:
2286         case QAquaSizeLarge:
2287             GetThemeMetric(kThemeMetricScrollBarWidth, &ret);
2288             break;
2289         case QAquaSizeMini:
2290         case QAquaSizeSmall:
2291             GetThemeMetric(kThemeMetricSmallScrollBarWidth, &ret);
2292             break;
2293         }
2294         break; }
2295     case PM_IndicatorHeight: {
2296         switch (d->aquaSizeConstrain(opt, widget)) {
2297         case QAquaSizeUnknown:
2298         case QAquaSizeLarge:
2299             GetThemeMetric(kThemeMetricCheckBoxHeight, &ret);
2300             break;
2301         case QAquaSizeMini:
2302             GetThemeMetric(kThemeMetricMiniCheckBoxHeight, &ret);
2303             break;
2304         case QAquaSizeSmall:
2305             GetThemeMetric(kThemeMetricSmallCheckBoxHeight, &ret);
2306             break;
2307         }
2308         break; }
2309     case PM_IndicatorWidth: {
2310         switch (d->aquaSizeConstrain(opt, widget)) {
2311         case QAquaSizeUnknown:
2312         case QAquaSizeLarge:
2313             GetThemeMetric(kThemeMetricCheckBoxWidth, &ret);
2314             break;
2315         case QAquaSizeMini:
2316             GetThemeMetric(kThemeMetricMiniCheckBoxWidth, &ret);
2317             break;
2318         case QAquaSizeSmall:
2319             GetThemeMetric(kThemeMetricSmallCheckBoxWidth, &ret);
2320             break;
2321         }
2322         ++ret;
2323         break; }
2324     case PM_ExclusiveIndicatorHeight: {
2325         switch (d->aquaSizeConstrain(opt, widget)) {
2326         case QAquaSizeUnknown:
2327         case QAquaSizeLarge:
2328             GetThemeMetric(kThemeMetricRadioButtonHeight, &ret);
2329             break;
2330         case QAquaSizeMini:
2331             GetThemeMetric(kThemeMetricMiniRadioButtonHeight, &ret);
2332             break;
2333         case QAquaSizeSmall:
2334             GetThemeMetric(kThemeMetricSmallRadioButtonHeight, &ret);
2335             break;
2336         }
2337         break; }
2338     case PM_ExclusiveIndicatorWidth: {
2339         switch (d->aquaSizeConstrain(opt, widget)) {
2340         case QAquaSizeUnknown:
2341         case QAquaSizeLarge:
2342             GetThemeMetric(kThemeMetricRadioButtonWidth, &ret);
2343             break;
2344         case QAquaSizeMini:
2345             GetThemeMetric(kThemeMetricMiniRadioButtonWidth, &ret);
2346             break;
2347         case QAquaSizeSmall:
2348             GetThemeMetric(kThemeMetricSmallRadioButtonWidth, &ret);
2349             break;
2350         }
2351         ++ret;
2352         break; }
2353     case PM_MenuVMargin:
2354         ret = 4;
2355         break;
2356     case PM_MenuPanelWidth:
2357         ret = 0;
2358         break;
2359     case PM_ToolTipLabelFrameWidth:
2360         ret = 0;
2361         break;
2362     case PM_SizeGripSize: {
2363         QAquaWidgetSize aSize;
2364         if (widget && widget->window()->windowType() == Qt::Tool)
2365             aSize = QAquaSizeSmall;
2366         else
2367             aSize = QAquaSizeLarge;
2368         const QSize size = qt_aqua_get_known_size(CT_SizeGrip, widget, QSize(), aSize);
2369         ret = size.width();
2370         break; }
2371     case PM_MdiSubWindowFrameWidth:
2372         ret = 1;
2373         break;
2374     case PM_DockWidgetFrameWidth:
2375         ret = 2;
2376         break;
2377     case PM_DockWidgetTitleMargin:
2378         ret = 0;
2379         break;
2380     case PM_DockWidgetSeparatorExtent:
2381         ret = 1;
2382         break;
2383     case PM_ToolBarHandleExtent:
2384         ret = 11;
2385         break;
2386     case PM_ToolBarItemMargin:
2387         ret = 0;
2388         break;
2389     case PM_ToolBarItemSpacing:
2390         ret = 4;
2391         break;
2392     case PM_SplitterWidth:
2393         ret = qMax(7, QApplication::globalStrut().width());
2394         break;
2395     case PM_LayoutLeftMargin:
2396     case PM_LayoutTopMargin:
2397     case PM_LayoutRightMargin:
2398     case PM_LayoutBottomMargin:
2399         {
2400             bool isWindow = false;
2401             if (opt) {
2402                 isWindow = (opt->state & State_Window);
2403             } else if (widget) {
2404                 isWindow = widget->isWindow();
2405             }
2406
2407             if (isWindow) {
2408                 bool isMetal = widget && widget->testAttribute(Qt::WA_MacBrushedMetal);
2409                 if (isMetal) {
2410                     if (metric == PM_LayoutTopMargin) {
2411                         return_SIZE(9 /* AHIG */, 6 /* guess */, 6 /* guess */);
2412                     } else if (metric == PM_LayoutBottomMargin) {
2413                         return_SIZE(18 /* AHIG */, 15 /* guess */, 13 /* guess */);
2414                     } else {
2415                         return_SIZE(14 /* AHIG */, 11 /* guess */, 9 /* guess */);
2416                     }
2417                 } else {
2418                     /*
2419                         AHIG would have (20, 8, 10) here but that makes
2420                         no sense. It would also have 14 for the top margin
2421                         but this contradicts both Builder and most
2422                         applications.
2423                     */
2424                     return_SIZE(20, 10, 10);    // AHIG
2425                 }
2426             } else {
2427                 // hack to detect QTabWidget
2428                 if (widget && widget->parentWidget()
2429                         && widget->parentWidget()->sizePolicy().controlType() == QSizePolicy::TabWidget) {
2430                     if (metric == PM_LayoutTopMargin) {
2431                         /*
2432                             Builder would have 14 (= 20 - 6) instead of 12,
2433                             but that makes the tab look disproportionate.
2434                         */
2435                         return_SIZE(12, 6, 6);  // guess
2436                     } else {
2437                         return_SIZE(20 /* Builder */, 8 /* guess */, 8 /* guess */);
2438                     }
2439                 } else {
2440                     /*
2441                         Child margins are highly inconsistent in AHIG and Builder.
2442                     */
2443                     return_SIZE(12, 8, 6);    // guess
2444                 }
2445             }
2446         }
2447     case PM_LayoutHorizontalSpacing:
2448     case PM_LayoutVerticalSpacing:
2449         return -1;
2450     case QStyle::PM_TabBarTabHSpace:
2451         switch (d->aquaSizeConstrain(opt, widget)) {
2452         case QAquaSizeLarge:
2453         case QAquaSizeUnknown:
2454             ret = QWindowsStyle::pixelMetric(metric, opt, widget);
2455             break;
2456         case QAquaSizeSmall:
2457             ret = 20;
2458             break;
2459         case QAquaSizeMini:
2460             ret = 16;
2461             break;
2462         }
2463         break;
2464     case PM_MenuHMargin:
2465         ret = 0;
2466         break;
2467     case PM_ToolBarFrameWidth:
2468         ret = 1;
2469         if (widget) {
2470             if (QMainWindow * mainWindow = qobject_cast<QMainWindow *>(widget->parent()))
2471                 if (mainWindow->unifiedTitleAndToolBarOnMac())
2472                     ret = 0;
2473         }
2474         break;
2475     default:
2476         ret = QWindowsStyle::pixelMetric(metric, opt, widget);
2477         break;
2478     }
2479     return ret;
2480 }
2481
2482 QPalette QMacStyle::standardPalette() const
2483 {
2484     QPalette pal = QWindowsStyle::standardPalette();
2485     pal.setColor(QPalette::Disabled, QPalette::Dark, QColor(191, 191, 191));
2486     pal.setColor(QPalette::Active, QPalette::Dark, QColor(191, 191, 191));
2487     pal.setColor(QPalette::Inactive, QPalette::Dark, QColor(191, 191, 191));
2488     return pal;
2489 }
2490
2491 int QMacStyle::styleHint(StyleHint sh, const QStyleOption *opt, const QWidget *w,
2492                          QStyleHintReturn *hret) const
2493 {
2494     SInt32 ret = 0;
2495     switch (sh) {
2496     case SH_Menu_SelectionWrap:
2497         ret = false;
2498         break;
2499     case SH_Menu_KeyboardSearch:
2500         ret = true;
2501         break;
2502     case SH_Menu_SpaceActivatesItem:
2503         ret = true;
2504         break;
2505     case SH_Slider_AbsoluteSetButtons:
2506         ret = Qt::LeftButton|Qt::MidButton;
2507         break;
2508     case SH_Slider_PageSetButtons:
2509         ret = 0;
2510         break;
2511     case SH_ScrollBar_ContextMenu:
2512         ret = false;
2513         break;
2514     case SH_TitleBar_AutoRaise:
2515         ret = true;
2516         break;
2517     case SH_Menu_AllowActiveAndDisabled:
2518         ret = false;
2519         break;
2520     case SH_Menu_SubMenuPopupDelay:
2521         ret = 100;
2522         break;
2523     case SH_ScrollBar_LeftClickAbsolutePosition: {
2524         if(QApplication::keyboardModifiers() & Qt::AltModifier)
2525             ret = false;
2526             //ret = !qt_scrollbar_jump_to_pos;
2527         else
2528             ret = true;
2529             //ret = qt_scrollbar_jump_to_pos;
2530         break; }
2531     case SH_TabBar_PreferNoArrows:
2532         ret = true;
2533         break;
2534     case SH_LineEdit_PasswordCharacter:
2535         ret = kBulletUnicode;
2536         break;
2537         /*
2538     case SH_DialogButtons_DefaultButton:
2539         ret = QDialogButtons::Reject;
2540         break;
2541         */
2542     case SH_Menu_SloppySubMenus:
2543         ret = true;
2544         break;
2545     case SH_GroupBox_TextLabelVerticalAlignment:
2546         ret = Qt::AlignTop;
2547         break;
2548     case SH_ScrollView_FrameOnlyAroundContents:
2549         ret = QWindowsStyle::styleHint(sh, opt, w, hret);
2550         break;
2551     case SH_Menu_FillScreenWithScroll:
2552         ret = false;
2553         break;
2554     case SH_Menu_Scrollable:
2555         ret = true;
2556         break;
2557     case SH_RichText_FullWidthSelection:
2558         ret = true;
2559         break;
2560     case SH_BlinkCursorWhenTextSelected:
2561         ret = false;
2562         break;
2563     case SH_ScrollBar_StopMouseOverSlider:
2564         ret = true;
2565         break;
2566     case SH_ListViewExpand_SelectMouseType:
2567         ret = QEvent::MouseButtonRelease;
2568         break;
2569     case SH_TabBar_SelectMouseType:
2570         if (const QStyleOptionTabBarBaseV2 *opt2 = qstyleoption_cast<const QStyleOptionTabBarBaseV2 *>(opt)) {
2571             ret = opt2->documentMode ? QEvent::MouseButtonPress : QEvent::MouseButtonRelease;
2572         } else {
2573             ret = QEvent::MouseButtonRelease;
2574         }
2575         break;
2576     case SH_ComboBox_Popup:
2577         if (const QStyleOptionComboBox *cmb = qstyleoption_cast<const QStyleOptionComboBox *>(opt))
2578             ret = !cmb->editable;
2579         else
2580             ret = 0;
2581         break;
2582     case SH_Workspace_FillSpaceOnMaximize:
2583         ret = true;
2584         break;
2585     case SH_Widget_ShareActivation:
2586         ret = true;
2587         break;
2588     case SH_Header_ArrowAlignment:
2589         ret = Qt::AlignRight;
2590         break;
2591     case SH_TabBar_Alignment: {
2592         if (const QTabWidget *tab = qobject_cast<const QTabWidget*>(w)) {
2593             if (tab->documentMode()) {
2594                 ret = Qt::AlignLeft;
2595                 break;
2596             }
2597         }
2598         if (const QTabBar *tab = qobject_cast<const QTabBar*>(w)) {
2599             if (tab->documentMode()) {
2600                 ret = Qt::AlignLeft;
2601                 break;
2602             }
2603         }
2604         ret = Qt::AlignCenter;
2605         } break;
2606     case SH_UnderlineShortcut:
2607         ret = false;
2608         break;
2609     case SH_ToolTipLabel_Opacity:
2610         ret = 242; // About 95%
2611         break;
2612     case SH_Button_FocusPolicy:
2613         ret = Qt::TabFocus;
2614         break;
2615     case SH_EtchDisabledText:
2616         ret = false;
2617         break;
2618     case SH_FocusFrame_Mask: {
2619         ret = true;
2620         if(QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) {
2621             const uchar fillR = 192, fillG = 191, fillB = 190;
2622             QImage img;
2623
2624             QSize pixmapSize = opt->rect.size();
2625             if (pixmapSize.isValid()) {
2626                 QPixmap pix(pixmapSize);
2627                 pix.fill(QColor(fillR, fillG, fillB));
2628                 QPainter pix_paint(&pix);
2629                 proxy()->drawControl(CE_FocusFrame, opt, &pix_paint, w);
2630                 pix_paint.end();
2631                 img = pix.toImage();
2632             }
2633
2634             const QRgb *sptr = (QRgb*)img.bits(), *srow;
2635             const int sbpl = img.bytesPerLine();
2636             const int w = sbpl/4, h = img.height();
2637
2638             QImage img_mask(img.width(), img.height(), QImage::Format_ARGB32);
2639             QRgb *dptr = (QRgb*)img_mask.bits(), *drow;
2640             const int dbpl = img_mask.bytesPerLine();
2641
2642             for (int y = 0; y < h; ++y) {
2643                 srow = sptr+((y*sbpl)/4);
2644                 drow = dptr+((y*dbpl)/4);
2645                 for (int x = 0; x < w; ++x) {
2646                     const int diff = (((qRed(*srow)-fillR)*(qRed(*srow)-fillR)) +
2647                                       ((qGreen(*srow)-fillG)*((qGreen(*srow)-fillG))) +
2648                                       ((qBlue(*srow)-fillB)*((qBlue(*srow)-fillB))));
2649                     (*drow++) = (diff < 100) ? 0xffffffff : 0xff000000;
2650                     ++srow;
2651                 }
2652             }
2653             QBitmap qmask = QBitmap::fromImage(img_mask);
2654             mask->region = QRegion(qmask);
2655         }
2656         break; }
2657     case SH_TitleBar_NoBorder:
2658         ret = 1;
2659         break;
2660     case SH_RubberBand_Mask:
2661         ret = 0;
2662         break;
2663     case SH_ComboBox_LayoutDirection:
2664         ret = Qt::LeftToRight;
2665         break;
2666     case SH_ItemView_EllipsisLocation:
2667         ret = Qt::AlignHCenter;
2668         break;
2669     case SH_ItemView_ShowDecorationSelected:
2670         ret = true;
2671         break;
2672     case SH_TitleBar_ModifyNotification:
2673         ret = false;
2674         break;
2675     case SH_ScrollBar_RollBetweenButtons:
2676         ret = true;
2677         break;
2678     case SH_WindowFrame_Mask:
2679         ret = 1;
2680         if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask *>(hret)) {
2681             mask->region = opt->rect;
2682             mask->region -= QRect(opt->rect.left(), opt->rect.top(), 5, 1);
2683             mask->region -= QRect(opt->rect.left(), opt->rect.top() + 1, 3, 1);
2684             mask->region -= QRect(opt->rect.left(), opt->rect.top() + 2, 2, 1);
2685             mask->region -= QRect(opt->rect.left(), opt->rect.top() + 3, 1, 2);
2686
2687             mask->region -= QRect(opt->rect.right() - 4, opt->rect.top(), 5, 1);
2688             mask->region -= QRect(opt->rect.right() - 2, opt->rect.top() + 1, 3, 1);
2689             mask->region -= QRect(opt->rect.right() - 1, opt->rect.top() + 2, 2, 1);
2690             mask->region -= QRect(opt->rect.right() , opt->rect.top() + 3, 1, 2);
2691         }
2692         break;
2693     case SH_TabBar_ElideMode:
2694         ret = Qt::ElideRight;
2695         break;
2696     case SH_DialogButtonLayout:
2697         ret = QDialogButtonBox::MacLayout;
2698         break;
2699     case SH_FormLayoutWrapPolicy:
2700         ret = QFormLayout::DontWrapRows;
2701         break;
2702     case SH_FormLayoutFieldGrowthPolicy:
2703         ret = QFormLayout::FieldsStayAtSizeHint;
2704         break;
2705     case SH_FormLayoutFormAlignment:
2706         ret = Qt::AlignHCenter | Qt::AlignTop;
2707         break;
2708     case SH_FormLayoutLabelAlignment:
2709         ret = Qt::AlignRight;
2710         break;
2711     case SH_ComboBox_PopupFrameStyle:
2712         ret = QFrame::NoFrame | QFrame::Plain;
2713         break;
2714     case SH_MessageBox_TextInteractionFlags:
2715         ret = Qt::TextSelectableByMouse | Qt::LinksAccessibleByMouse | Qt::TextSelectableByKeyboard;
2716         break;
2717     case SH_SpellCheckUnderlineStyle:
2718         ret = QTextCharFormat::DashUnderline;
2719         break;
2720     case SH_MessageBox_CenterButtons:
2721         ret = false;
2722         break;
2723     case SH_MenuBar_AltKeyNavigation:
2724         ret = false;
2725         break;
2726     case SH_ItemView_MovementWithoutUpdatingSelection:
2727         ret = false;
2728         break;
2729     case SH_FocusFrame_AboveWidget:
2730         ret = true;
2731         break;
2732     case SH_WizardStyle:
2733         ret = QWizard::MacStyle;
2734         break;
2735     case SH_ItemView_ArrowKeysNavigateIntoChildren:
2736         ret = false;
2737         break;
2738     case SH_Menu_FlashTriggeredItem:
2739         ret = true;
2740         break;
2741     case SH_Menu_FadeOutOnHide:
2742         ret = true;
2743         break;
2744     case SH_Menu_Mask:
2745         if (opt) {
2746             if (QStyleHintReturnMask *mask = qstyleoption_cast<QStyleHintReturnMask*>(hret)) {
2747                 ret = true;
2748                 HIRect menuRect = CGRectMake(opt->rect.x(), opt->rect.y() + 4,
2749                                              opt->rect.width(), opt->rect.height() - 8);
2750                 HIThemeMenuDrawInfo mdi;
2751                 mdi.version = 0;
2752                 if (w && qobject_cast<QMenu *>(w->parentWidget()))
2753                     mdi.menuType = kThemeMenuTypeHierarchical;
2754                 else
2755                     mdi.menuType = kThemeMenuTypePopUp;
2756                 QCFType<HIShapeRef> shape;
2757                 HIThemeGetMenuBackgroundShape(&menuRect, &mdi, &shape);
2758
2759                 mask->region = qt_mac_fromHIShapeRef(shape);
2760             }
2761         }
2762         break;
2763     case SH_ItemView_PaintAlternatingRowColorsForEmptyArea:
2764         ret = true;
2765         break;
2766     case SH_TabBar_CloseButtonPosition:
2767         ret = QTabBar::LeftSide;
2768         break;
2769     case SH_DockWidget_ButtonsHaveFrame:
2770         ret = false;
2771         break;
2772     default:
2773         ret = QWindowsStyle::styleHint(sh, opt, w, hret);
2774         break;
2775     }
2776     return ret;
2777 }
2778
2779 QPixmap QMacStyle::generatedIconPixmap(QIcon::Mode iconMode, const QPixmap &pixmap,
2780                                        const QStyleOption *opt) const
2781 {
2782     switch (iconMode) {
2783     case QIcon::Disabled: {
2784         QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32);
2785         int imgh = img.height();
2786         int imgw = img.width();
2787         QRgb pixel;
2788         for (int y = 0; y < imgh; ++y) {
2789             for (int x = 0; x < imgw; ++x) {
2790                 pixel = img.pixel(x, y);
2791                 img.setPixel(x, y, qRgba(qRed(pixel), qGreen(pixel), qBlue(pixel),
2792                                          qAlpha(pixel) / 2));
2793             }