Merge remote-tracking branch 'origin/stable' into dev
[qt:qtmacextras.git] / src / macextras / qmacnativetoolbar.mm
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the QtMacExtras module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #import <AppKit/AppKit.h>
42 #include "qmacnativetoolbar_p.h"
43
44 #include <QtGui/QGuiApplication>
45 #include <QtCore/QDebug>
46 #include <QtCore/QTimer>
47 #include <QtCore/QUuid>
48
49 #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0)
50 #include <QtGui/QGuiApplication>
51 #include <qpa/qplatformnativeinterface.h>
52 #else
53 #include <QtGui/QMainWindow>
54 #endif
55
56 #include "qmacfunctions.h"
57 #include "qmacfunctions_p.h"
58 #include "qmactoolbardelegate_p.h"
59 #include "qnstoolbar_p.h"
60
61 // from the Apple NSToolbar documentation
62 #define kNSToolbarIconSizeSmall 24
63 #define kNSToolbarIconSizeRegular 32
64 #define kNSToolbarIconSizeDefault kNSToolbarIconSizeRegular
65
66 NSString *toNSStandardItem(QMacToolButton::StandardItem standardItem)
67 {
68     if (standardItem == QMacToolButton::ShowColors)
69         return NSToolbarShowColorsItemIdentifier;
70     else if (standardItem == QMacToolButton::ShowFonts)
71         return NSToolbarShowFontsItemIdentifier;
72     else if (standardItem == QMacToolButton::PrintItem)
73         return NSToolbarPrintItemIdentifier;
74     else if (standardItem == QMacToolButton::Space)
75         return NSToolbarSpaceItemIdentifier;
76     else if (standardItem == QMacToolButton::FlexibleSpace)
77         return NSToolbarFlexibleSpaceItemIdentifier;
78     return @"";
79 }
80
81 QT_BEGIN_NAMESPACE
82
83 NSToolbarDisplayMode toNSToolbarDisplayMode(Qt::ToolButtonStyle toolButtonStyle)
84 {
85     switch (toolButtonStyle)
86     {
87     case Qt::ToolButtonIconOnly:
88         return NSToolbarDisplayModeIconOnly;
89     case Qt::ToolButtonTextOnly:
90         return NSToolbarDisplayModeLabelOnly;
91     case Qt::ToolButtonTextBesideIcon:
92     case Qt::ToolButtonTextUnderIcon:
93         return NSToolbarDisplayModeIconAndLabel;
94     case Qt::ToolButtonFollowStyle:
95     default:
96         return NSToolbarDisplayModeDefault;
97     }
98 }
99
100 Qt::ToolButtonStyle toQtToolButtonStyle(NSToolbarDisplayMode toolbarDisplayMode)
101 {
102     switch (toolbarDisplayMode)
103     {
104     case NSToolbarDisplayModeIconAndLabel:
105         return Qt::ToolButtonTextUnderIcon;
106     case NSToolbarDisplayModeIconOnly:
107         return Qt::ToolButtonIconOnly;
108     case NSToolbarDisplayModeLabelOnly:
109         return Qt::ToolButtonTextOnly;
110     case NSToolbarDisplayModeDefault:
111     default:
112         return Qt::ToolButtonFollowStyle;
113     }
114 }
115
116 QT_END_NAMESPACE
117
118 #include <qglobal.h>
119
120 @interface QT_MANGLE_NAMESPACE(QNSToolbarNotifier) : NSObject
121 {
122 @public
123     QMacNativeToolBarPrivate *pimpl;
124 }
125 - (void)notification:(NSNotification*)note;
126 @end
127
128 QT_NAMESPACE_ALIAS_OBJC_CLASS(QNSToolbarNotifier);
129
130 QT_BEGIN_NAMESPACE
131
132 class QMacNativeToolBarPrivate
133 {
134 public:
135     QMacNativeToolBar *qtToolbar;
136     QtNSToolbar *toolbar;
137     QMacToolbarDelegate *delegate;
138     QNSToolbarNotifier *notifier;
139
140     QMacNativeToolBarPrivate(QMacNativeToolBar *parent, const QString &identifier = QString())
141     {
142         qtToolbar = parent;
143         toolbar = [[QtNSToolbar alloc] initWithIdentifier:QtMac::toNSString(identifier.isEmpty() ? QUuid::createUuid().toString() : identifier)];
144         [toolbar setAutosavesConfiguration:NO];
145
146         delegate = [[QMacToolbarDelegate alloc] init];
147         [toolbar setDelegate:delegate];
148
149         notifier = [[QNSToolbarNotifier alloc] init];
150         notifier->pimpl = this;
151         [[NSNotificationCenter defaultCenter] addObserver:notifier selector:@selector(notification:) name:nil object:toolbar];
152     }
153
154     ~QMacNativeToolBarPrivate()
155     {
156         [[NSNotificationCenter defaultCenter] removeObserver:notifier name:nil object:toolbar];
157     }
158
159     void fireVisibilityChanged()
160     {
161         Q_ASSERT(qtToolbar);
162         Q_EMIT qtToolbar->visibilityChanged(qtToolbar->isVisible());
163     }
164
165     void fireShowsBaselineSeparatorChanged()
166     {
167         Q_ASSERT(qtToolbar);
168         Q_EMIT qtToolbar->showsBaselineSeparatorChanged(qtToolbar->showsBaselineSeparator());
169     }
170
171     void fireToolButtonStyleChanged()
172     {
173         Q_ASSERT(qtToolbar);
174         Q_EMIT qtToolbar->toolButtonStyleChanged(qtToolbar->toolButtonStyle());
175     }
176
177     void fireSizeModeChangedNotification()
178     {
179         Q_ASSERT(qtToolbar);
180         Q_EMIT qtToolbar->iconSizeChanged(qtToolbar->iconSize());
181         Q_EMIT qtToolbar->iconSizeChanged(qtToolbar->iconSizeType());
182     }
183
184     void fireAllowsUserCustomizationChanged()
185     {
186         Q_ASSERT(qtToolbar);
187         Q_EMIT qtToolbar->allowsUserCustomizationChanged(qtToolbar->allowsUserCustomization());
188     }
189 };
190
191 QMacNativeToolBar::QMacNativeToolBar(QObject *parent)
192     : QObject(parent), targetWindow(NULL), targetWidget(NULL), d(new QMacNativeToolBarPrivate(this))
193 {
194     setAllowsUserCustomization(true);
195     setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
196 }
197
198 QMacNativeToolBar::QMacNativeToolBar(const QString &identifier, QObject *parent)
199     : QObject(parent), targetWindow(NULL), targetWidget(NULL), d(new QMacNativeToolBarPrivate(this, identifier))
200 {
201     setAllowsUserCustomization(true);
202     setToolButtonStyle(Qt::ToolButtonTextUnderIcon);
203 }
204
205 QMacNativeToolBar::~QMacNativeToolBar()
206 {
207     [d->toolbar release];
208     delete d;
209 }
210
211 NSToolbar *QMacNativeToolBar::nativeToolbar() const
212 {
213     return d->toolbar;
214 }
215
216 QString QMacNativeToolBar::identifier() const
217 {
218     return QtMac::fromNSString([d->toolbar identifier]);
219 }
220
221 bool QMacNativeToolBar::isVisible() const
222 {
223     return [d->toolbar isVisible];
224 }
225
226 void QMacNativeToolBar::setVisible(bool visible)
227 {
228     [d->toolbar setVisible:visible];
229 }
230
231 bool QMacNativeToolBar::showsBaselineSeparator() const
232 {
233     return [d->toolbar showsBaselineSeparator];
234 }
235
236 void QMacNativeToolBar::setShowsBaselineSeparator(bool show)
237 {
238     [d->toolbar setShowsBaselineSeparator:show];
239 }
240
241 bool QMacNativeToolBar::allowsUserCustomization() const
242 {
243     return [d->toolbar allowsUserCustomization];
244 }
245
246 void QMacNativeToolBar::setAllowsUserCustomization(bool allow)
247 {
248     [d->toolbar setAllowsUserCustomization:allow];
249 }
250
251 Qt::ToolButtonStyle QMacNativeToolBar::toolButtonStyle() const
252 {
253     return toQtToolButtonStyle([d->toolbar displayMode]);
254 }
255
256 void QMacNativeToolBar::setToolButtonStyle(Qt::ToolButtonStyle toolButtonStyle)
257 {
258     [d->toolbar setDisplayMode:toNSToolbarDisplayMode(toolButtonStyle)];
259
260     // Since this isn't supported, no change event will be fired so do it manually
261     if (toolButtonStyle == Qt::ToolButtonTextBesideIcon)
262         d->fireToolButtonStyleChanged();
263 }
264
265 QSize QMacNativeToolBar::iconSize() const
266 {
267     switch (iconSizeType())
268     {
269     case QMacToolButton::IconSizeRegular:
270         return QSize(kNSToolbarIconSizeRegular, kNSToolbarIconSizeRegular);
271     case QMacToolButton::IconSizeSmall:
272         return QSize(kNSToolbarIconSizeSmall, kNSToolbarIconSizeSmall);
273     case QMacToolButton::IconSizeDefault:
274     default:
275         return QSize(kNSToolbarIconSizeDefault, kNSToolbarIconSizeDefault);
276     }
277 }
278
279 QMacToolButton::IconSize QMacNativeToolBar::iconSizeType() const
280 {
281     switch ([d->toolbar sizeMode])
282     {
283     case NSToolbarSizeModeRegular:
284         return QMacToolButton::IconSizeRegular;
285     case NSToolbarSizeModeSmall:
286         return QMacToolButton::IconSizeSmall;
287     case NSToolbarSizeModeDefault:
288     default:
289         return QMacToolButton::IconSizeDefault;
290     }
291 }
292
293 void QMacNativeToolBar::setIconSize(const QSize &iconSize)
294 {
295     if (iconSize.isEmpty())
296         setIconSize(QMacToolButton::IconSizeDefault);
297     else if (iconSize.width() <= kNSToolbarIconSizeSmall && iconSize.height() <= kNSToolbarIconSizeSmall)
298         setIconSize(QMacToolButton::IconSizeSmall);
299     else
300         setIconSize(QMacToolButton::IconSizeRegular);
301 }
302
303 void QMacNativeToolBar::setIconSize(QMacToolButton::IconSize iconSize)
304 {
305     switch (iconSize)
306     {
307     case QMacToolButton::IconSizeRegular:
308         [d->toolbar setSizeMode:NSToolbarSizeModeRegular];
309         break;
310     case QMacToolButton::IconSizeSmall:
311         [d->toolbar setSizeMode:NSToolbarSizeModeSmall];
312         break;
313     case QMacToolButton::IconSizeDefault:
314     default:
315         [d->toolbar setSizeMode:NSToolbarSizeModeDefault];
316         break;
317     }
318 }
319
320 void QMacNativeToolBar::showCustomizationSheet()
321 {
322     [d->toolbar runCustomizationPalette:nil];
323 }
324
325 QList<QMacToolButton *> QMacNativeToolBar::buttons()
326 {
327     return d->delegate->items;
328 }
329
330 QList<QMacToolButton *> QMacNativeToolBar::allowedButtons()
331 {
332     return d->delegate->allowedItems;
333 }
334
335 void QMacNativeToolBar::showInWindow(QWindow *window)
336 {
337     targetWindow = window;
338     QTimer::singleShot(100, this, SLOT(showInWindow_impl())); // ### hackety hack
339 }
340
341 void QMacNativeToolBar::showInWindow_impl()
342 {
343     if (!targetWindow) {
344         QTimer::singleShot(100, this, SLOT(showInWindow_impl()));
345         return;
346     }
347
348     NSWindow *macWindow = static_cast<NSWindow*>(
349         QGuiApplication::platformNativeInterface()->nativeResourceForWindow("nswindow", targetWindow));
350
351     if (!macWindow) {
352         QTimer::singleShot(100, this, SLOT(showInWindow_impl()));
353         return;
354     }
355
356     [macWindow setToolbar: d->toolbar];
357     [macWindow setShowsToolbarButton:YES];
358 }
359 void QMacNativeToolBar::removeFromWindow(QWindow *window)
360 {
361     if (!window)
362         return;
363
364     NSWindow *macWindow = static_cast<NSWindow*>(
365         QGuiApplication::platformNativeInterface()->nativeResourceForWindow("nswindow", window));
366     [macWindow setToolbar:nil];
367 }
368
369
370 void QMacNativeToolBar::setSelectedItem()
371 {
372 //    setSelectedItem(qobject_cast<QMacToolButton*>(sender()));
373 }
374
375 //### TODO- re-implement
376 #if 0
377 QMacToolButton *QMacNativeToolBar::setSelectedItem(QMacToolButton *action)
378 {
379     // If this action is checkable, find the corresponding NSToolBarItem on the
380     // real NSToolbar and set it to the selected item if it is checked
381     if (action && action->isCheckable())
382     {
383         checkSelectableItemSanity();
384
385         foreach (QMacToolButton *toolButton, allowedButtons())
386         {
387             if (toolButton->m_action && toolButton->m_action->isChecked())
388             {
389                 [d->toolbar setSelectedItemIdentifier:QtMac::toNSString(QString::number(qulonglong(toolButton)))];
390                 break;
391             }
392             else
393             {
394                 [d->toolbar setSelectedItemIdentifier:nil];
395             }
396         }
397     }
398
399     return action;
400 }
401
402 void QMacNativeToolBar::checkSelectableItemSanity()
403 {
404     // Find a list of all selectable actions
405     QList<QMacToolButton*> selectableActions;
406     foreach (QMacToolButton *button, allowedButtons())
407         if (button->m_action && button->m_action->isCheckable())
408             selectableActions.append(button->m_action);
409
410     // If there's more than one, we need to do some sanity checks
411     if (selectableActions.size() > 1)
412     {
413         // The action group that all selectable actions must belong to
414         QMacToolButtonGroup *group = NULL;
415
416         foreach (QMacToolButton *action, selectableActions)
417         {
418             // The first action group we find is "the" action group that
419             // all selectable actions on the toolbar must belong to
420             if (!group)
421                 group = action->actionGroup();
422
423             // An action not having a group is a failure
424             // All actions not belonging to the same group is a failure
425             // The group not being exclusive is a failure
426             if (!group || (group != action->actionGroup()) || (group && !group->isExclusive()))
427             {
428                 qWarning() << "All selectable actions in a QMacUnifiedToolBar should belong to the same exclusive QMacToolButtonGroup if there is more than one selectable action.";
429                 break;
430             }
431         }
432     }
433 }
434 #endif
435
436 QMacToolButton *QMacNativeToolBar::addAction(const QString &text)
437 {
438     return [d->delegate addActionWithText:&text];
439 }
440
441 QMacToolButton *QMacNativeToolBar::addAction(const QIcon &icon, const QString &text)
442 {
443     return [d->delegate addActionWithText:&text icon:&icon];
444 }
445
446 void QMacNativeToolBar::addSeparator()
447 {
448     addStandardItem(QMacToolButton::Space); // No Seprator on OS X.
449 }
450
451 QMacToolButton *QMacNativeToolBar::addStandardItem(QMacToolButton::StandardItem standardItem)
452 {
453     return [d->delegate addStandardItem:standardItem];
454 }
455
456 QMacToolButton *QMacNativeToolBar::addAllowedAction(const QString &text)
457 {
458     return [d->delegate addAllowedActionWithText:&text];
459 }
460
461 QMacToolButton *QMacNativeToolBar::addAllowedAction(const QIcon &icon, const QString &text)
462 {
463     return [d->delegate addAllowedActionWithText:&text icon:&icon];
464 }
465
466
467 QMacToolButton *QMacNativeToolBar::addAllowedStandardItem(QMacToolButton::StandardItem standardItem)
468 {
469     return [d->delegate addAllowedStandardItem:standardItem];
470 }
471
472 QT_END_NAMESPACE
473
474 QT_USE_NAMESPACE
475
476 @implementation QNSToolbarNotifier
477
478 - (void)notification:(NSNotification*)note
479 {
480     Q_ASSERT(pimpl);
481     if ([[note name] isEqualToString:QtNSToolbarVisibilityChangedNotification])
482         pimpl->fireVisibilityChanged();
483     else if ([[note name] isEqualToString:QtNSToolbarShowsBaselineSeparatorChangedNotification])
484         pimpl->fireShowsBaselineSeparatorChanged();
485     else if ([[note name] isEqualToString:QtNSToolbarDisplayModeChangedNotification])
486         pimpl->fireToolButtonStyleChanged();
487     else if ([[note name] isEqualToString:QtNSToolbarSizeModeChangedNotification])
488         pimpl->fireSizeModeChangedNotification();
489     else if ([[note name] isEqualToString:QtNSToolbarAllowsUserCustomizationChangedNotification])
490         pimpl->fireAllowsUserCustomizationChanged();
491 }
492
493 @end