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

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

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

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