source: trunk/src/gui/widgets/qmainwindowlayout_mac.mm@ 100

Last change on this file since 100 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 21.8 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** No Commercial Usage
10** This file contains pre-release code and may not be distributed.
11** You may use this file in accordance with the terms and conditions
12** contained in the either Technology Preview License Agreement or the
13** Beta Release License Agreement.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** 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 are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
41** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
42**
43****************************************************************************/
44
45#include <private/qmainwindowlayout_p.h>
46#include <qtoolbar.h>
47#include <private/qtoolbarlayout_p.h>
48#include <private/qt_cocoa_helpers_mac_p.h>
49
50#ifndef QT_MAC_USE_COCOA
51#include <Carbon/Carbon.h>
52#else
53#include <private/qcocoatoolbardelegate_mac_p.h>
54#endif
55
56QT_BEGIN_NAMESPACE
57#ifdef QT_NAMESPACE
58
59// namespace up the stuff
60#define SS(x) #x
61#define S0(x) SS(x)
62#define S "com.trolltech.qt-" S0(QT_NAMESPACE) ".qmainwindow.qtoolbarInHIToolbar"
63#define SToolbar "com.trolltech.qt-" S0(QT_NAMESPACE) ".hitoolbar-qtoolbar"
64#define SNSToolbar "com.trolltech.qt-" S0(QT_NAMESPACE) ".qtoolbarInNSToolbar"
65#define MacToolbar "com.trolltech.qt-" S0(QT_NAMESPACE) ".qmainwindow.mactoolbar"
66
67#ifndef QT_MAC_USE_COCOA
68static CFStringRef kQToolBarHIToolbarItemClassID = CFSTR(S);
69static CFStringRef kQToolBarHIToolbarIdentifier = CFSTR(SToolbar);
70#else
71static NSString *kQToolBarNSToolbarIdentifier = @SNSToolbar;
72#endif
73static CFStringRef kQMainWindowMacToolbarID = CFSTR(MacToolbar);
74#undef SS
75#undef S0
76#undef S
77#undef SToolbar
78#undef SNSToolbar
79#undef MacToolbar
80
81#else
82#ifndef QT_MAC_USE_COCOA
83static CFStringRef kQToolBarHIToolbarItemClassID = CFSTR("com.trolltech.qt.qmainwindow.qtoolbarInHIToolbar");
84static CFStringRef kQToolBarHIToolbarIdentifier = CFSTR("com.trolltech.qt.hitoolbar-qtoolbar");
85#else
86static NSString *kQToolBarNSToolbarIdentifier = @"com.trolltech.qt.qmainwindow.qtoolbarInNSToolbar";
87#endif
88static CFStringRef kQMainWindowMacToolbarID = CFSTR("com.trolltech.qt.qmainwindow.mactoolbar");
89#endif // QT_NAMESPACE
90
91#ifndef QT_MAC_USE_COCOA
92
93static const int kEventParamQToolBar = 'QTBR';
94static const int kEventParamQMainWindowLayout = 'QMWL';
95
96const EventTypeSpec qtoolbarEvents[] =
97{
98 { kEventClassHIObject, kEventHIObjectConstruct },
99 { kEventClassHIObject, kEventHIObjectDestruct },
100 { kEventClassHIObject, kEventHIObjectInitialize },
101 { kEventClassToolbarItem, kEventToolbarItemCreateCustomView }
102};
103
104struct QToolBarInHIToolbarInfo
105{
106 QToolBarInHIToolbarInfo(HIToolbarItemRef item)
107 : toolbarItem(item), mainWindowLayout(0)
108 {}
109 HIToolbarItemRef toolbarItem;
110 QMainWindowLayout *mainWindowLayout;
111};
112
113OSStatus QMainWindowLayout::qtoolbarInHIToolbarHandler(EventHandlerCallRef inCallRef,
114 EventRef event, void *data)
115{
116 OSStatus result = eventNotHandledErr;
117 QToolBarInHIToolbarInfo *object = static_cast<QToolBarInHIToolbarInfo *>(data);
118
119 switch (GetEventClass(event)) {
120 case kEventClassHIObject:
121 switch (GetEventKind(event)) {
122 case kEventHIObjectConstruct:
123 {
124 HIObjectRef toolbarItem;
125 GetEventParameter(event, kEventParamHIObjectInstance, typeHIObjectRef,
126 0, sizeof( HIObjectRef ), 0, &toolbarItem);
127
128 QToolBarInHIToolbarInfo *item = new QToolBarInHIToolbarInfo(toolbarItem);
129 SetEventParameter(event, kEventParamHIObjectInstance, typeVoidPtr,
130 sizeof(void *), &item);
131 result = noErr;
132 }
133 break;
134 case kEventHIObjectInitialize:
135 result = CallNextEventHandler(inCallRef, event);
136 if (result == noErr) {
137 QToolBar *toolbar = 0;
138 QMainWindowLayout *layout = 0;
139 GetEventParameter(event, kEventParamQToolBar, typeVoidPtr,
140 0, sizeof(void *), 0, &toolbar);
141 GetEventParameter(event, kEventParamQMainWindowLayout, typeVoidPtr,
142 0, sizeof(void *), 0, &layout);
143 object->mainWindowLayout = layout;
144 object->mainWindowLayout->unifiedToolbarHash.insert(object->toolbarItem, toolbar);
145 HIToolbarItemChangeAttributes(object->toolbarItem,
146 kHIToolbarItemLabelDisabled, 0);
147 }
148 break;
149
150 case kEventHIObjectDestruct:
151 delete object;
152 result = noErr;
153 break;
154 }
155 break;
156
157 case kEventClassToolbarItem:
158 switch (GetEventKind(event))
159 {
160 case kEventToolbarItemCreateCustomView:
161 {
162 QToolBar *toolbar
163 = object->mainWindowLayout->unifiedToolbarHash.value(object->toolbarItem);
164 if (toolbar) {
165 HIViewRef hiview = HIViewRef(toolbar->winId());
166 SetEventParameter(event, kEventParamControlRef, typeControlRef,
167 sizeof(HIViewRef), &hiview);
168 result = noErr;
169 }
170 }
171 break;
172 }
173 break;
174 }
175 return result;
176}
177
178void QMainWindowLayout::qtMacHIToolbarRegisterQToolBarInHIToolborItemClass()
179{
180 static bool registered = false;
181
182 if (!registered) {
183 HIObjectRegisterSubclass( kQToolBarHIToolbarItemClassID,
184 kHIToolbarItemClassID, 0, QMainWindowLayout::qtoolbarInHIToolbarHandler,
185 GetEventTypeCount(qtoolbarEvents), qtoolbarEvents, 0, 0 );
186 registered = true;
187 }
188}
189
190static void GetToolbarAllowedItems(CFMutableArrayRef array)
191{
192 CFArrayAppendValue(array, kQToolBarHIToolbarIdentifier);
193}
194
195HIToolbarItemRef QMainWindowLayout::createQToolBarInHIToolbarItem(QToolBar *toolbar,
196 QMainWindowLayout *layout)
197{
198 QMainWindowLayout::qtMacHIToolbarRegisterQToolBarInHIToolborItemClass();
199
200 EventRef event;
201 HIToolbarItemRef result = 0;
202
203 CFStringRef identifier = kQToolBarHIToolbarIdentifier;
204 UInt32 options = kHIToolbarItemAllowDuplicates;
205
206 CreateEvent(0, kEventClassHIObject, kEventHIObjectInitialize,
207 GetCurrentEventTime(), 0, &event);
208 SetEventParameter(event, kEventParamToolbarItemIdentifier, typeCFStringRef,
209 sizeof(CFStringRef), &identifier);
210 SetEventParameter(event, kEventParamAttributes, typeUInt32, sizeof(UInt32), &options);
211 SetEventParameter(event, kEventParamQToolBar, typeVoidPtr, sizeof(void *), &toolbar);
212 SetEventParameter(event, kEventParamQMainWindowLayout, typeVoidPtr, sizeof(void *), &layout);
213
214 HIObjectCreate(kQToolBarHIToolbarItemClassID, event,
215 static_cast<HIObjectRef *>(&result));
216
217 ReleaseEvent(event);
218 return result;
219
220}
221
222HIToolbarItemRef QMainWindowLayout::CreateToolbarItemForIdentifier(CFStringRef identifier,
223 CFTypeRef data)
224{
225 HIToolbarItemRef item = 0;
226 if (CFStringCompare(kQToolBarHIToolbarIdentifier, identifier,
227 kCFCompareBackwards) == kCFCompareEqualTo) {
228 if (data && CFGetTypeID(data) == CFArrayGetTypeID()) {
229 CFArrayRef array = static_cast<CFArrayRef>(data);
230 QToolBar *toolbar = static_cast<QToolBar *>(const_cast<void *>(CFArrayGetValueAtIndex(array, 0)));
231 QMainWindowLayout *layout = static_cast<QMainWindowLayout *>(const_cast<void *>(CFArrayGetValueAtIndex(array, 1)));
232 item = createQToolBarInHIToolbarItem(toolbar, layout);
233 }
234 }
235 return item;
236}
237
238static const EventTypeSpec kToolbarEvents[] = {
239{ kEventClassToolbar, kEventToolbarGetDefaultIdentifiers },
240{ kEventClassToolbar, kEventToolbarGetAllowedIdentifiers },
241{ kEventClassToolbar, kEventToolbarCreateItemWithIdentifier },
242{ kEventClassToolbar, kEventToolbarItemAdded },
243{ kEventClassToolbar, kEventToolbarItemRemoved }
244};
245
246OSStatus QMainWindowLayout::qtmacToolbarDelegate(EventHandlerCallRef, EventRef event, void *data)
247{
248 QMainWindowLayout *mainWindowLayout = static_cast<QMainWindowLayout *>(data);
249 OSStatus result = eventNotHandledErr;
250 CFMutableArrayRef array;
251 CFStringRef identifier;
252 switch (GetEventKind(event)) {
253 case kEventToolbarGetDefaultIdentifiers:
254 case kEventToolbarGetAllowedIdentifiers:
255 GetEventParameter(event, kEventParamMutableArray, typeCFMutableArrayRef, 0,
256 sizeof(CFMutableArrayRef), 0, &array);
257 GetToolbarAllowedItems(array);
258 result = noErr;
259 break;
260 case kEventToolbarCreateItemWithIdentifier: {
261 HIToolbarItemRef item;
262 CFTypeRef data = 0;
263 OSStatus err = GetEventParameter(event, kEventParamToolbarItemIdentifier, typeCFStringRef,
264 0, sizeof(CFStringRef), 0, &identifier);
265 err = GetEventParameter(event, kEventParamToolbarItemConfigData, typeCFTypeRef,
266 0, sizeof(CFTypeRef), 0, &data);
267 item = CreateToolbarItemForIdentifier(identifier, data);
268 if (item) {
269 result = SetEventParameter(event, kEventParamToolbarItem, typeHIToolbarItemRef,
270 sizeof(HIToolbarItemRef), &item );
271 }
272 break;
273 }
274 case kEventToolbarItemAdded: {
275 // Double check that our "view" of the toolbar is similar.
276 HIToolbarItemRef item;
277 CFIndex index;
278 if (GetEventParameter(event, kEventParamToolbarItem, typeHIToolbarItemRef,
279 0, sizeof(HIToolbarItemRef), 0, &item) == noErr
280 && GetEventParameter(event, kEventParamIndex, typeCFIndex, 0,
281 sizeof(CFIndex), 0, &index) == noErr) {
282 CFRetain(item); // We will watch this until it's removed from the list (or bust).
283 mainWindowLayout->toolbarItemsCopy.insert(index, item);
284 QToolBar *toolbar = mainWindowLayout->unifiedToolbarHash.value(item);
285 if (toolbar) {
286 int toolbarIndex = mainWindowLayout->qtoolbarsInUnifiedToolbarList.indexOf(toolbar);
287 if (index != toolbarIndex) {
288 // Dang, we must be out of sync, rebuild it from the "toolbarItemsCopy"
289 mainWindowLayout->qtoolbarsInUnifiedToolbarList.clear();
290 for (int i = 0; i < mainWindowLayout->toolbarItemsCopy.size(); ++i) {
291 // This will either append the correct toolbar or an
292 // null toolbar. This is fine because this list
293 // is really only kept to make sure that things are but in the right order.
294 mainWindowLayout->qtoolbarsInUnifiedToolbarList.append(
295 mainWindowLayout->unifiedToolbarHash.value(mainWindowLayout->
296 toolbarItemsCopy.at(i)));
297 }
298 }
299 }
300 }
301 break;
302 }
303 case kEventToolbarItemRemoved: {
304 HIToolbarItemRef item;
305 if (GetEventParameter(event, kEventParamToolbarItem, typeHIToolbarItemRef,
306 0, sizeof(HIToolbarItemRef), 0, &item) == noErr) {
307 mainWindowLayout->unifiedToolbarHash.remove(item);
308 for (int i = 0; i < mainWindowLayout->toolbarItemsCopy.size(); ++i) {
309 if (mainWindowLayout->toolbarItemsCopy.at(i) == item) {
310 // I know about it, so release it.
311 mainWindowLayout->toolbarItemsCopy.removeAt(i);
312 mainWindowLayout->qtoolbarsInUnifiedToolbarList.removeAt(i);
313 CFRelease(item);
314 break;
315 }
316 }
317 }
318 break;
319 }
320 }
321 return result;
322}
323#endif // ! QT_MAC_USE_COCOA
324
325#ifndef kWindowUnifiedTitleAndToolbarAttribute
326#define kWindowUnifiedTitleAndToolbarAttribute (1 << 7)
327#endif
328
329void QMainWindowLayout::updateHIToolBarStatus()
330{
331 bool useMacToolbar = layoutState.mainWindow->unifiedTitleAndToolBarOnMac();
332 if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) {
333#ifndef QT_MAC_USE_COCOA
334 if (useMacToolbar) {
335 ChangeWindowAttributes(qt_mac_window_for(layoutState.mainWindow),
336 kWindowUnifiedTitleAndToolbarAttribute, 0);
337 } else {
338 ChangeWindowAttributes(qt_mac_window_for(layoutState.mainWindow),
339 0, kWindowUnifiedTitleAndToolbarAttribute);
340 }
341#endif
342 macWindowToolbarShow(layoutState.mainWindow, useMacToolbar);
343 }
344
345 layoutState.mainWindow->setUpdatesEnabled(false); // reduces a little bit of flicker, not all though
346 if (!useMacToolbar) {
347 OSWindowRef windowRef = qt_mac_window_for(parentWidget());
348 macWindowToolbarShow(parentWidget(), false);
349 // Move everything out of the HIToolbar into the main toolbar.
350 while (!qtoolbarsInUnifiedToolbarList.isEmpty()) {
351 // Should shrink the list by one every time.
352 layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, qtoolbarsInUnifiedToolbarList.first());
353 }
354 macWindowToolbarSet(windowRef, NULL);
355 } else {
356 QList<QToolBar *> toolbars = layoutState.mainWindow->findChildren<QToolBar *>();
357 for (int i = 0; i < toolbars.size(); ++i) {
358 QToolBar *toolbar = toolbars.at(i);
359 if (toolBarArea(toolbar) == Qt::TopToolBarArea) {
360 removeWidget(toolbar); // Do this here, because we are in an in-between state.
361 layoutState.mainWindow->addToolBar(Qt::TopToolBarArea, toolbar);
362 }
363 }
364 }
365 layoutState.mainWindow->setUpdatesEnabled(true);
366}
367
368void QMainWindowLayout::insertIntoMacToolbar(QToolBar *before, QToolBar *toolbar)
369{
370 // This layering could go on to one more level, but I decided to stop here.
371 // The HIToolbar and NSToolbar APIs are fairly similar as you will see.
372 if (toolbar == 0)
373 return;
374
375
376 QToolBarLayout *toolbarLayout = static_cast<QToolBarLayout *>(toolbar->layout());
377 toolbarSaveState.insert(toolbar, ToolBarSaveState(toolbar->isMovable(),
378 toolbar->maximumSize()));
379
380 if (toolbarLayout->hasExpandFlag() == false)
381 toolbar->setMaximumSize(toolbar->sizeHint());
382
383 toolbar->setMovable(false);
384 toolbarLayout->setUsePopupMenu(true);
385 // Make the toolbar a child of the mainwindow to avoid creating a window.
386 toolbar->setParent(layoutState.mainWindow);
387 toolbar->createWinId(); // Now create the OSViewRef.
388
389 layoutState.mainWindow->createWinId();
390
391 OSWindowRef window = qt_mac_window_for(layoutState.mainWindow);
392 int beforeIndex = qtoolbarsInUnifiedToolbarList.indexOf(before);
393 if (beforeIndex == -1)
394 beforeIndex = qtoolbarsInUnifiedToolbarList.size();
395
396 int toolbarIndex = qtoolbarsInUnifiedToolbarList.indexOf(toolbar);
397#ifndef QT_MAC_USE_COCOA
398 HIToolbarRef macToolbar = NULL;
399 if ((GetWindowToolbar(window, &macToolbar) == noErr) && !macToolbar) {
400 HIToolbarCreate(kQMainWindowMacToolbarID,
401 kHIToolbarItemAllowDuplicates, &macToolbar);
402 InstallEventHandler(HIObjectGetEventTarget(static_cast<HIToolbarRef>(macToolbar)),
403 QMainWindowLayout::qtmacToolbarDelegate, GetEventTypeCount(kToolbarEvents),
404 kToolbarEvents, this, 0);
405 HIToolbarSetDisplaySize(macToolbar, kHIToolbarDisplaySizeNormal);
406 HIToolbarSetDisplayMode(macToolbar, kHIToolbarDisplayModeIconOnly);
407 macWindowToolbarSet(window, macToolbar);
408 if (layoutState.mainWindow->isVisible())
409 macWindowToolbarShow(layoutState.mainWindow, true);
410 CFRelease(macToolbar);
411 }
412#else
413 QMacCocoaAutoReleasePool pool;
414 NSToolbar *macToolbar = [window toolbar];
415 if (macToolbar == nil) {
416 macToolbar = [[NSToolbar alloc] initWithIdentifier:(NSString *)kQMainWindowMacToolbarID];
417 [macToolbar setDisplayMode:NSToolbarDisplayModeIconOnly];
418 [macToolbar setSizeMode:NSToolbarSizeModeRegular];
419 [macToolbar setDelegate:[[QCocoaToolBarDelegate alloc] initWithMainWindowLayout:this]];
420 [window setToolbar:macToolbar];
421 [macToolbar release];
422 }
423#endif
424 if (toolbarIndex != -1) {
425 qtoolbarsInUnifiedToolbarList.removeAt(toolbarIndex);
426#ifndef QT_MAC_USE_COCOA
427 HIToolbarRemoveItemAtIndex(macToolbar, toolbarIndex);
428#else
429 [macToolbar removeItemAtIndex:toolbarIndex];
430#endif
431 }
432 qtoolbarsInUnifiedToolbarList.insert(beforeIndex, toolbar);
433#ifndef QT_MAC_USE_COCOA
434 QCFType<HIToolbarItemRef> outItem;
435 const QObject *stupidArray[] = { toolbar, this };
436 QCFType<CFArrayRef> array = CFArrayCreate(0, reinterpret_cast<const void **>(&stupidArray),
437 2, 0);
438 HIToolbarCreateItemWithIdentifier(macToolbar, kQToolBarHIToolbarIdentifier,
439 array, &outItem);
440 HIToolbarInsertItemAtIndex(macToolbar, outItem, beforeIndex);
441#else
442 NSString *toolbarID = kQToolBarNSToolbarIdentifier;
443 toolbarID = [toolbarID stringByAppendingFormat:@"%p", toolbar];
444 cocoaItemIDToToolbarHash.insert(QCFString::toQString(CFStringRef(toolbarID)), toolbar);
445 [macToolbar insertItemWithItemIdentifier:toolbarID atIndex:beforeIndex];
446#endif
447}
448
449void QMainWindowLayout::removeFromMacToolbar(QToolBar *toolbar)
450{
451 QHash<void *, QToolBar *>::iterator it = unifiedToolbarHash.begin();
452 while (it != unifiedToolbarHash.end()) {
453 if (it.value() == toolbar) {
454 // Rescue our HIView and set it on the mainWindow again.
455 bool saveVisible = !toolbar->isHidden();
456 toolbar->setParent(0);
457 toolbar->setParent(parentWidget());
458 toolbar->setVisible(saveVisible);
459 ToolBarSaveState saveState = toolbarSaveState.value(toolbar);
460 static_cast<QToolBarLayout *>(toolbar->layout())->setUsePopupMenu(false);
461 toolbar->setMovable(saveState.movable);
462 toolbar->setMaximumSize(saveState.maximumSize);
463 toolbarSaveState.remove(toolbar);
464#ifndef QT_MAC_USE_COCOA
465 HIToolbarItemRef item = static_cast<HIToolbarItemRef>(it.key());
466 HIToolbarRemoveItemAtIndex(HIToolbarItemGetToolbar(item),
467 toolbarItemsCopy.indexOf(item));
468#else
469 NSToolbarItem *item = static_cast<NSToolbarItem *>(it.key());
470 [[qt_mac_window_for(layoutState.mainWindow->window()) toolbar]
471 removeItemAtIndex:toolbarItemsCopy.indexOf(item)];
472 // In Carbon this hash and list gets emptied via events. In Cocoa, we have to do it ourselves here.
473 it = unifiedToolbarHash.erase(it);
474 qtoolbarsInUnifiedToolbarList.removeAll(toolbar);
475#endif
476 break;
477 }
478 ++it;
479 }
480}
481
482void QMainWindowLayout::cleanUpMacToolbarItems()
483{
484 for (int i = 0; i < toolbarItemsCopy.size(); ++i)
485 CFRelease(toolbarItemsCopy.at(i));
486 toolbarItemsCopy.clear();
487 unifiedToolbarHash.clear();
488}
489
490void QMainWindowLayout::fixSizeInUnifiedToolbar(QToolBar *tb) const
491{
492 QHash<void *, QToolBar *>::const_iterator it = unifiedToolbarHash.constBegin();
493 NSToolbarItem *item = nil;
494 while (it != unifiedToolbarHash.constEnd()) {
495 if (tb == it.value()) {
496 item = static_cast<NSToolbarItem *>(it.key());
497 break;
498 }
499 ++it;
500 }
501 if (item) {
502 QMacCocoaAutoReleasePool pool;
503 QWidgetItem layoutItem(tb);
504 QSize size = layoutItem.maximumSize();
505 NSSize nssize = NSMakeSize(size.width(), size.height());
506 [item setMaxSize:nssize];
507 size = layoutItem.minimumSize();
508 nssize.width = size.width();
509 nssize.height = size.height();
510 [item setMinSize:nssize];
511 }
512}
513QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.