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

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

trunk: Merged in qt 4.6.1 sources.

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