source: trunk/src/gui/widgets/qmenu_mac.mm@ 112

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

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

File size: 72.4 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** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
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****************************************************************************/
41
42#include "qmenu.h"
43#include "qhash.h"
44#include <qdebug.h>
45#include "qapplication.h"
46#include <private/qt_mac_p.h>
47#include "qregexp.h"
48#include "qmainwindow.h"
49#include "qdockwidget.h"
50#include "qtoolbar.h"
51#include "qevent.h"
52#include "qstyle.h"
53#include "qwidgetaction.h"
54#include "qmacnativewidget_mac.h"
55
56#include <private/qapplication_p.h>
57#include <private/qcocoaapplication_mac_p.h>
58#include <private/qmenu_p.h>
59#include <private/qmenubar_p.h>
60#include <private/qcocoamenuloader_mac_p.h>
61#include <private/qcocoamenu_mac_p.h>
62#include <private/qt_cocoa_helpers_mac_p.h>
63#include <Cocoa/Cocoa.h>
64
65QT_BEGIN_NAMESPACE
66
67/*****************************************************************************
68 QMenu debug facilities
69 *****************************************************************************/
70
71/*****************************************************************************
72 QMenu globals
73 *****************************************************************************/
74bool qt_mac_no_native_menubar = false;
75bool qt_mac_no_menubar_merge = false;
76bool qt_mac_quit_menu_item_enabled = true;
77int qt_mac_menus_open_count = 0;
78
79static OSMenuRef qt_mac_create_menu(QWidget *w);
80
81#ifndef QT_MAC_USE_COCOA
82static uint qt_mac_menu_static_cmd_id = 'QT00';
83const UInt32 kMenuCreatorQt = 'cute';
84enum {
85 kMenuPropertyQAction = 'QAcT',
86 kMenuPropertyQWidget = 'QWId',
87 kMenuPropertyCausedQWidget = 'QCAU',
88 kMenuPropertyMergeMenu = 'QApP',
89 kMenuPropertyMergeList = 'QAmL',
90 kMenuPropertyWidgetActionWidget = 'QWid',
91 kMenuPropertyWidgetMenu = 'QWMe',
92
93 kHICommandAboutQt = 'AOQT',
94 kHICommandCustomMerge = 'AQt0'
95};
96#endif
97
98static struct {
99 QPointer<QMenuBar> qmenubar;
100 bool modal;
101} qt_mac_current_menubar = { 0, false };
102
103
104
105
106/*****************************************************************************
107 Externals
108 *****************************************************************************/
109extern OSViewRef qt_mac_hiview_for(const QWidget *w); //qwidget_mac.cpp
110extern HIViewRef qt_mac_hiview_for(OSWindowRef w); //qwidget_mac.cpp
111extern IconRef qt_mac_create_iconref(const QPixmap &px); //qpixmap_mac.cpp
112extern QWidget * mac_keyboard_grabber; //qwidget_mac.cpp
113extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); //qapplication_xxx.cpp
114RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
115void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
116
117/*****************************************************************************
118 QMenu utility functions
119 *****************************************************************************/
120bool qt_mac_watchingAboutToShow(QMenu *menu)
121{
122 return menu && menu->receivers(SIGNAL(aboutToShow()));
123}
124
125static int qt_mac_CountMenuItems(OSMenuRef menu)
126{
127 if (menu) {
128#ifndef QT_MAC_USE_COCOA
129 int ret = 0;
130 const int items = CountMenuItems(menu);
131 for(int i = 0; i < items; i++) {
132 MenuItemAttributes attr;
133 if (GetMenuItemAttributes(menu, i+1, &attr) == noErr &&
134 attr & kMenuItemAttrHidden)
135 continue;
136 ++ret;
137 }
138 return ret;
139#else
140 return [menu numberOfItems];
141#endif
142 }
143 return 0;
144}
145
146static bool actualMenuItemVisibility(const QMenuBarPrivate::QMacMenuBarPrivate *mbp,
147 const QMacMenuAction *action)
148{
149 bool visible = action->action->isVisible();
150 if (visible && action->action->text() == QString(QChar(0x14)))
151 return false;
152 if (visible && action->action->menu() && !action->action->menu()->actions().isEmpty() &&
153 !qt_mac_CountMenuItems(action->action->menu()->macMenu(mbp->apple_menu)) &&
154 !qt_mac_watchingAboutToShow(action->action->menu())) {
155 return false;
156 }
157 return visible;
158}
159
160#ifndef QT_MAC_USE_COCOA
161bool qt_mac_activate_action(MenuRef menu, uint command, QAction::ActionEvent action_e, bool by_accel)
162{
163 //fire event
164 QMacMenuAction *action = 0;
165 if (GetMenuCommandProperty(menu, command, kMenuCreatorQt, kMenuPropertyQAction, sizeof(action), 0, &action) != noErr) {
166 QMenuMergeList *list = 0;
167 GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
168 sizeof(list), 0, &list);
169 if (!list && qt_mac_current_menubar.qmenubar) {
170 MenuRef apple_menu = qt_mac_current_menubar.qmenubar->d_func()->mac_menubar->apple_menu;
171 GetMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList, sizeof(list), 0, &list);
172 if (list)
173 menu = apple_menu;
174 }
175 if (list) {
176 for(int i = 0; i < list->size(); ++i) {
177 QMenuMergeItem item = list->at(i);
178 if (item.command == command && item.action) {
179 action = item.action;
180 break;
181 }
182 }
183 }
184 if (!action)
185 return false;
186 }
187
188 if (action_e == QAction::Trigger && by_accel && action->ignore_accel) //no, not a real accel (ie tab)
189 return false;
190
191 // Unhighlight the highlighted menu item before triggering the action to
192 // prevent items from staying highlighted while a modal dialog is shown.
193 // This also fixed the problem that parentless modal dialogs leave
194 // the menu item highlighted (since the menu bar is cleared for these types of dialogs).
195 if (action_e == QAction::Trigger)
196 HiliteMenu(0);
197
198 action->action->activate(action_e);
199
200 //now walk up firing for each "caused" widget (like in the platform independent menu)
201 QWidget *caused = 0;
202 if (GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), 0, &caused) == noErr) {
203 MenuRef caused_menu = 0;
204 if (QMenu *qmenu2 = qobject_cast<QMenu*>(caused))
205 caused_menu = qmenu2->macMenu();
206 else if (QMenuBar *qmenubar2 = qobject_cast<QMenuBar*>(caused))
207 caused_menu = qmenubar2->macMenu();
208 else
209 caused_menu = 0;
210 while(caused_menu) {
211 //fire
212 QWidget *widget = 0;
213 GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(widget), 0, &widget);
214 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
215 if (action_e == QAction::Trigger) {
216 emit qmenu->triggered(action->action);
217 } else if (action_e == QAction::Hover) {
218 action->action->showStatusText(widget);
219 emit qmenu->hovered(action->action);
220 }
221 } else if (QMenuBar *qmenubar = qobject_cast<QMenuBar*>(widget)) {
222 if (action_e == QAction::Trigger) {
223 emit qmenubar->triggered(action->action);
224 } else if (action_e == QAction::Hover) {
225 action->action->showStatusText(widget);
226 emit qmenubar->hovered(action->action);
227 }
228 break; //nothing more..
229 }
230
231 //walk up
232 if (GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget,
233 sizeof(caused), 0, &caused) != noErr)
234 break;
235 if (QMenu *qmenu2 = qobject_cast<QMenu*>(caused))
236 caused_menu = qmenu2->macMenu();
237 else if (QMenuBar *qmenubar2 = qobject_cast<QMenuBar*>(caused))
238 caused_menu = qmenubar2->macMenu();
239 else
240 caused_menu = 0;
241 }
242 }
243 return true;
244}
245
246//lookup a QMacMenuAction in a menu
247static int qt_mac_menu_find_action(MenuRef menu, MenuCommand cmd)
248{
249 MenuItemIndex ret_idx;
250 MenuRef ret_menu;
251 if (GetIndMenuItemWithCommandID(menu, cmd, 1, &ret_menu, &ret_idx) == noErr) {
252 if (ret_menu == menu)
253 return (int)ret_idx;
254 }
255 return -1;
256}
257static int qt_mac_menu_find_action(MenuRef menu, QMacMenuAction *action)
258{
259 return qt_mac_menu_find_action(menu, action->command);
260}
261
262typedef QMultiHash<OSMenuRef, EventHandlerRef> EventHandlerHash;
263Q_GLOBAL_STATIC(EventHandlerHash, menu_eventHandlers_hash)
264
265static EventTypeSpec widget_in_menu_events[] = {
266 { kEventClassMenu, kEventMenuMeasureItemWidth },
267 { kEventClassMenu, kEventMenuMeasureItemHeight },
268 { kEventClassMenu, kEventMenuDrawItem },
269 { kEventClassMenu, kEventMenuCalculateSize }
270};
271
272static OSStatus qt_mac_widget_in_menu_eventHandler(EventHandlerCallRef er, EventRef event, void *)
273{
274 UInt32 ekind = GetEventKind(event);
275 UInt32 eclass = GetEventClass(event);
276 OSStatus result = eventNotHandledErr;
277 switch (eclass) {
278 case kEventClassMenu:
279 switch (ekind) {
280 default:
281 break;
282 case kEventMenuMeasureItemWidth: {
283 MenuItemIndex item;
284 GetEventParameter(event, kEventParamMenuItemIndex, typeMenuItemIndex,
285 0, sizeof(item), 0, &item);
286 OSMenuRef menu;
287 GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
288 QWidget *widget;
289 if (GetMenuItemProperty(menu, item, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
290 sizeof(widget), 0, &widget) == noErr) {
291 short width = short(widget->sizeHint().width());
292 SetEventParameter(event, kEventParamMenuItemWidth, typeSInt16,
293 sizeof(short), &width);
294 result = noErr;
295 }
296 break; }
297 case kEventMenuMeasureItemHeight: {
298 MenuItemIndex item;
299 GetEventParameter(event, kEventParamMenuItemIndex, typeMenuItemIndex,
300 0, sizeof(item), 0, &item);
301 OSMenuRef menu;
302 GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
303 QWidget *widget;
304 if (GetMenuItemProperty(menu, item, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
305 sizeof(widget), 0, &widget) == noErr && widget) {
306 short height = short(widget->sizeHint().height());
307 SetEventParameter(event, kEventParamMenuItemHeight, typeSInt16,
308 sizeof(short), &height);
309 result = noErr;
310 }
311 break; }
312 case kEventMenuDrawItem:
313 result = noErr;
314 break;
315 case kEventMenuCalculateSize: {
316 result = CallNextEventHandler(er, event);
317 if (result == noErr) {
318 OSMenuRef menu;
319 GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
320 HIViewRef content;
321 HIMenuGetContentView(menu, kThemeMenuTypePullDown, &content);
322 UInt16 count = CountMenuItems(menu);
323 for (MenuItemIndex i = 1; i <= count; ++i) {
324 QWidget *widget;
325 if (GetMenuItemProperty(menu, i, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
326 sizeof(widget), 0, &widget) == noErr && widget) {
327 RgnHandle itemRgn = qt_mac_get_rgn();
328 GetControlRegion(content, i, itemRgn);
329
330 Rect bounds;
331 GetRegionBounds( itemRgn, &bounds );
332 qt_mac_dispose_rgn(itemRgn);
333 widget->setGeometry(bounds.left, bounds.top,
334 bounds.right - bounds.left, bounds.bottom - bounds.top);
335 }
336 }
337 }
338 break; }
339 }
340 }
341 return result;
342}
343
344//handling of events for menurefs created by Qt..
345static EventTypeSpec menu_events[] = {
346 { kEventClassCommand, kEventCommandProcess },
347 { kEventClassMenu, kEventMenuTargetItem },
348 { kEventClassMenu, kEventMenuOpening },
349 { kEventClassMenu, kEventMenuClosed }
350};
351
352// Special case for kEventMenuMatchKey, see qt_mac_create_menu below.
353static EventTypeSpec menu_menu_events[] = {
354 { kEventClassMenu, kEventMenuMatchKey }
355};
356
357OSStatus qt_mac_menu_event(EventHandlerCallRef er, EventRef event, void *)
358{
359 QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
360
361 bool handled_event = true;
362 UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
363 switch(eclass) {
364 case kEventClassCommand:
365 if (ekind == kEventCommandProcess) {
366 UInt32 context;
367 GetEventParameter(event, kEventParamMenuContext, typeUInt32,
368 0, sizeof(context), 0, &context);
369 HICommand cmd;
370 GetEventParameter(event, kEventParamDirectObject, typeHICommand,
371 0, sizeof(cmd), 0, &cmd);
372 if (!mac_keyboard_grabber && (context & kMenuContextKeyMatching)) {
373 QMacMenuAction *action = 0;
374 if (GetMenuCommandProperty(cmd.menu.menuRef, cmd.commandID, kMenuCreatorQt,
375 kMenuPropertyQAction, sizeof(action), 0, &action) == noErr) {
376 QWidget *widget = 0;
377 if (qApp->activePopupWidget())
378 widget = (qApp->activePopupWidget()->focusWidget() ?
379 qApp->activePopupWidget()->focusWidget() : qApp->activePopupWidget());
380 else if (QApplicationPrivate::focus_widget)
381 widget = QApplicationPrivate::focus_widget;
382 if (widget) {
383 int key = action->action->shortcut();
384 QKeyEvent accel_ev(QEvent::ShortcutOverride, (key & (~Qt::KeyboardModifierMask)),
385 Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask));
386 accel_ev.ignore();
387 qt_sendSpontaneousEvent(widget, &accel_ev);
388 if (accel_ev.isAccepted()) {
389 handled_event = false;
390 break;
391 }
392 }
393 }
394 }
395 handled_event = qt_mac_activate_action(cmd.menu.menuRef, cmd.commandID,
396 QAction::Trigger, context & kMenuContextKeyMatching);
397 }
398 break;
399 case kEventClassMenu: {
400 MenuRef menu;
401 GetEventParameter(event, kEventParamDirectObject, typeMenuRef, NULL, sizeof(menu), NULL, &menu);
402 if (ekind == kEventMenuMatchKey) {
403 // Don't activate any actions if we are showing a native modal dialog,
404 // the key events should go to the dialog in this case.
405 if (QApplicationPrivate::native_modal_dialog_active)
406 return menuItemNotFoundErr;
407
408 handled_event = false;
409 } else if (ekind == kEventMenuTargetItem) {
410 MenuCommand command;
411 GetEventParameter(event, kEventParamMenuCommand, typeMenuCommand,
412 0, sizeof(command), 0, &command);
413 handled_event = qt_mac_activate_action(menu, command, QAction::Hover, false);
414 } else if (ekind == kEventMenuOpening || ekind == kEventMenuClosed) {
415 qt_mac_menus_open_count += (ekind == kEventMenuOpening) ? 1 : -1;
416 MenuRef mr;
417 GetEventParameter(event, kEventParamDirectObject, typeMenuRef,
418 0, sizeof(mr), 0, &mr);
419
420 QWidget *widget = 0;
421 if (GetMenuItemProperty(mr, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(widget), 0, &widget) == noErr) {
422 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
423 handled_event = true;
424 if (ekind == kEventMenuOpening) {
425 emit qmenu->aboutToShow();
426
427 int merged = 0;
428 const QMenuPrivate::QMacMenuPrivate *mac_menu = qmenu->d_func()->mac_menu;
429 for(int i = 0; i < mac_menu->actionItems.size(); ++i) {
430 QMacMenuAction *action = mac_menu->actionItems.at(i);
431 if (action->action->isSeparator()) {
432 bool hide = false;
433 if(!action->action->isVisible()) {
434 hide = true;
435 } else if (merged && merged == i) {
436 hide = true;
437 } else {
438 for(int l = i+1; l < mac_menu->actionItems.size(); ++l) {
439 QMacMenuAction *action = mac_menu->actionItems.at(l);
440 if (action->merged) {
441 hide = true;
442 } else if (action->action->isSeparator()) {
443 if (hide)
444 break;
445 } else if (!action->merged) {
446 hide = false;
447 break;
448 }
449 }
450 }
451
452 const int index = qt_mac_menu_find_action(mr, action);
453 if (hide) {
454 ++merged;
455 ChangeMenuItemAttributes(mr, index, kMenuItemAttrHidden, 0);
456 } else {
457 ChangeMenuItemAttributes(mr, index, 0, kMenuItemAttrHidden);
458 }
459 } else if (action->merged) {
460 ++merged;
461 }
462 }
463 } else {
464 emit qmenu->aboutToHide();
465 }
466 }
467 }
468 } else {
469 handled_event = false;
470 }
471 break; }
472 default:
473 handled_event = false;
474 break;
475 }
476 if (!handled_event) //let the event go through
477 return CallNextEventHandler(er, event);
478 return noErr; //we eat the event
479}
480static EventHandlerRef mac_menu_event_handler = 0;
481static EventHandlerUPP mac_menu_eventUPP = 0;
482static void qt_mac_cleanup_menu_event()
483{
484 if (mac_menu_event_handler) {
485 RemoveEventHandler(mac_menu_event_handler);
486 mac_menu_event_handler = 0;
487 }
488 if (mac_menu_eventUPP) {
489 DisposeEventHandlerUPP(mac_menu_eventUPP);
490 mac_menu_eventUPP = 0;
491 }
492}
493static inline void qt_mac_create_menu_event_handler()
494{
495 if (!mac_menu_event_handler) {
496 mac_menu_eventUPP = NewEventHandlerUPP(qt_mac_menu_event);
497 InstallEventHandler(GetApplicationEventTarget(), mac_menu_eventUPP,
498 GetEventTypeCount(menu_events), menu_events, 0,
499 &mac_menu_event_handler);
500 qAddPostRoutine(qt_mac_cleanup_menu_event);
501 }
502}
503
504
505//enabling of commands
506static void qt_mac_command_set_enabled(MenuRef menu, UInt32 cmd, bool b)
507{
508 if (cmd == kHICommandQuit)
509 qt_mac_quit_menu_item_enabled = b;
510
511 if (b) {
512 EnableMenuCommand(menu, cmd);
513 if (MenuRef dock_menu = GetApplicationDockTileMenu())
514 EnableMenuCommand(dock_menu, cmd);
515 } else {
516 DisableMenuCommand(menu, cmd);
517 if (MenuRef dock_menu = GetApplicationDockTileMenu())
518 DisableMenuCommand(dock_menu, cmd);
519 }
520}
521
522static bool qt_mac_auto_apple_menu(MenuCommand cmd)
523{
524 return (cmd == kHICommandPreferences || cmd == kHICommandQuit);
525}
526
527static void qt_mac_get_accel(quint32 accel_key, quint32 *modif, quint32 *key) {
528 if (modif) {
529 *modif = 0;
530 if ((accel_key & Qt::CTRL) != Qt::CTRL)
531 *modif |= kMenuNoCommandModifier;
532 if ((accel_key & Qt::META) == Qt::META)
533 *modif |= kMenuControlModifier;
534 if ((accel_key & Qt::ALT) == Qt::ALT)
535 *modif |= kMenuOptionModifier;
536 if ((accel_key & Qt::SHIFT) == Qt::SHIFT)
537 *modif |= kMenuShiftModifier;
538 }
539
540 accel_key &= ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL);
541 if (key) {
542 *key = 0;
543 if (accel_key == Qt::Key_Return)
544 *key = kMenuReturnGlyph;
545 else if (accel_key == Qt::Key_Enter)
546 *key = kMenuEnterGlyph;
547 else if (accel_key == Qt::Key_Tab)
548 *key = kMenuTabRightGlyph;
549 else if (accel_key == Qt::Key_Backspace)
550 *key = kMenuDeleteLeftGlyph;
551 else if (accel_key == Qt::Key_Delete)
552 *key = kMenuDeleteRightGlyph;
553 else if (accel_key == Qt::Key_Escape)
554 *key = kMenuEscapeGlyph;
555 else if (accel_key == Qt::Key_PageUp)
556 *key = kMenuPageUpGlyph;
557 else if (accel_key == Qt::Key_PageDown)
558 *key = kMenuPageDownGlyph;
559 else if (accel_key == Qt::Key_Up)
560 *key = kMenuUpArrowGlyph;
561 else if (accel_key == Qt::Key_Down)
562 *key = kMenuDownArrowGlyph;
563 else if (accel_key == Qt::Key_Left)
564 *key = kMenuLeftArrowGlyph;
565 else if (accel_key == Qt::Key_Right)
566 *key = kMenuRightArrowGlyph;
567 else if (accel_key == Qt::Key_CapsLock)
568 *key = kMenuCapsLockGlyph;
569 else if (accel_key >= Qt::Key_F1 && accel_key <= Qt::Key_F15)
570 *key = (accel_key - Qt::Key_F1) + kMenuF1Glyph;
571 else if (accel_key == Qt::Key_Home)
572 *key = kMenuNorthwestArrowGlyph;
573 else if (accel_key == Qt::Key_End)
574 *key = kMenuSoutheastArrowGlyph;
575 }
576}
577#else // Cocoa
578static inline void syncNSMenuItemVisiblity(NSMenuItem *menuItem, bool actionVisibility)
579{
580 [menuItem setHidden:NO];
581 [menuItem setHidden:YES];
582 [menuItem setHidden:!actionVisibility];
583}
584
585static inline void syncMenuBarItemsVisiblity(const QMenuBarPrivate::QMacMenuBarPrivate *mac_menubar)
586{
587 const QList<QMacMenuAction *> &menubarActions = mac_menubar->actionItems;
588 for (int i = 0; i < menubarActions.size(); ++i) {
589 const QMacMenuAction *action = menubarActions.at(i);
590 syncNSMenuItemVisiblity(action->menuItem, actualMenuItemVisibility(mac_menubar, action));
591 }
592}
593
594static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader()
595{
596 return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
597}
598
599static NSMenuItem *createNSMenuItem(const QString &title)
600{
601 NSMenuItem *item = [[NSMenuItem alloc]
602 initWithTitle:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(title)))
603 action:@selector(qtDispatcherToQAction:) keyEquivalent:@""];
604 [item setTarget:getMenuLoader()];
605 return item;
606}
607#endif
608
609
610
611// helper that recurses into a menu structure and en/dis-ables them
612void qt_mac_set_modal_state_helper_recursive(OSMenuRef menu, OSMenuRef merge, bool on)
613{
614#ifndef QT_MAC_USE_COCOA
615 for (int i = 0; i < CountMenuItems(menu); i++) {
616 OSMenuRef submenu;
617 GetMenuItemHierarchicalMenu(menu, i+1, &submenu);
618 if (submenu != merge) {
619 if (submenu)
620 qt_mac_set_modal_state_helper_recursive(submenu, merge, on);
621 if (on)
622 DisableMenuItem(submenu, 0);
623 else
624 EnableMenuItem(submenu, 0);
625 }
626 }
627#else
628 for (NSMenuItem *item in [menu itemArray]) {
629 OSMenuRef submenu = [item submenu];
630 if (submenu != merge) {
631 if (submenu)
632 qt_mac_set_modal_state_helper_recursive(submenu, merge, on);
633 if (!on) {
634 // The item should follow what the QAction has.
635 if ([item tag]) {
636 QAction *action = reinterpret_cast<QAction *>([item tag]);
637 [item setEnabled:action->isEnabled()];
638 } else {
639 [item setEnabled:YES];
640 }
641 } else {
642 [item setEnabled:NO];
643 }
644 }
645 }
646#endif
647}
648
649//toggling of modal state
650static void qt_mac_set_modal_state(OSMenuRef menu, bool on)
651{
652#ifndef QT_MAC_USE_COCOA
653 OSMenuRef merge = 0;
654 GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
655 sizeof(merge), 0, &merge);
656
657 qt_mac_set_modal_state_helper_recursive(menu, merge, on);
658
659 UInt32 commands[] = { kHICommandQuit, kHICommandPreferences, kHICommandAbout, kHICommandAboutQt, 0 };
660 for(int c = 0; commands[c]; c++) {
661 bool enabled = !on;
662 if (enabled) {
663 QMacMenuAction *action = 0;
664 GetMenuCommandProperty(menu, commands[c], kMenuCreatorQt, kMenuPropertyQAction,
665 sizeof(action), 0, &action);
666 if (!action && merge) {
667 GetMenuCommandProperty(merge, commands[c], kMenuCreatorQt, kMenuPropertyQAction,
668 sizeof(action), 0, &action);
669 if (!action) {
670 QMenuMergeList *list = 0;
671 GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
672 sizeof(list), 0, &list);
673 for(int i = 0; list && i < list->size(); ++i) {
674 QMenuMergeItem item = list->at(i);
675 if (item.command == commands[c] && item.action) {
676 action = item.action;
677 break;
678 }
679 }
680 }
681 }
682
683 if (!action) {
684 if (commands[c] != kHICommandQuit)
685 enabled = false;
686 } else {
687 enabled = action->action ? action->action->isEnabled() : 0;
688 }
689 }
690 qt_mac_command_set_enabled(menu, commands[c], enabled);
691 }
692#else
693 OSMenuRef merge = QMenuPrivate::mergeMenuHash.value(menu);
694 qt_mac_set_modal_state_helper_recursive(menu, merge, on);
695 // I'm ignoring the special items now, since they should get handled via a syncAction()
696#endif
697}
698
699bool qt_mac_menubar_is_open()
700{
701 return qt_mac_menus_open_count > 0;
702}
703
704void qt_mac_clear_menubar()
705{
706#ifndef QT_MAC_USE_COCOA
707 MenuRef clear_menu = 0;
708 if (CreateNewMenu(0, 0, &clear_menu) == noErr) {
709 SetRootMenu(clear_menu);
710 ReleaseMenu(clear_menu);
711 } else {
712 qWarning("QMenu: Internal error at %s:%d", __FILE__, __LINE__);
713 }
714 ClearMenuBar();
715 qt_mac_command_set_enabled(0, kHICommandPreferences, false);
716 InvalMenuBar();
717#else
718 QMacCocoaAutoReleasePool pool;
719 QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
720 NSMenu *menu = [loader menu];
721 [loader ensureAppMenuInMenu:menu];
722 [NSApp setMainMenu:menu];
723#endif
724}
725
726
727QMacMenuAction::~QMacMenuAction()
728{
729#ifdef QT_MAC_USE_COCOA
730 [menu release];
731 [menuItem setTag:nil];
732 [menuItem release];
733#endif
734}
735
736#ifndef QT_MAC_USE_COCOA
737static MenuCommand qt_mac_menu_merge_action(MenuRef merge, QMacMenuAction *action)
738#else
739static NSMenuItem *qt_mac_menu_merge_action(OSMenuRef merge, QMacMenuAction *action)
740#endif
741{
742 if (qt_mac_no_menubar_merge || action->action->menu() || action->action->isSeparator()
743 || action->action->menuRole() == QAction::NoRole)
744 return 0;
745
746 QString t = qt_mac_removeMnemonics(action->action->text().toLower());
747 int st = t.lastIndexOf(QLatin1Char('\t'));
748 if (st != -1)
749 t.remove(st, t.length()-st);
750 t.replace(QRegExp(QString::fromLatin1("\\.*$")), QLatin1String("")); //no ellipses
751 //now the fun part
752#ifndef QT_MAC_USE_COCOA
753 MenuCommand ret = 0;
754#else
755 NSMenuItem *ret = 0;
756 QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
757#endif
758 switch (action->action->menuRole()) {
759 case QAction::NoRole:
760 ret = 0;
761 break;
762 case QAction::ApplicationSpecificRole:
763#ifndef QT_MAC_USE_COCOA
764 {
765 QMenuMergeList *list = 0;
766 if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
767 sizeof(list), 0, &list) == noErr && list) {
768 MenuCommand lastCustom = kHICommandCustomMerge;
769 for(int i = 0; i < list->size(); ++i) {
770 QMenuMergeItem item = list->at(i);
771 if (item.command == lastCustom)
772 ++lastCustom;
773 }
774 ret = lastCustom;
775 } else {
776 // The list hasn't been created, so, must be the first one.
777 ret = kHICommandCustomMerge;
778 }
779 }
780#else
781 ret = [loader appSpecificMenuItem];
782#endif
783 break;
784 case QAction::AboutRole:
785#ifndef QT_MAC_USE_COCOA
786 ret = kHICommandAbout;
787#else
788 ret = [loader aboutMenuItem];
789#endif
790 break;
791 case QAction::AboutQtRole:
792#ifndef QT_MAC_USE_COCOA
793 ret = kHICommandAboutQt;
794#else
795 ret = [loader aboutQtMenuItem];
796#endif
797 break;
798 case QAction::QuitRole:
799#ifndef QT_MAC_USE_COCOA
800 ret = kHICommandQuit;
801#else
802 ret = [loader quitMenuItem];
803#endif
804 break;
805 case QAction::PreferencesRole:
806#ifndef QT_MAC_USE_COCOA
807 ret = kHICommandPreferences;
808#else
809 ret = [loader preferencesMenuItem];
810#endif
811 break;
812 case QAction::TextHeuristicRole: {
813 QString aboutString = QMenuBar::tr("About").toLower();
814 if (t.startsWith(aboutString) || t.endsWith(aboutString)) {
815 if (t.indexOf(QRegExp(QString::fromLatin1("qt$"), Qt::CaseInsensitive)) == -1) {
816#ifndef QT_MAC_USE_COCOA
817 ret = kHICommandAbout;
818#else
819 ret = [loader aboutMenuItem];
820#endif
821 } else {
822#ifndef QT_MAC_USE_COCOA
823 ret = kHICommandAboutQt;
824#else
825 ret = [loader aboutQtMenuItem];
826#endif
827 }
828 } else if (t.startsWith(QMenuBar::tr("Config").toLower())
829 || t.startsWith(QMenuBar::tr("Preference").toLower())
830 || t.startsWith(QMenuBar::tr("Options").toLower())
831 || t.startsWith(QMenuBar::tr("Setting").toLower())
832 || t.startsWith(QMenuBar::tr("Setup").toLower())) {
833#ifndef QT_MAC_USE_COCOA
834 ret = kHICommandPreferences;
835#else
836 ret = [loader preferencesMenuItem];
837#endif
838 } else if (t.startsWith(QMenuBar::tr("Quit").toLower())
839 || t.startsWith(QMenuBar::tr("Exit").toLower())) {
840#ifndef QT_MAC_USE_COCOA
841 ret = kHICommandQuit;
842#else
843 ret = [loader quitMenuItem];
844#endif
845 }
846 }
847 break;
848 }
849
850#ifndef QT_MAC_USE_COCOA
851 QMenuMergeList *list = 0;
852 if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
853 sizeof(list), 0, &list) == noErr && list) {
854 for(int i = 0; i < list->size(); ++i) {
855 QMenuMergeItem item = list->at(i);
856 if (item.command == ret && item.action)
857 return 0;
858 }
859 }
860
861 QAction *cmd_action = 0;
862 if (GetMenuCommandProperty(merge, ret, kMenuCreatorQt, kMenuPropertyQAction,
863 sizeof(cmd_action), 0, &cmd_action) == noErr && cmd_action)
864 return 0; //already taken
865#else
866 if (QMenuMergeList *list = QMenuPrivate::mergeMenuItemsHash.value(merge)) {
867 for(int i = 0; i < list->size(); ++i) {
868 const QMenuMergeItem &item = list->at(i);
869 if (item.menuItem == ret && item.action)
870 return 0;
871 }
872 }
873
874 if ([ret tag] != 0)
875 ret = 0; // already taken
876#endif
877 return ret;
878}
879
880static QString qt_mac_menu_merge_text(QMacMenuAction *action)
881{
882 QString ret;
883#ifdef QT_MAC_USE_COCOA
884 QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
885#endif
886 if (action->action->menuRole() == QAction::ApplicationSpecificRole)
887 ret = action->action->text();
888#ifndef QT_MAC_USE_COCOA
889 else if (action->command == kHICommandAbout)
890 ret = QMenuBar::tr("About %1").arg(qAppName());
891 else if (action->command == kHICommandAboutQt)
892 ret = QMenuBar::tr("About Qt");
893 else if (action->command == kHICommandPreferences)
894 ret = QMenuBar::tr("Preferences");
895 else if (action->command == kHICommandQuit)
896 ret = QMenuBar::tr("Quit %1").arg(qAppName());
897#else
898 else if (action->menuItem == [loader aboutMenuItem])
899 ret = QMenuBar::tr("About %1").arg(qAppName());
900 else if (action->menuItem == [loader aboutQtMenuItem])
901 ret = QMenuBar::tr("About Qt");
902 else if (action->menuItem == [loader preferencesMenuItem])
903 ret = QMenuBar::tr("Preferences");
904 else if (action->menuItem == [loader quitMenuItem])
905 ret = QMenuBar::tr("Quit %1").arg(qAppName());
906#endif
907 return ret;
908}
909
910static QKeySequence qt_mac_menu_merge_accel(QMacMenuAction *action)
911{
912 QKeySequence ret;
913#ifdef QT_MAC_USE_COCOA
914 QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
915#endif
916 if (action->action->menuRole() == QAction::ApplicationSpecificRole)
917 ret = action->action->shortcut();
918#ifndef QT_MAC_USE_COCOA
919 else if (action->command == kHICommandPreferences)
920 ret = QKeySequence(Qt::CTRL+Qt::Key_Comma);
921 else if (action->command == kHICommandQuit)
922 ret = QKeySequence(Qt::CTRL+Qt::Key_Q);
923#else
924 else if (action->menuItem == [loader preferencesMenuItem])
925 ret = QKeySequence(Qt::CTRL+Qt::Key_Comma);
926 else if (action->menuItem == [loader quitMenuItem])
927 ret = QKeySequence(Qt::CTRL+Qt::Key_Q);
928#endif
929 return ret;
930}
931
932void Q_GUI_EXPORT qt_mac_set_menubar_icons(bool b)
933{ QApplication::instance()->setAttribute(Qt::AA_DontShowIconsInMenus, !b); }
934void Q_GUI_EXPORT qt_mac_set_native_menubar(bool b) { qt_mac_no_native_menubar = !b; }
935void Q_GUI_EXPORT qt_mac_set_menubar_merge(bool b) { qt_mac_no_menubar_merge = !b; }
936
937/*****************************************************************************
938 QMenu bindings
939 *****************************************************************************/
940QMenuPrivate::QMacMenuPrivate::QMacMenuPrivate() : menu(0)
941{
942}
943
944QMenuPrivate::QMacMenuPrivate::~QMacMenuPrivate()
945{
946#ifndef QT_MAC_USE_COCOA
947 for(QList<QMacMenuAction*>::Iterator it = actionItems.begin(); it != actionItems.end(); ++it) {
948 QMacMenuAction *action = (*it);
949 RemoveMenuCommandProperty(action->menu, action->command, kMenuCreatorQt, kMenuPropertyQAction);
950 if (action->merged) {
951 QMenuMergeList *list = 0;
952 GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
953 sizeof(list), 0, &list);
954 for(int i = 0; list && i < list->size(); ) {
955 QMenuMergeItem item = list->at(i);
956 if (item.action == action)
957 list->removeAt(i);
958 else
959 ++i;
960 }
961 }
962 delete action;
963 }
964 if (menu) {
965 EventHandlerHash::iterator it = menu_eventHandlers_hash()->find(menu);
966 while (it != menu_eventHandlers_hash()->end() && it.key() == menu) {
967 RemoveEventHandler(it.value());
968 ++it;
969 }
970 menu_eventHandlers_hash()->remove(menu);
971 ReleaseMenu(menu);
972 }
973#else
974 QMacCocoaAutoReleasePool pool;
975 while (actionItems.size()) {
976 QMacMenuAction *action = actionItems.takeFirst();
977 if (QMenuMergeList *list = mergeMenuItemsHash.value(action->menu)) {
978 int i = 0;
979 while (i < list->size()) {
980 const QMenuMergeItem &item = list->at(i);
981 if (item.action == action)
982 list->removeAt(i);
983 else
984 ++i;
985 }
986 }
987 delete action;
988 }
989 mergeMenuHash.remove(menu);
990 mergeMenuItemsHash.remove(menu);
991 [menu release];
992#endif
993}
994
995void
996QMenuPrivate::QMacMenuPrivate::addAction(QAction *a, QMacMenuAction *before, QMenuPrivate *qmenu)
997{
998 QMacMenuAction *action = new QMacMenuAction;
999 action->action = a;
1000 action->ignore_accel = 0;
1001 action->merged = 0;
1002 action->menu = 0;
1003#ifndef QT_MAC_USE_COCOA
1004 action->command = qt_mac_menu_static_cmd_id++;
1005#endif
1006 addAction(action, before, qmenu);
1007}
1008
1009void
1010QMenuPrivate::QMacMenuPrivate::addAction(QMacMenuAction *action, QMacMenuAction *before, QMenuPrivate *qmenu)
1011{
1012#ifdef QT_MAC_USE_COCOA
1013 QMacCocoaAutoReleasePool pool;
1014 Q_UNUSED(qmenu);
1015#endif
1016 if (!action)
1017 return;
1018 int before_index = actionItems.indexOf(before);
1019 if (before_index < 0) {
1020 before = 0;
1021 before_index = actionItems.size();
1022 }
1023 actionItems.insert(before_index, action);
1024
1025#ifndef QT_MAC_USE_COCOA
1026 int index = qt_mac_menu_find_action(menu, action);
1027#else
1028 [menu retain];
1029 [action->menu release];
1030#endif
1031 action->menu = menu;
1032
1033 /* When the action is considered a mergable action it
1034 will stay that way, until removed.. */
1035 if (!qt_mac_no_menubar_merge) {
1036#ifndef QT_MAC_USE_COCOA
1037 MenuRef merge = 0;
1038 GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
1039 sizeof(merge), 0, &merge);
1040#else
1041 OSMenuRef merge = QMenuPrivate::mergeMenuHash.value(menu);
1042#endif
1043 if (merge) {
1044#ifndef QT_MAC_USE_COCOA
1045 if (MenuCommand cmd = qt_mac_menu_merge_action(merge, action)) {
1046 action->merged = 1;
1047 action->menu = merge;
1048 action->command = cmd;
1049 if (qt_mac_auto_apple_menu(cmd))
1050 index = 0; //no need
1051
1052 QMenuMergeList *list = 0;
1053 if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
1054 sizeof(list), 0, &list) != noErr || !list) {
1055 list = new QMenuMergeList;
1056 SetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
1057 sizeof(list), &list);
1058 }
1059 list->append(QMenuMergeItem(cmd, action));
1060 }
1061#else
1062 if (NSMenuItem *cmd = qt_mac_menu_merge_action(merge, action)) {
1063 action->merged = 1;
1064 [merge retain];
1065 [action->menu release];
1066 action->menu = merge;
1067 [cmd retain];
1068 [cmd setAction:@selector(qtDispatcherToQAction:)];
1069 [cmd setTarget:getMenuLoader()];
1070 [action->menuItem release];
1071 action->menuItem = cmd;
1072 QMenuMergeList *list = QMenuPrivate::mergeMenuItemsHash.value(merge);
1073 if (!list) {
1074 list = new QMenuMergeList;
1075 QMenuPrivate::mergeMenuItemsHash.insert(merge, list);
1076 }
1077 list->append(QMenuMergeItem(cmd, action));
1078 }
1079#endif
1080 }
1081 }
1082
1083#ifdef QT_MAC_USE_COCOA
1084 NSMenuItem *newItem = action->menuItem;
1085#endif
1086 if (
1087#ifndef QT_MAC_USE_COCOA
1088 index == -1
1089#else
1090 newItem == 0
1091#endif
1092 ) {
1093#ifndef QT_MAC_USE_COCOA
1094 index = before_index;
1095 MenuItemAttributes attr = kMenuItemAttrAutoRepeat;
1096#else
1097 newItem = createNSMenuItem(action->action->text());
1098 action->menuItem = newItem;
1099#endif
1100 if (before) {
1101#ifndef QT_MAC_USE_COCOA
1102 InsertMenuItemTextWithCFString(action->menu, 0, qMax(before_index, 0), attr, action->command);
1103#else
1104 [menu insertItem:newItem atIndex:qMax(before_index, 0)];
1105#endif
1106 } else {
1107#ifndef QT_MAC_USE_COCOA
1108 // Append the menu item to the menu. If it is a kHICommandAbout or a kHICommandAboutQt append
1109 // a separator also (to get a separator above "Preferences"), but make sure that we don't
1110 // add separators between two "about" items.
1111
1112 // Build a set of all commands that could possibly be before the separator.
1113 QSet<MenuCommand> mergedItems;
1114 mergedItems.insert(kHICommandAbout);
1115 mergedItems.insert(kHICommandAboutQt);
1116 mergedItems.insert(kHICommandCustomMerge);
1117
1118 QMenuMergeList *list = 0;
1119 if (GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
1120 sizeof(list), 0, &list) == noErr && list) {
1121 for (int i = 0; i < list->size(); ++i) {
1122 MenuCommand command = list->at(i).command;
1123 if (command > kHICommandCustomMerge) {
1124 mergedItems.insert(command);
1125 }
1126 }
1127 }
1128
1129 const int itemCount = CountMenuItems(action->menu);
1130 MenuItemAttributes testattr;
1131 GetMenuItemAttributes(action->menu, itemCount , &testattr);
1132 if (mergedItems.contains(action->command)
1133 && (testattr & kMenuItemAttrSeparator)) {
1134 InsertMenuItemTextWithCFString(action->menu, 0, qMax(itemCount - 1, 0), attr, action->command);
1135 index = itemCount;
1136 } else {
1137 MenuItemIndex tmpIndex;
1138 AppendMenuItemTextWithCFString(action->menu, 0, attr, action->command, &tmpIndex);
1139 index = tmpIndex;
1140 if (mergedItems.contains(action->command))
1141 AppendMenuItemTextWithCFString(action->menu, 0, kMenuItemAttrSeparator, 0, &tmpIndex);
1142 }
1143#else
1144 [menu addItem:newItem];
1145#endif
1146 }
1147
1148 QWidget *widget = qmenu ? qmenu->widgetItems.value(action->action) : 0;
1149 if (widget) {
1150#ifndef QT_MAC_USE_COCOA
1151 ChangeMenuAttributes(action->menu, kMenuAttrDoNotCacheImage, 0);
1152 attr = kMenuItemAttrCustomDraw;
1153 SetMenuItemProperty(action->menu, index, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
1154 sizeof(QWidget *), &widget);
1155 HIViewRef content;
1156 HIMenuGetContentView(action->menu, kThemeMenuTypePullDown, &content);
1157
1158 EventHandlerRef eventHandlerRef;
1159 InstallMenuEventHandler(action->menu, qt_mac_widget_in_menu_eventHandler,
1160 GetEventTypeCount(widget_in_menu_events),
1161 widget_in_menu_events, 0, &eventHandlerRef);
1162 menu_eventHandlers_hash()->insert(action->menu, eventHandlerRef);
1163
1164 QWidget *menuWidget = 0;
1165 GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyWidgetMenu,
1166 sizeof(menuWidget), 0, &menuWidget);
1167 if(!menuWidget) {
1168 menuWidget = new QMacNativeWidget(content);
1169 SetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyWidgetMenu,
1170 sizeof(menuWidget), &menuWidget);
1171 menuWidget->show();
1172 }
1173 widget->setParent(menuWidget);
1174#else
1175 QMacNativeWidget *container = new QMacNativeWidget(0);
1176 container->resize(widget->sizeHint());
1177 widget->setAttribute(Qt::WA_LayoutUsesWidgetRect);
1178 widget->setParent(container);
1179
1180 NSView *containerView = qt_mac_nativeview_for(container);
1181 [containerView setAutoresizesSubviews:YES];
1182 [containerView setAutoresizingMask:NSViewWidthSizable];
1183 [qt_mac_nativeview_for(widget) setAutoresizingMask:NSViewWidthSizable];
1184
1185 [newItem setView:containerView];
1186 container->show();
1187#endif
1188 widget->show();
1189 }
1190
1191 } else {
1192#ifndef QT_MAC_USE_COCOA
1193 qt_mac_command_set_enabled(action->menu, action->command, !QApplicationPrivate::modalState());
1194#else
1195 [newItem setEnabled:!QApplicationPrivate::modalState()];
1196#endif
1197 }
1198#ifndef QT_MAC_USE_COCOA
1199 SetMenuCommandProperty(action->menu, action->command, kMenuCreatorQt, kMenuPropertyQAction,
1200 sizeof(action), &action);
1201#else
1202 [newItem setTag:long(static_cast<QAction *>(action->action))];
1203#endif
1204 syncAction(action);
1205}
1206
1207// return an autoreleased string given a QKeySequence (currently only looks at the first one).
1208NSString *keySequenceToKeyEqivalent(const QKeySequence &accel)
1209{
1210 quint32 accel_key = (accel[0] & ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL));
1211 unichar keyEquiv[1] = { 0 };
1212 if (accel_key == Qt::Key_Return)
1213 keyEquiv[0] = kReturnCharCode;
1214 else if (accel_key == Qt::Key_Enter)
1215 keyEquiv[0] = kEnterCharCode;
1216 else if (accel_key == Qt::Key_Tab)
1217 keyEquiv[0] = kTabCharCode;
1218 else if (accel_key == Qt::Key_Backspace)
1219 keyEquiv[0] = kBackspaceCharCode;
1220 else if (accel_key == Qt::Key_Delete)
1221 keyEquiv[0] = NSDeleteFunctionKey;
1222 else if (accel_key == Qt::Key_Escape)
1223 keyEquiv[0] = kEscapeCharCode;
1224 else if (accel_key == Qt::Key_PageUp)
1225 keyEquiv[0] = NSPageUpFunctionKey;
1226 else if (accel_key == Qt::Key_PageDown)
1227 keyEquiv[0] = NSPageDownFunctionKey;
1228 else if (accel_key == Qt::Key_Up)
1229 keyEquiv[0] = NSUpArrowFunctionKey;
1230 else if (accel_key == Qt::Key_Down)
1231 keyEquiv[0] = NSDownArrowFunctionKey;
1232 else if (accel_key == Qt::Key_Left)
1233 keyEquiv[0] = NSLeftArrowFunctionKey;
1234 else if (accel_key == Qt::Key_Right)
1235 keyEquiv[0] = NSRightArrowFunctionKey;
1236 else if (accel_key == Qt::Key_CapsLock)
1237 keyEquiv[0] = kMenuCapsLockGlyph; // ### Cocoa has no equivalent
1238 else if (accel_key >= Qt::Key_F1 && accel_key <= Qt::Key_F15)
1239 keyEquiv[0] = (accel_key - Qt::Key_F1) + NSF1FunctionKey;
1240 else if (accel_key == Qt::Key_Home)
1241 keyEquiv[0] = NSHomeFunctionKey;
1242 else if (accel_key == Qt::Key_End)
1243 keyEquiv[0] = NSEndFunctionKey;
1244 else
1245 keyEquiv[0] = unichar(QChar(accel_key).toLower().unicode());
1246 return [NSString stringWithCharacters:keyEquiv length:1];
1247}
1248
1249// return the cocoa modifier mask for the QKeySequence (currently only looks at the first one).
1250NSUInteger keySequenceModifierMask(const QKeySequence &accel)
1251{
1252 NSUInteger ret = 0;
1253 quint32 accel_key = accel[0];
1254 if ((accel_key & Qt::CTRL) == Qt::CTRL)
1255 ret |= NSCommandKeyMask;
1256 if ((accel_key & Qt::META) == Qt::META)
1257 ret |= NSControlKeyMask;
1258 if ((accel_key & Qt::ALT) == Qt::ALT)
1259 ret |= NSAlternateKeyMask;
1260 if ((accel_key & Qt::SHIFT) == Qt::SHIFT)
1261 ret |= NSShiftKeyMask;
1262 return ret;
1263}
1264
1265void
1266QMenuPrivate::QMacMenuPrivate::syncAction(QMacMenuAction *action)
1267{
1268 if (!action)
1269 return;
1270
1271#ifndef QT_MAC_USE_COCOA
1272 const int index = qt_mac_menu_find_action(action->menu, action);
1273 if (index == -1)
1274 return;
1275#else
1276 NSMenuItem *item = action->menuItem;
1277 if (!item)
1278 return;
1279#endif
1280
1281#ifndef QT_MAC_USE_COCOA
1282 if (!action->action->isVisible()) {
1283 ChangeMenuItemAttributes(action->menu, index, kMenuItemAttrHidden, 0);
1284 return;
1285 }
1286 ChangeMenuItemAttributes(action->menu, index, 0, kMenuItemAttrHidden);
1287#else
1288 QMacCocoaAutoReleasePool pool;
1289 NSMenu *menu = [item menu];
1290 bool actionVisible = action->action->isVisible();
1291 [item setHidden:!actionVisible];
1292 if (!actionVisible)
1293 return;
1294#endif
1295
1296#ifndef QT_MAC_USE_COCOA
1297 if (action->action->isSeparator()) {
1298 ChangeMenuItemAttributes(action->menu, index, kMenuItemAttrSeparator, 0);
1299 return;
1300 }
1301 ChangeMenuItemAttributes(action->menu, index, 0, kMenuItemAttrSeparator);
1302#else
1303 int itemIndex = [menu indexOfItem:item];
1304 Q_ASSERT(itemIndex != -1);
1305 if (action->action->isSeparator()) {
1306 action->menuItem = [NSMenuItem separatorItem];
1307 [action->menuItem retain];
1308 [menu insertItem: action->menuItem atIndex:itemIndex];
1309 [menu removeItem:item];
1310 [item release];
1311 item = action->menuItem;
1312 return;
1313 } else if ([item isSeparatorItem]) {
1314 // I'm no longer a separator...
1315 action->menuItem = createNSMenuItem(action->action->text());
1316 [menu insertItem:action->menuItem atIndex:itemIndex];
1317 [menu removeItem:item];
1318 [item release];
1319 item = action->menuItem;
1320 }
1321#endif
1322
1323 //find text (and accel)
1324 action->ignore_accel = 0;
1325 QString text = action->action->text();
1326 QKeySequence accel = action->action->shortcut();
1327 {
1328 int st = text.lastIndexOf(QLatin1Char('\t'));
1329 if (st != -1) {
1330 action->ignore_accel = 1;
1331 accel = QKeySequence(text.right(text.length()-(st+1)));
1332 text.remove(st, text.length()-st);
1333 }
1334 }
1335 {
1336 QString cmd_text = qt_mac_menu_merge_text(action);
1337 if (!cmd_text.isEmpty()) {
1338 text = cmd_text;
1339 accel = qt_mac_menu_merge_accel(action);
1340 }
1341 }
1342 if (accel.count() > 1)
1343 text += QLatin1String(" (****)"); //just to denote a multi stroke shortcut
1344
1345 QString finalString = qt_mac_removeMnemonics(text);
1346
1347#ifndef QT_MAC_USE_COCOA
1348 MenuItemDataRec data;
1349 memset(&data, '\0', sizeof(data));
1350
1351 //Carbon text
1352 data.whichData |= kMenuItemDataCFString;
1353 QCFString cfstring(finalString); // Hold the reference to the end of the function.
1354 data.cfText = cfstring;
1355
1356 // Carbon enabled
1357 data.whichData |= kMenuItemDataEnabled;
1358 data.enabled = action->action->isEnabled();
1359 // Carbon icon
1360 data.whichData |= kMenuItemDataIconHandle;
1361 if (!action->action->icon().isNull()
1362 && action->action->isIconVisibleInMenu()) {
1363 data.iconType = kMenuIconRefType;
1364 data.iconHandle = (Handle)qt_mac_create_iconref(action->action->icon().pixmap(22, QIcon::Normal));
1365 } else {
1366 data.iconType = kMenuNoIcon;
1367 }
1368 if (action->action->font().resolve()) { // Carbon font
1369 if (action->action->font().bold())
1370 data.style |= bold;
1371 if (action->action->font().underline())
1372 data.style |= underline;
1373 if (action->action->font().italic())
1374 data.style |= italic;
1375 if (data.style)
1376 data.whichData |= kMenuItemDataStyle;
1377 data.whichData |= kMenuItemDataFontID;
1378 data.fontID = action->action->font().macFontID();
1379 }
1380#else
1381 // Cocoa Font and title
1382 if (action->action->font().resolve()) {
1383 const QFont &actionFont = action->action->font();
1384 NSFont *customMenuFont = [NSFont fontWithName:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(actionFont.family())))
1385 size:actionFont.pointSize()];
1386 NSArray *keys = [NSArray arrayWithObjects:NSFontAttributeName, nil];
1387 NSArray *objects = [NSArray arrayWithObjects:customMenuFont, nil];
1388 NSDictionary *attributes = [NSDictionary dictionaryWithObjects:objects forKeys:keys];
1389 NSAttributedString *str = [[[NSAttributedString alloc] initWithString:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(finalString)))
1390 attributes:attributes] autorelease];
1391 [item setAttributedTitle: str];
1392 } else {
1393 [item setTitle: reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(finalString)))];
1394 }
1395 [item setTitle:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(qt_mac_removeMnemonics(text))))];
1396
1397 // Cocoa Enabled
1398 [item setEnabled: action->action->isEnabled()];
1399
1400 // Cocoa icon
1401 NSImage *nsimage = 0;
1402 if (!action->action->icon().isNull() && action->action->isIconVisibleInMenu()) {
1403 nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(action->action->icon().pixmap(22, QIcon::Normal)));
1404 }
1405 [item setImage:nsimage];
1406 [nsimage release];
1407#endif
1408
1409 if (action->action->menu()) { //submenu
1410#ifndef QT_MAC_USE_COCOA
1411 data.whichData |= kMenuItemDataSubmenuHandle;
1412 data.submenuHandle = action->action->menu()->macMenu();
1413 QWidget *caused = 0;
1414 GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(caused), 0, &caused);
1415 SetMenuItemProperty(data.submenuHandle, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), &caused);
1416#else
1417 [item setSubmenu:static_cast<NSMenu *>(action->action->menu()->macMenu())];
1418#endif
1419 } else { //respect some other items
1420#ifndef QT_MAC_USE_COCOA
1421 //shortcuts (say we are setting them all so that we can also clear them).
1422 data.whichData |= kMenuItemDataCmdKey;
1423 data.whichData |= kMenuItemDataCmdKeyModifiers;
1424 data.whichData |= kMenuItemDataCmdKeyGlyph;
1425 if (!accel.isEmpty()) {
1426 qt_mac_get_accel(accel[0], (quint32*)&data.cmdKeyModifiers, (quint32*)&data.cmdKeyGlyph);
1427 if (data.cmdKeyGlyph == 0)
1428 data.cmdKey = (UniChar)accel[0];
1429 }
1430#else
1431 [item setSubmenu:0];
1432 if (!accel.isEmpty()) {
1433 [item setKeyEquivalent:keySequenceToKeyEqivalent(accel)];
1434 [item setKeyEquivalentModifierMask:keySequenceModifierMask(accel)];
1435 } else {
1436 [item setKeyEquivalent:@""];
1437 [item setKeyEquivalentModifierMask:NSCommandKeyMask];
1438 }
1439#endif
1440 }
1441#ifndef QT_MAC_USE_COCOA
1442 //mark glyph
1443 data.whichData |= kMenuItemDataMark;
1444 if (action->action->isChecked()) {
1445#if 0
1446 if (action->action->actionGroup() &&
1447 action->action->actionGroup()->isExclusive())
1448 data.mark = diamondMark;
1449 else
1450#endif
1451 data.mark = checkMark;
1452 } else {
1453 data.mark = noMark;
1454 }
1455
1456 //actually set it
1457 SetMenuItemData(action->menu, action->command, true, &data);
1458
1459 // Free up memory
1460 if (data.iconHandle)
1461 ReleaseIconRef(IconRef(data.iconHandle));
1462#else
1463 //mark glyph
1464 [item setState:action->action->isChecked() ? NSOnState : NSOffState];
1465#endif
1466}
1467
1468void
1469QMenuPrivate::QMacMenuPrivate::removeAction(QMacMenuAction *action)
1470{
1471 if (!action)
1472 return;
1473#ifndef QT_MAC_USE_COCOA
1474 if (action->command == kHICommandQuit || action->command == kHICommandPreferences)
1475 qt_mac_command_set_enabled(action->menu, action->command, false);
1476 else
1477 DeleteMenuItem(action->menu, qt_mac_menu_find_action(action->menu, action));
1478#else
1479 QMacCocoaAutoReleasePool pool;
1480 QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
1481 if (action->menuItem == [loader quitMenuItem] || action->menuItem == [loader preferencesMenuItem])
1482 [action->menuItem setEnabled:false];
1483 else
1484 [[action->menuItem menu] removeItem:action->menuItem];
1485#endif
1486 actionItems.removeAll(action);
1487}
1488
1489OSMenuRef
1490QMenuPrivate::macMenu(OSMenuRef merge)
1491{
1492 Q_UNUSED(merge);
1493 Q_Q(QMenu);
1494 if (mac_menu && mac_menu->menu)
1495 return mac_menu->menu;
1496 if (!mac_menu)
1497 mac_menu = new QMacMenuPrivate;
1498 mac_menu->menu = qt_mac_create_menu(q);
1499 if (merge) {
1500#ifndef QT_MAC_USE_COCOA
1501 SetMenuItemProperty(mac_menu->menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu, sizeof(merge), &merge);
1502#else
1503 mergeMenuHash.insert(mac_menu->menu, merge);
1504#endif
1505 }
1506 QList<QAction*> items = q->actions();
1507 for(int i = 0; i < items.count(); i++)
1508 mac_menu->addAction(items[i], 0, this);
1509 return mac_menu->menu;
1510}
1511
1512/*!
1513 \internal
1514*/
1515void QMenuPrivate::setMacMenuEnabled(bool enable)
1516{
1517 if (!macMenu(0))
1518 return;
1519
1520 QMacCocoaAutoReleasePool pool;
1521 if (enable) {
1522 for (int i = 0; i < mac_menu->actionItems.count(); ++i) {
1523 QMacMenuAction *menuItem = mac_menu->actionItems.at(i);
1524 if (menuItem && menuItem->action && menuItem->action->isEnabled()) {
1525#ifndef QT_MAC_USE_COCOA
1526 // Only enable those items which contains an enabled QAction.
1527 // i == 0 -> the menu itself, hence i + 1 for items.
1528 EnableMenuItem(mac_menu->menu, i + 1);
1529#else
1530 [menuItem->menuItem setEnabled:true];
1531#endif
1532 }
1533 }
1534 } else {
1535#ifndef QT_MAC_USE_COCOA
1536 DisableAllMenuItems(mac_menu->menu);
1537#else
1538 NSMenu *menu = mac_menu->menu;
1539 for (NSMenuItem *item in [menu itemArray]) {
1540 [item setEnabled:false];
1541 }
1542#endif
1543 }
1544}
1545
1546/*!
1547 \internal
1548
1549 This function will return the OSMenuRef used to create the native menu bar
1550 bindings.
1551
1552 If Qt is built against Carbon, the OSMenuRef is a MenuRef that can be used
1553 with Carbon's Menu Manager API.
1554
1555 If Qt is built against Cocoa, the OSMenuRef is a NSMenu pointer.
1556
1557 \warning This function is not portable.
1558
1559 \sa QMenuBar::macMenu()
1560*/
1561OSMenuRef QMenu::macMenu(OSMenuRef merge) { return d_func()->macMenu(merge); }
1562
1563/*****************************************************************************
1564 QMenuBar bindings
1565 *****************************************************************************/
1566typedef QHash<QWidget *, QMenuBar *> MenuBarHash;
1567Q_GLOBAL_STATIC(MenuBarHash, menubars)
1568static QMenuBar *fallback = 0;
1569QMenuBarPrivate::QMacMenuBarPrivate::QMacMenuBarPrivate() : menu(0), apple_menu(0)
1570{
1571}
1572
1573QMenuBarPrivate::QMacMenuBarPrivate::~QMacMenuBarPrivate()
1574{
1575 for(QList<QMacMenuAction*>::Iterator it = actionItems.begin(); it != actionItems.end(); ++it)
1576 delete (*it);
1577#ifndef QT_MAC_USE_COCOA
1578 if (apple_menu) {
1579 QMenuMergeList *list = 0;
1580 GetMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
1581 sizeof(list), 0, &list);
1582 if (list) {
1583 RemoveMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList);
1584 delete list;
1585 }
1586 ReleaseMenu(apple_menu);
1587 }
1588 if (menu)
1589 ReleaseMenu(menu);
1590#else
1591 [apple_menu release];
1592 [menu release];
1593#endif
1594}
1595
1596void
1597QMenuBarPrivate::QMacMenuBarPrivate::addAction(QAction *a, QMacMenuAction *before)
1598{
1599 if (a->isSeparator() || !menu)
1600 return;
1601 QMacMenuAction *action = new QMacMenuAction;
1602 action->action = a;
1603 action->ignore_accel = 1;
1604#ifndef QT_MAC_USE_COCOA
1605 action->command = qt_mac_menu_static_cmd_id++;
1606#endif
1607 addAction(action, before);
1608}
1609
1610void
1611QMenuBarPrivate::QMacMenuBarPrivate::addAction(QMacMenuAction *action, QMacMenuAction *before)
1612{
1613 if (!action || !menu)
1614 return;
1615
1616 int before_index = actionItems.indexOf(before);
1617 if (before_index < 0) {
1618 before = 0;
1619 before_index = actionItems.size();
1620 }
1621 actionItems.insert(before_index, action);
1622
1623 MenuItemIndex index = actionItems.size()-1;
1624
1625 action->menu = menu;
1626#ifdef QT_MAC_USE_COCOA
1627 QMacCocoaAutoReleasePool pool;
1628 [action->menu retain];
1629 NSMenuItem *newItem = createNSMenuItem(action->action->text());
1630 action->menuItem = newItem;
1631#endif
1632 if (before) {
1633#ifndef QT_MAC_USE_COCOA
1634 InsertMenuItemTextWithCFString(action->menu, 0, qMax(1, before_index+1), 0, action->command);
1635#else
1636 [menu insertItem:newItem atIndex:qMax(1, before_index + 1)];
1637#endif
1638 index = before_index;
1639 } else {
1640#ifndef QT_MAC_USE_COCOA
1641 AppendMenuItemTextWithCFString(action->menu, 0, 0, action->command, &index);
1642#else
1643 [menu addItem:newItem];
1644#endif
1645 }
1646#ifndef QT_MAC_USE_COCOA
1647 SetMenuItemProperty(action->menu, index, kMenuCreatorQt, kMenuPropertyQAction, sizeof(action),
1648 &action);
1649#else
1650 [newItem setTag:long(static_cast<QAction *>(action->action))];
1651#endif
1652 syncAction(action);
1653}
1654
1655void
1656QMenuBarPrivate::QMacMenuBarPrivate::syncAction(QMacMenuAction *action)
1657{
1658 if (!action || !menu)
1659 return;
1660#ifndef QT_MAC_USE_COCOA
1661 const int index = qt_mac_menu_find_action(action->menu, action);
1662#else
1663 QMacCocoaAutoReleasePool pool;
1664 NSMenuItem *item = action->menuItem;
1665#endif
1666
1667 OSMenuRef submenu = 0;
1668 bool release_submenu = false;
1669 if (action->action->menu()) {
1670 if ((submenu = action->action->menu()->macMenu(apple_menu))) {
1671#ifndef QT_MAC_USE_COCOA
1672 QWidget *caused = 0;
1673 GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(caused), 0, &caused);
1674 SetMenuItemProperty(submenu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), &caused);
1675#else
1676 [item setSubmenu:submenu];
1677#endif
1678 }
1679#ifndef QT_MAC_USE_COCOA
1680 } else { // create a submenu to act as menu
1681 release_submenu = true;
1682 CreateNewMenu(0, 0, &submenu);
1683#endif
1684 }
1685
1686 if (submenu) {
1687 bool visible = actualMenuItemVisibility(this, action);
1688#ifndef QT_MAC_USE_COCOA
1689 SetMenuItemHierarchicalMenu(action->menu, index, submenu);
1690 SetMenuTitleWithCFString(submenu, QCFString(qt_mac_removeMnemonics(action->action->text())));
1691 if (visible)
1692 ChangeMenuAttributes(submenu, 0, kMenuAttrHidden);
1693 else
1694 ChangeMenuAttributes(submenu, kMenuAttrHidden, 0);
1695#else
1696 [item setSubmenu: submenu];
1697 [submenu setTitle:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(qt_mac_removeMnemonics(action->action->text()))))];
1698 syncNSMenuItemVisiblity(item, visible);
1699#endif
1700 if (release_submenu) { //no pointers to it
1701#ifndef QT_MAC_USE_COCOA
1702 ReleaseMenu(submenu);
1703#else
1704 [submenu release];
1705#endif
1706 }
1707 } else {
1708 qWarning("QMenu: No OSMenuRef created for popup menu");
1709 }
1710}
1711
1712void
1713QMenuBarPrivate::QMacMenuBarPrivate::removeAction(QMacMenuAction *action)
1714{
1715 if (!action || !menu)
1716 return;
1717#ifndef QT_MAC_USE_COCOA
1718 DeleteMenuItem(action->menu, qt_mac_menu_find_action(action->menu, action));
1719#else
1720 QMacCocoaAutoReleasePool pool;
1721 [action->menu removeItem:action->menuItem];
1722#endif
1723 actionItems.removeAll(action);
1724}
1725
1726void
1727QMenuBarPrivate::macCreateMenuBar(QWidget *parent)
1728{
1729 Q_Q(QMenuBar);
1730 static int checkEnv = -1;
1731 if (qt_mac_no_native_menubar == false && checkEnv < 0) {
1732 checkEnv = !qgetenv("QT_MAC_NO_NATIVE_MENUBAR").isEmpty();
1733 qt_mac_no_native_menubar = checkEnv;
1734 }
1735 if (!qt_mac_no_native_menubar) {
1736 extern void qt_event_request_menubarupdate(); //qapplication_mac.cpp
1737 qt_event_request_menubarupdate();
1738 if (!parent && !fallback) {
1739 fallback = q;
1740 mac_menubar = new QMacMenuBarPrivate;
1741 } else if (parent && parent->isWindow()) {
1742 menubars()->insert(q->window(), q);
1743 mac_menubar = new QMacMenuBarPrivate;
1744 }
1745 }
1746}
1747
1748void QMenuBarPrivate::macDestroyMenuBar()
1749{
1750 Q_Q(QMenuBar);
1751 QMacCocoaAutoReleasePool pool;
1752 if (fallback == q)
1753 fallback = 0;
1754 delete mac_menubar;
1755 QWidget *tlw = q->window();
1756 menubars()->remove(tlw);
1757 mac_menubar = 0;
1758
1759 if (qt_mac_current_menubar.qmenubar == q) {
1760 extern void qt_event_request_menubarupdate(); //qapplication_mac.cpp
1761 qt_event_request_menubarupdate();
1762 }
1763}
1764
1765OSMenuRef QMenuBarPrivate::macMenu()
1766{
1767 Q_Q(QMenuBar);
1768 if (!mac_menubar) {
1769 return 0;
1770 } else if (!mac_menubar->menu) {
1771 mac_menubar->menu = qt_mac_create_menu(q);
1772 ProcessSerialNumber mine, front;
1773 if (GetCurrentProcess(&mine) == noErr && GetFrontProcess(&front) == noErr) {
1774 if (!qt_mac_no_menubar_merge && !mac_menubar->apple_menu) {
1775 mac_menubar->apple_menu = qt_mac_create_menu(q);
1776#ifndef QT_MAC_USE_COCOA
1777 MenuItemIndex index;
1778 AppendMenuItemTextWithCFString(mac_menubar->menu, 0, 0, 0, &index);
1779
1780 SetMenuTitleWithCFString(mac_menubar->apple_menu, QCFString(QString(QChar(0x14))));
1781 SetMenuItemHierarchicalMenu(mac_menubar->menu, index, mac_menubar->apple_menu);
1782 SetMenuItemProperty(mac_menubar->apple_menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(q), &q);
1783#else
1784 [mac_menubar->apple_menu setTitle:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(QString(QChar(0x14)))))];
1785 NSMenuItem *apple_menuItem = [[NSMenuItem alloc] init];
1786 [apple_menuItem setSubmenu:mac_menubar->menu];
1787 [mac_menubar->apple_menu addItem:apple_menuItem];
1788 [apple_menuItem release];
1789#endif
1790 }
1791 if (mac_menubar->apple_menu) {
1792#ifndef QT_MAC_USE_COCOA
1793 SetMenuItemProperty(mac_menubar->menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
1794 sizeof(mac_menubar->apple_menu), &mac_menubar->apple_menu);
1795#else
1796 QMenuPrivate::mergeMenuHash.insert(mac_menubar->menu, mac_menubar->apple_menu);
1797#endif
1798 }
1799 QList<QAction*> items = q->actions();
1800 for(int i = 0; i < items.count(); i++)
1801 mac_menubar->addAction(items[i]);
1802 }
1803 }
1804 return mac_menubar->menu;
1805}
1806
1807/*!
1808 \internal
1809
1810 This function will return the OSMenuRef used to create the native menu bar
1811 bindings. This OSMenuRef is then set as the root menu for the Menu
1812 Manager.
1813
1814 \warning This function is not portable.
1815
1816 \sa QMenu::macMenu()
1817*/
1818OSMenuRef QMenuBar::macMenu() { return d_func()->macMenu(); }
1819
1820/* !
1821 \internal
1822 Ancestor function that crosses windows (QWidget::isAncestorOf
1823 only considers widgets within the same window).
1824*/
1825static bool qt_mac_is_ancestor(QWidget* possibleAncestor, QWidget *child)
1826{
1827 QWidget * current = child->parentWidget();
1828 while (current != 0) {
1829 if (current == possibleAncestor)
1830 return true;
1831 current = current->parentWidget();
1832 }
1833 return false;
1834}
1835
1836/* !
1837 \internal
1838 Returns true if the entries of menuBar should be disabled,
1839 based on the modality type of modalWidget.
1840*/
1841static bool qt_mac_should_disable_menu(QMenuBar *menuBar, QWidget *modalWidget)
1842{
1843 if (modalWidget == 0 || menuBar == 0)
1844 return false;
1845 const Qt::WindowModality modality = modalWidget->windowModality();
1846 if (modality == Qt::ApplicationModal) {
1847 return true;
1848 } else if (modality == Qt::WindowModal) {
1849 QWidget * parent = menuBar->parentWidget();
1850
1851 // Special case for the global menu bar: It's not associated
1852 // with a window so don't disable it.
1853 if (parent == 0)
1854 return false;
1855
1856 // Disable menu entries in menu bars that belong to ancestors of
1857 // the modal widget, leave entries in unrelated menu bars enabled.
1858 return qt_mac_is_ancestor(parent, modalWidget);
1859 }
1860 return false; // modality == NonModal
1861}
1862
1863static void cancelAllMenuTracking()
1864{
1865#ifdef QT_MAC_USE_COCOA
1866 QMacCocoaAutoReleasePool pool;
1867 NSMenu *mainMenu = [NSApp mainMenu];
1868 [mainMenu cancelTracking];
1869 for (NSMenuItem *item in [mainMenu itemArray]) {
1870 if ([item submenu]) {
1871 [[item submenu] cancelTracking];
1872 }
1873 }
1874#endif
1875}
1876
1877/*!
1878 \internal
1879
1880 This function will update the current menu bar and set it as the
1881 active menu bar in the Menu Manager.
1882
1883 \warning This function is not portable.
1884
1885 \sa QMenu::macMenu(), QMenuBar::macMenu()
1886*/
1887bool QMenuBar::macUpdateMenuBar()
1888{
1889 if (qt_mac_no_native_menubar) //nothing to be done..
1890 return true;
1891
1892 cancelAllMenuTracking();
1893 QMenuBar *mb = 0;
1894 //find a menu bar
1895 QWidget *w = qApp->activeWindow();
1896
1897 if (!w) {
1898 QWidgetList tlws = QApplication::topLevelWidgets();
1899 for(int i = 0; i < tlws.size(); ++i) {
1900 QWidget *tlw = tlws.at(i);
1901 if ((tlw->isVisible() && tlw->windowType() != Qt::Tool &&
1902 tlw->windowType() != Qt::Popup)) {
1903 w = tlw;
1904 break;
1905 }
1906 }
1907 }
1908 if (w) {
1909 mb = menubars()->value(w);
1910#ifndef QT_NO_MAINWINDOW
1911 QDockWidget *dw = qobject_cast<QDockWidget *>(w);
1912 if (!mb && dw) {
1913 QMainWindow *mw = qobject_cast<QMainWindow *>(dw->parentWidget());
1914 if (mw && (mb = menubars()->value(mw)))
1915 w = mw;
1916 }
1917#endif
1918 while(w && !mb)
1919 mb = menubars()->value((w = w->parentWidget()));
1920 }
1921 if (!mb)
1922 mb = fallback;
1923 //now set it
1924 bool ret = false;
1925 if (mb) {
1926#ifdef QT_MAC_USE_COCOA
1927 QMacCocoaAutoReleasePool pool;
1928#endif
1929 if (OSMenuRef menu = mb->macMenu()) {
1930#ifndef QT_MAC_USE_COCOA
1931 SetRootMenu(menu);
1932#else
1933 QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
1934 [loader ensureAppMenuInMenu:menu];
1935 [NSApp setMainMenu:menu];
1936 syncMenuBarItemsVisiblity(mb->d_func()->mac_menubar);
1937#endif
1938 QWidget *modalWidget = qApp->activeModalWidget();
1939 if (mb != menubars()->value(modalWidget)) {
1940 qt_mac_set_modal_state(menu, qt_mac_should_disable_menu(mb, modalWidget));
1941 }
1942 }
1943 qt_mac_current_menubar.qmenubar = mb;
1944 qt_mac_current_menubar.modal = QApplicationPrivate::modalState();
1945 ret = true;
1946 } else if (qt_mac_current_menubar.qmenubar) {
1947 const bool modal = QApplicationPrivate::modalState();
1948 if (modal != qt_mac_current_menubar.modal) {
1949 ret = true;
1950 if (OSMenuRef menu = qt_mac_current_menubar.qmenubar->macMenu()) {
1951#ifndef QT_MAC_USE_COCOA
1952 SetRootMenu(menu);
1953#else
1954 QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
1955 [loader ensureAppMenuInMenu:menu];
1956 [NSApp setMainMenu:menu];
1957 syncMenuBarItemsVisiblity(qt_mac_current_menubar.qmenubar->d_func()->mac_menubar);
1958#endif
1959 QWidget *modalWidget = qApp->activeModalWidget();
1960 if (qt_mac_current_menubar.qmenubar != menubars()->value(modalWidget)) {
1961 qt_mac_set_modal_state(menu, qt_mac_should_disable_menu(mb, modalWidget));
1962 }
1963 }
1964 qt_mac_current_menubar.modal = modal;
1965 }
1966 }
1967 if(!ret)
1968 qt_mac_clear_menubar();
1969 return ret;
1970}
1971
1972QHash<OSMenuRef, OSMenuRef> QMenuPrivate::mergeMenuHash;
1973QHash<OSMenuRef, QMenuMergeList*> QMenuPrivate::mergeMenuItemsHash;
1974
1975bool QMenuPrivate::QMacMenuPrivate::merged(const QAction *action) const
1976{
1977#ifndef QT_MAC_USE_COCOA
1978 MenuRef merge = 0;
1979 GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
1980 sizeof(merge), 0, &merge);
1981 if (merge) {
1982 QMenuMergeList *list = 0;
1983 if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
1984 sizeof(list), 0, &list) == noErr && list) {
1985 for(int i = 0; i < list->size(); ++i) {
1986 QMenuMergeItem item = list->at(i);
1987 if (item.action->action == action)
1988 return true;
1989 }
1990 }
1991 }
1992#else
1993 if (OSMenuRef merge = mergeMenuHash.value(menu)) {
1994 if (QMenuMergeList *list = mergeMenuItemsHash.value(merge)) {
1995 for(int i = 0; i < list->size(); ++i) {
1996 const QMenuMergeItem &item = list->at(i);
1997 if (item.action->action == action)
1998 return true;
1999 }
2000 }
2001 }
2002#endif
2003 return false;
2004}
2005
2006//creation of the OSMenuRef
2007static OSMenuRef qt_mac_create_menu(QWidget *w)
2008{
2009 OSMenuRef ret;
2010#ifndef QT_MAC_USE_COCOA
2011 ret = 0;
2012 if (CreateNewMenu(0, 0, &ret) == noErr) {
2013 qt_mac_create_menu_event_handler();
2014 SetMenuItemProperty(ret, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(w), &w);
2015
2016 // kEventMenuMatchKey is only sent to the menu itself and not to
2017 // the application, install a separate handler for that event.
2018 EventHandlerRef eventHandlerRef;
2019 InstallMenuEventHandler(ret, qt_mac_menu_event,
2020 GetEventTypeCount(menu_menu_events),
2021 menu_menu_events, 0, &eventHandlerRef);
2022 menu_eventHandlers_hash()->insert(ret, eventHandlerRef);
2023 } else {
2024 qWarning("QMenu: Internal error");
2025 }
2026#else
2027 if (QMenu *qmenu = qobject_cast<QMenu *>(w)){
2028 ret = [[QT_MANGLE_NAMESPACE(QCocoaMenu) alloc] initWithQMenu:qmenu];
2029 } else {
2030 ret = [[NSMenu alloc] init];
2031 }
2032#endif
2033 return ret;
2034}
2035
2036
2037
2038QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.