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

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

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

File size: 248.1 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42/*
43 Note: The qdoc comments for QMacStyle are contained in
44 .../doc/src/qstyles.qdoc.
45*/
46
47#include "qmacstyle_mac.h"
48
49#if defined(Q_WS_MAC) && !defined(QT_NO_STYLE_MAC)
50#define QMAC_QAQUASTYLE_SIZE_CONSTRAIN
51//#define DEBUG_SIZE_CONSTRAINT
52
53#include <private/qapplication_p.h>
54#include <private/qcombobox_p.h>
55#include <private/qmacstylepixmaps_mac_p.h>
56#include <private/qpaintengine_mac_p.h>
57#include <private/qpainter_p.h>
58#include <private/qprintengine_mac_p.h>
59#include <qapplication.h>
60#include <qbitmap.h>
61#include <qcheckbox.h>
62#include <qcombobox.h>
63#include <qdialogbuttonbox.h>
64#include <qdockwidget.h>
65#include <qevent.h>
66#include <qfocusframe.h>
67#include <qformlayout.h>
68#include <qgroupbox.h>
69#include <qhash.h>
70#include <qheaderview.h>
71#include <qlayout.h>
72#include <qlineedit.h>
73#include <qlistview.h>
74#include <qmainwindow.h>
75#include <qmap.h>
76#include <qmenubar.h>
77#include <qpaintdevice.h>
78#include <qpainter.h>
79#include <qpixmapcache.h>
80#include <qpointer.h>
81#include <qprogressbar.h>
82#include <qpushbutton.h>
83#include <qradiobutton.h>
84#include <qrubberband.h>
85#include <qsizegrip.h>
86#include <qspinbox.h>
87#include <qsplitter.h>
88#include <qstyleoption.h>
89#include <qtextedit.h>
90#include <qtextstream.h>
91#include <qtoolbar.h>
92#include <qtoolbutton.h>
93#include <qtreeview.h>
94#include <qtableview.h>
95#include <qwizard.h>
96#include <qdebug.h>
97#include <qlibrary.h>
98#include <qdatetimeedit.h>
99#include <qmath.h>
100#include <QtGui/qgraphicsproxywidget.h>
101#include <QtGui/qgraphicsview.h>
102#include <private/qt_cocoa_helpers_mac_p.h>
103#include "qmacstyle_mac_p.h"
104#include <private/qstylehelper_p.h>
105
106QT_BEGIN_NAMESPACE
107
108// The following constants are used for adjusting the size
109// of push buttons so that they are drawn inside their bounds.
110const int QMacStylePrivate::PushButtonLeftOffset = 6;
111const int QMacStylePrivate::PushButtonTopOffset = 4;
112const int QMacStylePrivate::PushButtonRightOffset = 12;
113const int QMacStylePrivate::PushButtonBottomOffset = 12;
114const int QMacStylePrivate::MiniButtonH = 26;
115const int QMacStylePrivate::SmallButtonH = 30;
116const int QMacStylePrivate::BevelButtonW = 50;
117const int QMacStylePrivate::BevelButtonH = 22;
118const int QMacStylePrivate::PushButtonContentPadding = 6;
119
120// These colors specify the titlebar gradient colors on
121// Leopard. Ideally we should get them from the system.
122static const QColor titlebarGradientActiveBegin(220, 220, 220);
123static const QColor titlebarGradientActiveEnd(151, 151, 151);
124static const QColor titlebarSeparatorLineActive(111, 111, 111);
125static const QColor titlebarGradientInactiveBegin(241, 241, 241);
126static const QColor titlebarGradientInactiveEnd(207, 207, 207);
127static const QColor titlebarSeparatorLineInactive(131, 131, 131);
128
129// Gradient colors used for the dock widget title bar and
130// non-unifed tool bar bacground.
131static const QColor mainWindowGradientBegin(240, 240, 240);
132static const QColor mainWindowGradientEnd(200, 200, 200);
133
134static const int DisclosureOffset = 4;
135
136// Resolve these at run-time, since the functions was moved in Leopard.
137typedef HIRect * (*PtrHIShapeGetBounds)(HIShapeRef, HIRect *);
138static PtrHIShapeGetBounds ptrHIShapeGetBounds = 0;
139
140static int closeButtonSize = 12;
141
142extern QRegion qt_mac_convert_mac_region(RgnHandle); //qregion_mac.cpp
143
144static bool isVerticalTabs(const QTabBar::Shape shape) {
145 return (shape == QTabBar::RoundedEast
146 || shape == QTabBar::TriangularEast
147 || shape == QTabBar::RoundedWest
148 || shape == QTabBar::TriangularWest);
149}
150
151void drawTabCloseButton(QPainter *p, bool hover, bool active, bool selected)
152{
153 // draw background circle
154 p->setRenderHints(QPainter::Antialiasing);
155 QRect rect(0, 0, closeButtonSize, closeButtonSize);
156 QColor background;
157 if (hover) {
158 background = QColor(124, 124, 124);
159 } else {
160 if (active) {
161 if (selected)
162 background = QColor(104, 104, 104);
163 else
164 background = QColor(83, 83, 83);
165 } else {
166 if (selected)
167 background = QColor(144, 144, 144);
168 else
169 background = QColor(114, 114, 114);
170 }
171 }
172 p->setPen(Qt::transparent);
173 p->setBrush(background);
174 p->drawEllipse(rect);
175
176 // draw cross
177 int min = 3;
178 int max = 9;
179 QPen crossPen;
180 crossPen.setColor(QColor(194, 194, 194));
181 crossPen.setWidthF(1.3);
182 crossPen.setCapStyle(Qt::FlatCap);
183 p->setPen(crossPen);
184 p->drawLine(min, min, max, max);
185 p->drawLine(min, max, max, min);
186}
187
188QRect rotateTabPainter(QPainter *p, QTabBar::Shape shape, QRect tabRect)
189{
190 if (isVerticalTabs(shape)) {
191 int newX, newY, newRot;
192 if (shape == QTabBar::RoundedEast
193 || shape == QTabBar::TriangularEast) {
194 newX = tabRect.width();
195 newY = tabRect.y();
196 newRot = 90;
197 } else {
198 newX = 0;
199 newY = tabRect.y() + tabRect.height();
200 newRot = -90;
201 }
202 tabRect.setRect(0, 0, tabRect.height(), tabRect.width());
203 QMatrix m;
204 m.translate(newX, newY);
205 m.rotate(newRot);
206 p->setMatrix(m, true);
207 }
208 return tabRect;
209}
210
211void drawTabShape(QPainter *p, const QStyleOptionTabV3 *tabOpt)
212{
213 QRect r = tabOpt->rect;
214 p->translate(tabOpt->rect.x(), tabOpt->rect.y());
215 r.moveLeft(0);
216 r.moveTop(0);
217 QRect tabRect = rotateTabPainter(p, tabOpt->shape, r);
218
219 int width = tabRect.width();
220 int height = 20;
221 bool active = (tabOpt->state & QStyle::State_Active);
222 bool selected = (tabOpt->state & QStyle::State_Selected);
223
224 if (selected) {
225 QRect rect(1, 0, width - 2, height);
226
227 // fill body
228 if (active) {
229 int d = (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) ? 16 : 0;
230 p->fillRect(rect, QColor(151 + d, 151 + d, 151 + d));
231 } else {
232 int d = (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) ? 9 : 0;
233 QLinearGradient gradient(rect.topLeft(), rect.bottomLeft());
234 gradient.setColorAt(0, QColor(207 + d, 207 + d, 207 + d));
235 gradient.setColorAt(0.5, QColor(206 + d, 206 + d, 206 + d));
236 gradient.setColorAt(1, QColor(201 + d, 201 + d, 201 + d));
237 p->fillRect(rect, gradient);
238 }
239
240 // draw border
241 QColor borderSides;
242 QColor borderBottom;
243 if (active) {
244 borderSides = QColor(88, 88, 88);
245 borderBottom = QColor(88, 88, 88);
246 } else {
247 borderSides = QColor(121, 121, 121);
248 borderBottom = QColor(116, 116, 116);
249 }
250
251 p->setPen(borderSides);
252
253 int bottom = height;
254 // left line
255 p->drawLine(0, 1, 0, bottom-2);
256 // right line
257 p->drawLine(width-1, 1, width-1, bottom-2);
258
259 // bottom line
260 if (active) {
261 p->setPen(QColor(168, 168, 168));
262 p->drawLine(3, bottom-1, width-3, bottom-1);
263 }
264 p->setPen(borderBottom);
265 p->drawLine(2, bottom, width-2, bottom);
266
267 int w = 3;
268 QRectF rectangleLeft(1, height - w, w, w);
269 QRectF rectangleRight(width - 2, height - 1, w, w);
270 int startAngle = 180 * 16;
271 int spanAngle = 90 * 16;
272 p->setRenderHint(QPainter::Antialiasing);
273 p->drawArc(rectangleLeft, startAngle, spanAngle);
274 p->drawArc(rectangleRight, startAngle, -spanAngle);
275 } else {
276 // when the mouse is over non selected tabs they get a new color
277 bool hover = (tabOpt->state & QStyle::State_MouseOver);
278 if (hover) {
279 QRect rect(1, 2, width - 1, height - 1);
280 p->fillRect(rect, QColor(110, 110, 110));
281 }
282
283 // seperator lines between tabs
284 bool west = (tabOpt->shape == QTabBar::RoundedWest || tabOpt->shape == QTabBar::TriangularWest);
285 bool drawOnRight = !west;
286 if ((!drawOnRight && tabOpt->selectedPosition != QStyleOptionTab::NextIsSelected)
287 || (drawOnRight && tabOpt->selectedPosition != QStyleOptionTab::NextIsSelected)) {
288 QColor borderColor;
289 QColor borderHighlightColor;
290 if (active) {
291 borderColor = QColor(64, 64, 64);
292 borderHighlightColor = QColor(140, 140, 140);
293 } else {
294 borderColor = QColor(135, 135, 135);
295 borderHighlightColor = QColor(178, 178, 178);
296 }
297
298 int x = drawOnRight ? width : 0;
299
300 // tab seperator line
301 p->setPen(borderColor);
302 p->drawLine(x, 2, x, height + 1);
303
304 // tab seperator highlight
305 p->setPen(borderHighlightColor);
306 p->drawLine(x-1, 2, x-1, height + 1);
307 p->drawLine(x+1, 2, x+1, height + 1);
308 }
309 }
310}
311
312void drawTabBase(QPainter *p, const QStyleOptionTabBarBaseV2 *tbb, const QWidget *w)
313{
314 QRect r = tbb->rect;
315 if (isVerticalTabs(tbb->shape)) {
316 r.setWidth(w->width());
317 } else {
318 r.setHeight(w->height());
319 }
320 QRect tabRect = rotateTabPainter(p, tbb->shape, r);
321 int width = tabRect.width();
322 int height = tabRect.height();
323 bool active = (tbb->state & QStyle::State_Active);
324
325 // top border lines
326 QColor borderHighlightTop;
327 QColor borderTop;
328 if (active) {
329 borderTop = QColor(64, 64, 64);
330 borderHighlightTop = QColor(174, 174, 174);
331 } else {
332 borderTop = QColor(135, 135, 135);
333 borderHighlightTop = QColor(207, 207, 207);
334 }
335 p->setPen(borderHighlightTop);
336 p->drawLine(tabRect.x(), 0, width, 0);
337 p->setPen(borderTop);
338 p->drawLine(tabRect.x(), 1, width, 1);
339
340 // center block
341 QRect centralRect(tabRect.x(), 2, width, height - 2);
342 if (active) {
343 QColor mainColor = QColor(120, 120, 120);
344 p->fillRect(centralRect, mainColor);
345 } else {
346 QLinearGradient gradient(centralRect.topLeft(), centralRect.bottomLeft());
347 gradient.setColorAt(0, QColor(165, 165, 165));
348 gradient.setColorAt(0.5, QColor(164, 164, 164));
349 gradient.setColorAt(1, QColor(158, 158, 158));
350 p->fillRect(centralRect, gradient);
351 }
352
353 // bottom border lines
354 QColor borderHighlightBottom;
355 QColor borderBottom;
356 if (active) {
357 borderHighlightBottom = QColor(153, 153, 153);
358 borderBottom = QColor(64, 64, 64);
359 } else {
360 borderHighlightBottom = QColor(177, 177, 177);
361 borderBottom = QColor(127, 127, 127);
362 }
363 p->setPen(borderHighlightBottom);
364 p->drawLine(tabRect.x(), height - 2, width, height - 2);
365 p->setPen(borderBottom);
366 p->drawLine(tabRect.x(), height - 1, width, height - 1);
367}
368
369static int getControlSize(const QStyleOption *option, const QWidget *widget)
370{
371 if (option) {
372 if (option->state & (QStyle::State_Small | QStyle::State_Mini))
373 return (option->state & QStyle::State_Mini) ? QAquaSizeMini : QAquaSizeSmall;
374 } else if (widget) {
375 switch (QMacStyle::widgetSizePolicy(widget)) {
376 case QMacStyle::SizeSmall:
377 return QAquaSizeSmall;
378 case QMacStyle::SizeMini:
379 return QAquaSizeMini;
380 default:
381 break;
382 }
383 }
384 return QAquaSizeLarge;
385}
386
387
388static inline bool isTreeView(const QWidget *widget)
389{
390 return (widget && widget->parentWidget() &&
391 (qobject_cast<const QTreeView *>(widget->parentWidget())
392#ifdef QT3_SUPPORT
393 || widget->parentWidget()->inherits("Q3ListView")
394#endif
395 ));
396}
397
398QString qt_mac_removeMnemonics(const QString &original)
399{
400 QString returnText(original.size(), 0);
401 int finalDest = 0;
402 int currPos = 0;
403 int l = original.length();
404 while (l) {
405 if (original.at(currPos) == QLatin1Char('&')
406 && (l == 1 || original.at(currPos + 1) != QLatin1Char('&'))) {
407 ++currPos;
408 --l;
409 if (l == 0)
410 break;
411 }
412 returnText[finalDest] = original.at(currPos);
413 ++currPos;
414 ++finalDest;
415 --l;
416 }
417 returnText.truncate(finalDest);
418 return returnText;
419}
420
421static inline ThemeTabDirection getTabDirection(QTabBar::Shape shape)
422{
423 ThemeTabDirection ttd;
424 switch (shape) {
425 case QTabBar::RoundedSouth:
426 case QTabBar::TriangularSouth:
427 ttd = kThemeTabSouth;
428 break;
429 default: // Added to remove the warning, since all values are taken care of, really!
430 case QTabBar::RoundedNorth:
431 case QTabBar::TriangularNorth:
432 ttd = kThemeTabNorth;
433 break;
434 case QTabBar::RoundedWest:
435 case QTabBar::TriangularWest:
436 ttd = kThemeTabWest;
437 break;
438 case QTabBar::RoundedEast:
439 case QTabBar::TriangularEast:
440 ttd = kThemeTabEast;
441 break;
442 }
443 return ttd;
444}
445
446QT_BEGIN_INCLUDE_NAMESPACE
447#include "moc_qmacstyle_mac.cpp"
448#include "moc_qmacstyle_mac_p.cpp"
449QT_END_INCLUDE_NAMESPACE
450
451/*****************************************************************************
452 External functions
453 *****************************************************************************/
454extern CGContextRef qt_mac_cg_context(const QPaintDevice *); //qpaintdevice_mac.cpp
455extern QRegion qt_mac_convert_mac_region(HIShapeRef); //qregion_mac.cpp
456void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
457extern QPaintDevice *qt_mac_safe_pdev; //qapplication_mac.cpp
458
459/*****************************************************************************
460 QMacCGStyle globals
461 *****************************************************************************/
462const int qt_mac_hitheme_version = 0; //the HITheme version we speak
463const int macItemFrame = 2; // menu item frame width
464const int macItemHMargin = 3; // menu item hor text margin
465const int macItemVMargin = 2; // menu item ver text margin
466const int macRightBorder = 12; // right border on mac
467const ThemeWindowType QtWinType = kThemeDocumentWindow; // Window type we use for QTitleBar.
468QPixmap *qt_mac_backgroundPattern = 0; // stores the standard widget background.
469
470/*****************************************************************************
471 QMacCGStyle utility functions
472 *****************************************************************************/
473static inline int qt_mac_hitheme_tab_version()
474{
475 return 1;
476}
477
478static inline HIRect qt_hirectForQRect(const QRect &convertRect, const QRect &rect = QRect())
479{
480 return CGRectMake(convertRect.x() + rect.x(), convertRect.y() + rect.y(),
481 convertRect.width() - rect.width(), convertRect.height() - rect.height());
482}
483
484static inline const QRect qt_qrectForHIRect(const HIRect &hirect)
485{
486 return QRect(QPoint(int(hirect.origin.x), int(hirect.origin.y)),
487 QSize(int(hirect.size.width), int(hirect.size.height)));
488}
489
490inline bool qt_mac_is_metal(const QWidget *w)
491{
492 for (; w; w = w->parentWidget()) {
493 if (w->testAttribute(Qt::WA_MacBrushedMetal))
494 return true;
495 if (w->isWindow() && w->testAttribute(Qt::WA_WState_Created)) { // If not created will fall through to the opaque check and be fine anyway.
496 return macWindowIsTextured(qt_mac_window_for(w));
497 }
498 if (w->d_func()->isOpaque)
499 break;
500 }
501 return false;
502}
503
504static int qt_mac_aqua_get_metric(ThemeMetric met)
505{
506 SInt32 ret;
507 GetThemeMetric(met, &ret);
508 return ret;
509}
510
511static QSize qt_aqua_get_known_size(QStyle::ContentsType ct, const QWidget *widg, QSize szHint,
512 QAquaWidgetSize sz)
513{
514 QSize ret(-1, -1);
515 if (sz != QAquaSizeSmall && sz != QAquaSizeLarge && sz != QAquaSizeMini) {
516 qDebug("Not sure how to return this...");
517 return ret;
518 }
519 if ((widg && widg->testAttribute(Qt::WA_SetFont)) || !QApplication::desktopSettingsAware()) {
520 // If you're using a custom font and it's bigger than the default font,
521 // then no constraints for you. If you are smaller, we can try to help you out
522 QFont font = qt_app_fonts_hash()->value(widg->metaObject()->className(), QFont());
523 if (widg->font().pointSize() > font.pointSize())
524 return ret;
525 }
526
527 if (ct == QStyle::CT_CustomBase && widg) {
528 if (qobject_cast<const QPushButton *>(widg))
529 ct = QStyle::CT_PushButton;
530 else if (qobject_cast<const QRadioButton *>(widg))
531 ct = QStyle::CT_RadioButton;
532 else if (qobject_cast<const QCheckBox *>(widg))
533 ct = QStyle::CT_CheckBox;
534 else if (qobject_cast<const QComboBox *>(widg))
535 ct = QStyle::CT_ComboBox;
536 else if (qobject_cast<const QToolButton *>(widg))
537 ct = QStyle::CT_ToolButton;
538 else if (qobject_cast<const QSlider *>(widg))
539 ct = QStyle::CT_Slider;
540 else if (qobject_cast<const QProgressBar *>(widg))
541 ct = QStyle::CT_ProgressBar;
542 else if (qobject_cast<const QLineEdit *>(widg))
543 ct = QStyle::CT_LineEdit;
544 else if (qobject_cast<const QHeaderView *>(widg)
545#ifdef QT3_SUPPORT
546 || widg->inherits("Q3Header")
547#endif