1
/****************************************************************************
3
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4
** All rights reserved.
5
** Contact: Nokia Corporation (qt-info@nokia.com)
7
** This file is part of the QtGui module of the Qt Toolkit.
9
** $QT_BEGIN_LICENSE:LGPL$
10
** No Commercial Usage
11
** This file contains pre-release code and may not be distributed.
12
** You may use this file in accordance with the terms and conditions
13
** contained in the Technology Preview License Agreement accompanying
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.
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.
28
** If you have questions regarding the use of this file, please contact
29
** Nokia at qt-info@nokia.com.
40
****************************************************************************/
45
#include "qapplication.h"
46
#include <private/qt_mac_p.h>
48
#include "qmainwindow.h"
49
#include "qdockwidget.h"
53
#include "qwidgetaction.h"
54
#include "qmacnativewidget_mac.h"
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>
67
/*****************************************************************************
68
QMenu debug facilities
69
*****************************************************************************/
71
/*****************************************************************************
73
*****************************************************************************/
74
bool qt_mac_no_menubar_merge = false;
75
bool qt_mac_quit_menu_item_enabled = true;
76
int qt_mac_menus_open_count = 0;
78
static OSMenuRef qt_mac_create_menu(QWidget *w);
80
#ifndef QT_MAC_USE_COCOA
81
static uint qt_mac_menu_static_cmd_id = 'QT00';
82
const UInt32 kMenuCreatorQt = 'cute';
84
kMenuPropertyQAction = 'QAcT',
85
kMenuPropertyQWidget = 'QWId',
86
kMenuPropertyCausedQWidget = 'QCAU',
87
kMenuPropertyMergeMenu = 'QApP',
88
kMenuPropertyMergeList = 'QAmL',
89
kMenuPropertyWidgetActionWidget = 'QWid',
90
kMenuPropertyWidgetMenu = 'QWMe',
92
kHICommandAboutQt = 'AOQT',
93
kHICommandCustomMerge = 'AQt0'
98
QPointer<QMenuBar> qmenubar;
100
} qt_mac_current_menubar = { 0, false };
105
/*****************************************************************************
107
*****************************************************************************/
108
extern OSViewRef qt_mac_hiview_for(const QWidget *w); //qwidget_mac.cpp
109
extern HIViewRef qt_mac_hiview_for(OSWindowRef w); //qwidget_mac.cpp
110
extern IconRef qt_mac_create_iconref(const QPixmap &px); //qpixmap_mac.cpp
111
extern QWidget * mac_keyboard_grabber; //qwidget_mac.cpp
112
extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); //qapplication_xxx.cpp
113
RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
114
void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
116
/*****************************************************************************
117
QMenu utility functions
118
*****************************************************************************/
119
bool qt_mac_watchingAboutToShow(QMenu *menu)
121
return menu && menu->receivers(SIGNAL(aboutToShow()));
124
static int qt_mac_CountMenuItems(OSMenuRef menu)
127
#ifndef QT_MAC_USE_COCOA
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)
139
return [menu numberOfItems];
145
static quint32 constructModifierMask(quint32 accel_key)
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;
155
if ((accel_key & Qt::META) != Qt::META)
156
ret |= kMenuNoCommandModifier;
157
if ((accel_key & Qt::CTRL) == Qt::CTRL)
158
ret |= kMenuControlModifier;
160
if ((accel_key & Qt::CTRL) != Qt::CTRL)
161
ret |= kMenuNoCommandModifier;
162
if ((accel_key & Qt::META) == Qt::META)
163
ret |= kMenuControlModifier;
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;
178
static bool actualMenuItemVisibility(const QMenuBarPrivate::QMacMenuBarPrivate *mbp,
179
const QMacMenuAction *action)
181
bool visible = action->action->isVisible();
182
if (visible && action->action->text() == QString(QChar(0x14)))
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())) {
192
#ifndef QT_MAC_USE_COCOA
193
bool qt_mac_activate_action(MenuRef menu, uint command, QAction::ActionEvent action_e, bool by_accel)
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);
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;
220
if (action_e == QAction::Trigger && by_accel && action->ignore_accel) //no, not a real accel (ie tab)
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)
230
action->action->activate(action_e);
232
//now walk up firing for each "caused" widget (like in the platform independent menu)
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();
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);
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);
260
break; //nothing more..
264
if (GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget,
265
sizeof(caused), 0, &caused) != noErr)
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();
278
//lookup a QMacMenuAction in a menu
279
static int qt_mac_menu_find_action(MenuRef menu, MenuCommand cmd)
281
MenuItemIndex ret_idx;
283
if (GetIndMenuItemWithCommandID(menu, cmd, 1, &ret_menu, &ret_idx) == noErr) {
284
if (ret_menu == menu)
289
static int qt_mac_menu_find_action(MenuRef menu, QMacMenuAction *action)
291
return qt_mac_menu_find_action(menu, action->command);
294
typedef QMultiHash<OSMenuRef, EventHandlerRef> EventHandlerHash;
295
Q_GLOBAL_STATIC(EventHandlerHash, menu_eventHandlers_hash)
297
static EventTypeSpec widget_in_menu_events[] = {
298
{ kEventClassMenu, kEventMenuMeasureItemWidth },
299
{ kEventClassMenu, kEventMenuMeasureItemHeight },
300
{ kEventClassMenu, kEventMenuDrawItem },
301
{ kEventClassMenu, kEventMenuCalculateSize }
304
static OSStatus qt_mac_widget_in_menu_eventHandler(EventHandlerCallRef er, EventRef event, void *)
306
UInt32 ekind = GetEventKind(event);
307
UInt32 eclass = GetEventClass(event);
308
OSStatus result = eventNotHandledErr;
310
case kEventClassMenu:
314
case kEventMenuMeasureItemWidth: {
316
GetEventParameter(event, kEventParamMenuItemIndex, typeMenuItemIndex,
317
0, sizeof(item), 0, &item);
319
GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
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);
329
case kEventMenuMeasureItemHeight: {
331
GetEventParameter(event, kEventParamMenuItemIndex, typeMenuItemIndex,
332
0, sizeof(item), 0, &item);
334
GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
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);
344
case kEventMenuDrawItem:
347
case kEventMenuCalculateSize: {
348
result = CallNextEventHandler(er, event);
349
if (result == noErr) {
351
GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
353
HIMenuGetContentView(menu, kThemeMenuTypePullDown, &content);
354
UInt16 count = CountMenuItems(menu);
355
for (MenuItemIndex i = 1; i <= count; ++i) {
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);
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);
376
//handling of events for menurefs created by Qt..
377
static EventTypeSpec menu_events[] = {
378
{ kEventClassCommand, kEventCommandProcess },
379
{ kEventClassMenu, kEventMenuTargetItem },
380
{ kEventClassMenu, kEventMenuOpening },
381
{ kEventClassMenu, kEventMenuClosed }
384
// Special case for kEventMenuMatchKey, see qt_mac_create_menu below.
385
static EventTypeSpec menu_menu_events[] = {
386
{ kEventClassMenu, kEventMenuMatchKey }
389
OSStatus qt_mac_menu_event(EventHandlerCallRef er, EventRef event, void *)
391
QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
393
bool handled_event = true;
394
UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
396
case kEventClassCommand:
397
if (ekind == kEventCommandProcess) {
399
GetEventParameter(event, kEventParamMenuContext, typeUInt32,
400
0, sizeof(context), 0, &context);
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) {
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;
415
int key = action->action->shortcut();
416
QKeyEvent accel_ev(QEvent::ShortcutOverride, (key & (~Qt::KeyboardModifierMask)),
417
Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask));
419
qt_sendSpontaneousEvent(widget, &accel_ev);
420
if (accel_ev.isAccepted()) {
421
handled_event = false;
427
handled_event = qt_mac_activate_action(cmd.menu.menuRef, cmd.commandID,
428
QAction::Trigger, context & kMenuContextKeyMatching);
431
case kEventClassMenu: {
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;
440
handled_event = false;
441
} else if (ekind == kEventMenuTargetItem) {
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;
449
GetEventParameter(event, kEventParamDirectObject, typeMenuRef,
450
0, sizeof(mr), 0, &mr);
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();
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()) {
466
if(!action->action->isVisible()) {
468
} else if (merged && merged == i) {
471
for(int l = i+1; l < mac_menu->actionItems.size(); ++l) {
472
QMacMenuAction *action = mac_menu->actionItems.at(l);
473
if (action->merged) {
475
} else if (action->action->isSeparator()) {
478
} else if (!action->merged) {
485
const int index = qt_mac_menu_find_action(mr, action);
488
ChangeMenuItemAttributes(mr, index, kMenuItemAttrHidden, 0);
490
ChangeMenuItemAttributes(mr, index, 0, kMenuItemAttrHidden);
492
} else if (action->merged) {
497
emit qmenu->aboutToHide();
502
handled_event = false;
506
handled_event = false;
509
if (!handled_event) //let the event go through
510
return CallNextEventHandler(er, event);
511
return noErr; //we eat the event
513
static EventHandlerRef mac_menu_event_handler = 0;
514
static EventHandlerUPP mac_menu_eventUPP = 0;
515
static void qt_mac_cleanup_menu_event()
517
if (mac_menu_event_handler) {
518
RemoveEventHandler(mac_menu_event_handler);
519
mac_menu_event_handler = 0;
521
if (mac_menu_eventUPP) {
522
DisposeEventHandlerUPP(mac_menu_eventUPP);
523
mac_menu_eventUPP = 0;
526
static inline void qt_mac_create_menu_event_handler()
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);
538
//enabling of commands
539
static void qt_mac_command_set_enabled(MenuRef menu, UInt32 cmd, bool b)
541
if (cmd == kHICommandQuit)
542
qt_mac_quit_menu_item_enabled = b;
545
EnableMenuCommand(menu, cmd);
546
if (MenuRef dock_menu = GetApplicationDockTileMenu())
547
EnableMenuCommand(dock_menu, cmd);
549
DisableMenuCommand(menu, cmd);
550
if (MenuRef dock_menu = GetApplicationDockTileMenu())
551
DisableMenuCommand(dock_menu, cmd);
555
static bool qt_mac_auto_apple_menu(MenuCommand cmd)
557
return (cmd == kHICommandPreferences || cmd == kHICommandQuit);
560
static void qt_mac_get_accel(quint32 accel_key, quint32 *modif, quint32 *key) {
562
*modif = constructModifierMask(accel_key);
565
accel_key &= ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL);
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;
603
static inline void syncNSMenuItemVisiblity(NSMenuItem *menuItem, bool actionVisibility)
605
[menuItem setHidden:NO];
606
[menuItem setHidden:YES];
607
[menuItem setHidden:!actionVisibility];
610
static inline void syncNSMenuItemEnabled(NSMenuItem *menuItem, bool enabled)
612
[menuItem setEnabled:NO];
613
[menuItem setEnabled:YES];
614
[menuItem setEnabled:enabled];
617
static inline void syncMenuBarItemsVisiblity(const QMenuBarPrivate::QMacMenuBarPrivate *mac_menubar)
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));
626
static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader()
628
return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
631
static NSMenuItem *createNSMenuItem(const QString &title)
633
NSMenuItem *item = [[NSMenuItem alloc]
634
initWithTitle:qt_mac_QStringToNSString(title)
635
action:@selector(qtDispatcherToQAction:) keyEquivalent:@""];
636
[item setTarget:getMenuLoader()];
643
// helper that recurses into a menu structure and en/dis-ables them
644
void qt_mac_set_modal_state_helper_recursive(OSMenuRef menu, OSMenuRef merge, bool on)
646
#ifndef QT_MAC_USE_COCOA
647
for (int i = 0; i < CountMenuItems(menu); i++) {
649
GetMenuItemHierarchicalMenu(menu, i+1, &submenu);
650
if (submenu != merge) {
652
qt_mac_set_modal_state_helper_recursive(submenu, merge, on);
654
DisableMenuItem(submenu, 0);
656
EnableMenuItem(submenu, 0);
660
for (NSMenuItem *item in [menu itemArray]) {
661
OSMenuRef submenu = [item submenu];
662
if (submenu != merge) {
664
qt_mac_set_modal_state_helper_recursive(submenu, merge, on);
666
// The item should follow what the QAction has.
668
QAction *action = reinterpret_cast<QAction *>([item tag]);
669
syncNSMenuItemEnabled(item, action->isEnabled());
671
syncNSMenuItemEnabled(item, YES);
674
syncNSMenuItemEnabled(item, NO);
681
//toggling of modal state
682
static void qt_mac_set_modal_state(OSMenuRef menu, bool on)
684
#ifndef QT_MAC_USE_COCOA
686
GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
687
sizeof(merge), 0, &merge);
689
qt_mac_set_modal_state_helper_recursive(menu, merge, on);
691
UInt32 commands[] = { kHICommandQuit, kHICommandPreferences, kHICommandAbout, kHICommandAboutQt, 0 };
692
for(int c = 0; commands[c]; c++) {
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);
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;
716
if (commands[c] != kHICommandQuit)
719
enabled = action->action ? action->action->isEnabled() : 0;
722
qt_mac_command_set_enabled(menu, commands[c], enabled);
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()
731
bool qt_mac_menubar_is_open()
733
return qt_mac_menus_open_count > 0;
736
void qt_mac_clear_menubar()
738
if (QApplication::testAttribute(Qt::AA_MacPluginApplication))
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);
747
qWarning("QMenu: Internal error at %s:%d", __FILE__, __LINE__);
750
qt_mac_command_set_enabled(0, kHICommandPreferences, false);
753
QMacCocoaAutoReleasePool pool;
754
QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
755
NSMenu *menu = [loader menu];
756
[loader ensureAppMenuInMenu:menu];
757
[NSApp setMainMenu:menu];
762
QMacMenuAction::~QMacMenuAction()
764
#ifdef QT_MAC_USE_COCOA
767
QAction::MenuRole role = action->menuRole();
768
// Check if the item is owned by Qt, and should be hidden to keep it from causing
769
// problems. Do it for everything but the quit menu item since that should always
771
if (role > QAction::ApplicationSpecificRole && role < QAction::QuitRole) {
772
[menuItem setHidden:YES];
773
} else if (role == QAction::TextHeuristicRole
774
&& menuItem != [getMenuLoader() quitMenuItem]) {
775
[menuItem setHidden:YES];
778
[menuItem setTag:nil];
783
#ifndef QT_MAC_USE_COCOA
784
static MenuCommand qt_mac_menu_merge_action(MenuRef merge, QMacMenuAction *action)
786
static NSMenuItem *qt_mac_menu_merge_action(OSMenuRef merge, QMacMenuAction *action)
789
if (qt_mac_no_menubar_merge || action->action->menu() || action->action->isSeparator()
790
|| action->action->menuRole() == QAction::NoRole)
793
QString t = qt_mac_removeMnemonics(action->action->text().toLower());
794
int st = t.lastIndexOf(QLatin1Char('\t'));
796
t.remove(st, t.length()-st);
797
t.replace(QRegExp(QString::fromLatin1("\\.*$")), QLatin1String("")); //no ellipses
799
#ifndef QT_MAC_USE_COCOA
803
QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
805
switch (action->action->menuRole()) {
806
case QAction::NoRole:
809
case QAction::ApplicationSpecificRole:
810
#ifndef QT_MAC_USE_COCOA
812
QMenuMergeList *list = 0;
813
if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
814
sizeof(list), 0, &list) == noErr && list) {
815
MenuCommand lastCustom = kHICommandCustomMerge;
816
for(int i = 0; i < list->size(); ++i) {
817
QMenuMergeItem item = list->at(i);
818
if (item.command == lastCustom)
823
// The list hasn't been created, so, must be the first one.
824
ret = kHICommandCustomMerge;
828
ret = [loader appSpecificMenuItem];
831
case QAction::AboutRole:
832
#ifndef QT_MAC_USE_COCOA
833
ret = kHICommandAbout;
835
ret = [loader aboutMenuItem];
838
case QAction::AboutQtRole:
839
#ifndef QT_MAC_USE_COCOA
840
ret = kHICommandAboutQt;
842
ret = [loader aboutQtMenuItem];
845
case QAction::QuitRole:
846
#ifndef QT_MAC_USE_COCOA
847
ret = kHICommandQuit;
849
ret = [loader quitMenuItem];
852
case QAction::PreferencesRole:
853
#ifndef QT_MAC_USE_COCOA
854
ret = kHICommandPreferences;
856
ret = [loader preferencesMenuItem];
859
case QAction::TextHeuristicRole: {
860
QString aboutString = QMenuBar::tr("About").toLower();
861
if (t.startsWith(aboutString) || t.endsWith(aboutString)) {
862
if (t.indexOf(QRegExp(QString::fromLatin1("qt$"), Qt::CaseInsensitive)) == -1) {
863
#ifndef QT_MAC_USE_COCOA
864
ret = kHICommandAbout;
866
ret = [loader aboutMenuItem];
869
#ifndef QT_MAC_USE_COCOA
870
ret = kHICommandAboutQt;
872
ret = [loader aboutQtMenuItem];
875
} else if (t.startsWith(QMenuBar::tr("Config").toLower())
876
|| t.startsWith(QMenuBar::tr("Preference").toLower())
877
|| t.startsWith(QMenuBar::tr("Options").toLower())
878
|| t.startsWith(QMenuBar::tr("Setting").toLower())
879
|| t.startsWith(QMenuBar::tr("Setup").toLower())) {
880
#ifndef QT_MAC_USE_COCOA
881
ret = kHICommandPreferences;
883
ret = [loader preferencesMenuItem];
885
} else if (t.startsWith(QMenuBar::tr("Quit").toLower())
886
|| t.startsWith(QMenuBar::tr("Exit").toLower())) {
887
#ifndef QT_MAC_USE_COCOA
888
ret = kHICommandQuit;
890
ret = [loader quitMenuItem];
897
#ifndef QT_MAC_USE_COCOA
898
QMenuMergeList *list = 0;
899
if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
900
sizeof(list), 0, &list) == noErr && list) {
901
for(int i = 0; i < list->size(); ++i) {
902
QMenuMergeItem item = list->at(i);
903
if (item.command == ret && item.action)
908
QAction *cmd_action = 0;
909
if (GetMenuCommandProperty(merge, ret, kMenuCreatorQt, kMenuPropertyQAction,
910
sizeof(cmd_action), 0, &cmd_action) == noErr && cmd_action)
911
return 0; //already taken
913
if (QMenuMergeList *list = QMenuPrivate::mergeMenuItemsHash.value(merge)) {
914
for(int i = 0; i < list->size(); ++i) {
915
const QMenuMergeItem &item = list->at(i);
916
if (item.menuItem == ret && item.action)
925
static QString qt_mac_menu_merge_text(QMacMenuAction *action)
928
#ifdef QT_MAC_USE_COCOA
929
QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
931
if (action->action->menuRole() == QAction::ApplicationSpecificRole)
932
ret = action->action->text();
933
#ifndef QT_MAC_USE_COCOA
934
else if (action->command == kHICommandAbout)
935
ret = QMenuBar::tr("About %1").arg(qAppName());
936
else if (action->command == kHICommandAboutQt)
937
ret = QMenuBar::tr("About Qt");
938
else if (action->command == kHICommandPreferences)
939
ret = QMenuBar::tr("Preferences");
940
else if (action->command == kHICommandQuit)
941
ret = QMenuBar::tr("Quit %1").arg(qAppName());
943
else if (action->menuItem == [loader aboutMenuItem])
944
ret = QMenuBar::tr("About %1").arg(qAppName());
945
else if (action->menuItem == [loader aboutQtMenuItem])
946
ret = QMenuBar::tr("About Qt");
947
else if (action->menuItem == [loader preferencesMenuItem])
948
ret = QMenuBar::tr("Preferences");
949
else if (action->menuItem == [loader quitMenuItem])
950
ret = QMenuBar::tr("Quit %1").arg(qAppName());
955
static QKeySequence qt_mac_menu_merge_accel(QMacMenuAction *action)
958
#ifdef QT_MAC_USE_COCOA
959
QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
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);
969
else if (action->menuItem == [loader preferencesMenuItem])
970
ret = QKeySequence(QKeySequence::Preferences);
971
else if (action->menuItem == [loader quitMenuItem])
972
ret = QKeySequence(QKeySequence::Quit);
977
void Q_GUI_EXPORT qt_mac_set_menubar_icons(bool b)
978
{ QApplication::instance()->setAttribute(Qt::AA_DontShowIconsInMenus, !b); }
979
void Q_GUI_EXPORT qt_mac_set_native_menubar(bool b)
980
{ QApplication::instance()->setAttribute(Qt::AA_DontUseNativeMenuBar, !b); }
981
void Q_GUI_EXPORT qt_mac_set_menubar_merge(bool b) { qt_mac_no_menubar_merge = !b; }
983
/*****************************************************************************
985
*****************************************************************************/
986
QMenuPrivate::QMacMenuPrivate::QMacMenuPrivate() : menu(0)
990
QMenuPrivate::QMacMenuPrivate::~QMacMenuPrivate()
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)
1011
EventHandlerHash::iterator it = menu_eventHandlers_hash()->find(menu);
1012
while (it != menu_eventHandlers_hash()->end() && it.key() == menu) {
1013
RemoveEventHandler(it.value());
1016
menu_eventHandlers_hash()->remove(menu);
1020
QMacCocoaAutoReleasePool pool;
1021
while (actionItems.size()) {
1022
QMacMenuAction *action = actionItems.takeFirst();
1023
if (QMenuMergeList *list = mergeMenuItemsHash.value(action->menu)) {
1025
while (i < list->size()) {
1026
const QMenuMergeItem &item = list->at(i);
1027
if (item.action == action)
1035
mergeMenuHash.remove(menu);
1036
mergeMenuItemsHash.remove(menu);
1042
QMenuPrivate::QMacMenuPrivate::addAction(QAction *a, QMacMenuAction *before, QMenuPrivate *qmenu)
1044
QMacMenuAction *action = new QMacMenuAction;
1046
action->ignore_accel = 0;
1049
#ifndef QT_MAC_USE_COCOA
1050
action->command = qt_mac_menu_static_cmd_id++;
1052
addAction(action, before, qmenu);
1056
QMenuPrivate::QMacMenuPrivate::addAction(QMacMenuAction *action, QMacMenuAction *before, QMenuPrivate *qmenu)
1058
#ifdef QT_MAC_USE_COCOA
1059
QMacCocoaAutoReleasePool pool;
1064
int before_index = actionItems.indexOf(before);
1065
if (before_index < 0) {
1067
before_index = actionItems.size();
1069
actionItems.insert(before_index, action);
1071
#ifndef QT_MAC_USE_COCOA
1072
int index = qt_mac_menu_find_action(menu, action);
1075
[action->menu release];
1077
action->menu = menu;
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
1084
GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
1085
sizeof(merge), 0, &merge);
1087
OSMenuRef merge = QMenuPrivate::mergeMenuHash.value(menu);
1090
#ifndef QT_MAC_USE_COCOA
1091
if (MenuCommand cmd = qt_mac_menu_merge_action(merge, action)) {
1093
action->menu = merge;
1094
action->command = cmd;
1095
if (qt_mac_auto_apple_menu(cmd))
1096
index = 0; //no need
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);
1105
list->append(QMenuMergeItem(cmd, action));
1108
if (NSMenuItem *cmd = qt_mac_menu_merge_action(merge, action)) {
1111
[action->menu release];
1112
action->menu = merge;
1114
[cmd setAction:@selector(qtDispatcherToQAction:)];
1115
[cmd setTarget:getMenuLoader()];
1116
[action->menuItem release];
1117
action->menuItem = cmd;
1118
QMenuMergeList *list = QMenuPrivate::mergeMenuItemsHash.value(merge);
1120
list = new QMenuMergeList;
1121
QMenuPrivate::mergeMenuItemsHash.insert(merge, list);
1123
list->append(QMenuMergeItem(cmd, action));
1129
#ifdef QT_MAC_USE_COCOA
1130
NSMenuItem *newItem = action->menuItem;
1133
#ifndef QT_MAC_USE_COCOA
1139
#ifndef QT_MAC_USE_COCOA
1140
index = before_index;
1141
MenuItemAttributes attr = kMenuItemAttrAutoRepeat;
1143
newItem = createNSMenuItem(action->action->text());
1144
action->menuItem = newItem;
1147
#ifndef QT_MAC_USE_COCOA
1148
InsertMenuItemTextWithCFString(action->menu, 0, qMax(before_index, 0), attr, action->command);
1150
[menu insertItem:newItem atIndex:qMax(before_index, 0)];
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.
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);
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);
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);
1183
MenuItemIndex tmpIndex;
1184
AppendMenuItemTextWithCFString(action->menu, 0, attr, action->command, &tmpIndex);
1186
if (mergedItems.contains(action->command))
1187
AppendMenuItemTextWithCFString(action->menu, 0, kMenuItemAttrSeparator, 0, &tmpIndex);
1190
[menu addItem:newItem];
1194
QWidget *widget = qmenu ? qmenu->widgetItems.value(qmenu->actions.indexOf(action->action)) : 0;
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);
1202
HIMenuGetContentView(action->menu, kThemeMenuTypePullDown, &content);
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);
1210
QWidget *menuWidget = 0;
1211
GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyWidgetMenu,
1212
sizeof(menuWidget), 0, &menuWidget);
1214
menuWidget = new QMacNativeWidget(content);
1215
SetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyWidgetMenu,
1216
sizeof(menuWidget), &menuWidget);
1219
widget->setParent(menuWidget);
1221
QMacNativeWidget *container = new QMacNativeWidget(0);
1222
container->resize(widget->sizeHint());
1223
widget->setAttribute(Qt::WA_LayoutUsesWidgetRect);
1224
widget->setParent(container);
1226
NSView *containerView = qt_mac_nativeview_for(container);
1227
[containerView setAutoresizesSubviews:YES];
1228
[containerView setAutoresizingMask:NSViewWidthSizable];
1229
[qt_mac_nativeview_for(widget) setAutoresizingMask:NSViewWidthSizable];
1231
[newItem setView:containerView];
1238
#ifndef QT_MAC_USE_COCOA
1239
qt_mac_command_set_enabled(action->menu, action->command, !QApplicationPrivate::modalState());
1241
[newItem setEnabled:!QApplicationPrivate::modalState()];
1244
#ifndef QT_MAC_USE_COCOA
1245
SetMenuCommandProperty(action->menu, action->command, kMenuCreatorQt, kMenuPropertyQAction,
1246
sizeof(action), &action);
1248
[newItem setTag:long(static_cast<QAction *>(action->action))];
1253
// return an autoreleased string given a QKeySequence (currently only looks at the first one).
1254
NSString *keySequenceToKeyEqivalent(const QKeySequence &accel)
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;
1263
keyEquiv = unichar(QChar(accel_key).toLower().unicode());
1265
return [NSString stringWithCharacters:&keyEquiv.unicode() length:1];
1268
// return the cocoa modifier mask for the QKeySequence (currently only looks at the first one).
1269
NSUInteger keySequenceModifierMask(const QKeySequence &accel)
1271
return constructModifierMask(accel[0]);
1275
QMenuPrivate::QMacMenuPrivate::syncAction(QMacMenuAction *action)
1280
#ifndef QT_MAC_USE_COCOA
1281
const int index = qt_mac_menu_find_action(action->menu, action);
1285
NSMenuItem *item = action->menuItem;
1290
#ifndef QT_MAC_USE_COCOA
1291
if (!action->action->isVisible()) {
1292
ChangeMenuItemAttributes(action->menu, index, kMenuItemAttrHidden, 0);
1295
ChangeMenuItemAttributes(action->menu, index, 0, kMenuItemAttrHidden);
1297
QMacCocoaAutoReleasePool pool;
1298
NSMenu *menu = [item menu];
1299
bool actionVisible = action->action->isVisible();
1300
[item setHidden:!actionVisible];
1305
#ifndef QT_MAC_USE_COCOA
1306
if (action->action->isSeparator()) {
1307
ChangeMenuItemAttributes(action->menu, index, kMenuItemAttrSeparator, 0);
1310
ChangeMenuItemAttributes(action->menu, index, 0, kMenuItemAttrSeparator);
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];
1320
item = action->menuItem;
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];
1328
item = action->menuItem;
1332
//find text (and accel)
1333
action->ignore_accel = 0;
1334
QString text = action->action->text();
1335
QKeySequence accel = action->action->shortcut();
1337
int st = text.lastIndexOf(QLatin1Char('\t'));
1339
action->ignore_accel = 1;
1340
accel = QKeySequence(text.right(text.length()-(st+1)));
1341
text.remove(st, text.length()-st);
1345
QString cmd_text = qt_mac_menu_merge_text(action);
1346
if (!cmd_text.isEmpty()) {
1348
accel = qt_mac_menu_merge_accel(action);
1351
// Show multiple key sequences as part of the menu text.
1352
if (accel.count() > 1)
1353
text += QLatin1String(" (") + accel.toString(QKeySequence::NativeText) + QLatin1String(")");
1355
QString finalString = qt_mac_removeMnemonics(text);
1357
#ifndef QT_MAC_USE_COCOA
1358
MenuItemDataRec data;
1359
memset(&data, '\0', sizeof(data));
1362
data.whichData |= kMenuItemDataCFString;
1363
QCFString cfstring(finalString); // Hold the reference to the end of the function.
1364
data.cfText = cfstring;
1367
data.whichData |= kMenuItemDataEnabled;
1368
data.enabled = action->action->isEnabled();
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));
1376
data.iconType = kMenuNoIcon;
1378
if (action->action->font().resolve()) { // Carbon font
1379
if (action->action->font().bold())
1381
if (action->action->font().underline())
1382
data.style |= underline;
1383
if (action->action->font().italic())
1384
data.style |= italic;
1386
data.whichData |= kMenuItemDataStyle;
1387
data.whichData |= kMenuItemDataFontID;
1388
data.fontID = action->action->font().macFontID();
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];
1403
[item setTitle: qt_mac_QStringToNSString(finalString)];
1405
[item setTitle:qt_mac_QStringToNSString(qt_mac_removeMnemonics(text))];
1408
[item setEnabled: action->action->isEnabled()];
1411
NSImage *nsimage = 0;
1412
if (!action->action->icon().isNull() && action->action->isIconVisibleInMenu()) {
1413
nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(action->action->icon().pixmap(16, QIcon::Normal)));
1415
[item setImage:nsimage];
1419
if (action->action->menu()) { //submenu
1420
#ifndef QT_MAC_USE_COCOA
1421
data.whichData |= kMenuItemDataSubmenuHandle;
1422
data.submenuHandle = action->action->menu()->macMenu();
1423
QWidget *caused = 0;
1424
GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(caused), 0, &caused);
1425
SetMenuItemProperty(data.submenuHandle, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), &caused);
1427
NSMenu *subMenu = static_cast<NSMenu *>(action->action->menu()->macMenu());
1428
if ([subMenu supermenu] != nil) {
1429
// The menu is already a sub-menu of another one. Cocoa will throw an exception,
1430
// in such cases. For the time being, a new QMenu with same set of actions is the
1432
action->action->setEnabled(false);
1434
[item setSubmenu:subMenu];
1437
} else { //respect some other items
1438
#ifndef QT_MAC_USE_COCOA
1439
//shortcuts (say we are setting them all so that we can also clear them).
1440
data.whichData |= kMenuItemDataCmdKey;
1441
data.whichData |= kMenuItemDataCmdKeyModifiers;
1442
data.whichData |= kMenuItemDataCmdKeyGlyph;
1443
if (accel.count() == 1) {
1444
qt_mac_get_accel(accel[0], (quint32*)&data.cmdKeyModifiers, (quint32*)&data.cmdKeyGlyph);
1445
if (data.cmdKeyGlyph == 0)
1446
data.cmdKey = (UniChar)accel[0];
1449
[item setSubmenu:0];
1450
// No key equivalent set for multiple key QKeySequence.
1451
if (accel.count() == 1) {
1452
[item setKeyEquivalent:keySequenceToKeyEqivalent(accel)];
1453
[item setKeyEquivalentModifierMask:keySequenceModifierMask(accel)];
1455
[item setKeyEquivalent:@""];
1456
[item setKeyEquivalentModifierMask:NSCommandKeyMask];
1460
#ifndef QT_MAC_USE_COCOA
1462
data.whichData |= kMenuItemDataMark;
1463
if (action->action->isChecked()) {
1465
if (action->action->actionGroup() &&
1466
action->action->actionGroup()->isExclusive())
1467
data.mark = diamondMark;
1470
data.mark = checkMark;
1476
SetMenuItemData(action->menu, action->command, true, &data);
1479
if (data.iconHandle)
1480
ReleaseIconRef(IconRef(data.iconHandle));
1483
[item setState:action->action->isChecked() ? NSOnState : NSOffState];
1488
QMenuPrivate::QMacMenuPrivate::removeAction(QMacMenuAction *action)
1492
#ifndef QT_MAC_USE_COCOA
1493
if (action->command == kHICommandQuit || action->command == kHICommandPreferences)
1494
qt_mac_command_set_enabled(action->menu, action->command, false);
1496
DeleteMenuItem(action->menu, qt_mac_menu_find_action(action->menu, action));
1498
QMacCocoaAutoReleasePool pool;
1499
if (action->merged) {
1500
if (reinterpret_cast<QAction *>([action->menuItem tag]) == action->action) {
1501
QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
1502
[action->menuItem setEnabled:false];
1503
if (action->menuItem != [loader quitMenuItem]
1504
&& action->menuItem != [loader preferencesMenuItem]) {
1505
[[action->menuItem menu] removeItem:action->menuItem];
1509
[[action->menuItem menu] removeItem:action->menuItem];
1512
actionItems.removeAll(action);
1516
QMenuPrivate::macMenu(OSMenuRef merge)
1520
if (mac_menu && mac_menu->menu)
1521
return mac_menu->menu;
1523
mac_menu = new QMacMenuPrivate;
1524
mac_menu->menu = qt_mac_create_menu(q);
1526
#ifndef QT_MAC_USE_COCOA
1527
SetMenuItemProperty(mac_menu->menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu, sizeof(merge), &merge);
1529
mergeMenuHash.insert(mac_menu->menu, merge);
1532
QList<QAction*> items = q->actions();
1533
for(int i = 0; i < items.count(); i++)
1534
mac_menu->addAction(items[i], 0, this);
1535
syncSeparatorsCollapsible(collapsibleSeparators);
1536
return mac_menu->menu;
1543
QMenuPrivate::syncSeparatorsCollapsible(bool collapse)
1545
#ifndef QT_MAC_USE_COCOA
1547
ChangeMenuAttributes(mac_menu->menu, kMenuAttrCondenseSeparators, 0);
1549
ChangeMenuAttributes(mac_menu->menu, 0, kMenuAttrCondenseSeparators);
1551
qt_mac_menu_collapseSeparators(mac_menu->menu, collapse);
1560
void QMenuPrivate::setMacMenuEnabled(bool enable)
1565
QMacCocoaAutoReleasePool pool;
1567
for (int i = 0; i < mac_menu->actionItems.count(); ++i) {
1568
QMacMenuAction *menuItem = mac_menu->actionItems.at(i);
1569
if (menuItem && menuItem->action && menuItem->action->isEnabled()) {
1570
#ifndef QT_MAC_USE_COCOA
1571
// Only enable those items which contains an enabled QAction.
1572
// i == 0 -> the menu itself, hence i + 1 for items.
1573
EnableMenuItem(mac_menu->menu, i + 1);
1575
[menuItem->menuItem setEnabled:true];
1580
#ifndef QT_MAC_USE_COCOA
1581
DisableAllMenuItems(mac_menu->menu);
1583
NSMenu *menu = mac_menu->menu;
1584
for (NSMenuItem *item in [menu itemArray]) {
1585
[item setEnabled:false];
1594
This function will return the OSMenuRef used to create the native menu bar
1597
If Qt is built against Carbon, the OSMenuRef is a MenuRef that can be used
1598
with Carbon's Menu Manager API.
1600
If Qt is built against Cocoa, the OSMenuRef is a NSMenu pointer.
1602
\warning This function is not portable.
1604
\sa QMenuBar::macMenu()
1606
OSMenuRef QMenu::macMenu(OSMenuRef merge) { return d_func()->macMenu(merge); }
1608
/*****************************************************************************
1610
*****************************************************************************/
1611
typedef QHash<QWidget *, QMenuBar *> MenuBarHash;
1612
Q_GLOBAL_STATIC(MenuBarHash, menubars)
1613
static QMenuBar *fallback = 0;
1614
QMenuBarPrivate::QMacMenuBarPrivate::QMacMenuBarPrivate() : menu(0), apple_menu(0)
1618
QMenuBarPrivate::QMacMenuBarPrivate::~QMacMenuBarPrivate()
1620
for(QList<QMacMenuAction*>::Iterator it = actionItems.begin(); it != actionItems.end(); ++it)
1622
#ifndef QT_MAC_USE_COCOA
1624
QMenuMergeList *list = 0;
1625
GetMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
1626
sizeof(list), 0, &list);
1628
RemoveMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList);
1631
ReleaseMenu(apple_menu);
1636
[apple_menu release];
1642
QMenuBarPrivate::QMacMenuBarPrivate::addAction(QAction *a, QMacMenuAction *before)
1644
if (a->isSeparator() || !menu)
1646
QMacMenuAction *action = new QMacMenuAction;
1648
action->ignore_accel = 1;
1649
#ifndef QT_MAC_USE_COCOA
1650
action->command = qt_mac_menu_static_cmd_id++;
1652
addAction(action, before);
1656
QMenuBarPrivate::QMacMenuBarPrivate::addAction(QMacMenuAction *action, QMacMenuAction *before)
1658
if (!action || !menu)
1661
int before_index = actionItems.indexOf(before);
1662
if (before_index < 0) {
1664
before_index = actionItems.size();
1666
actionItems.insert(before_index, action);
1668
MenuItemIndex index = actionItems.size()-1;
1670
action->menu = menu;
1671
#ifdef QT_MAC_USE_COCOA
1672
QMacCocoaAutoReleasePool pool;
1673
[action->menu retain];
1674
NSMenuItem *newItem = createNSMenuItem(action->action->text());
1675
action->menuItem = newItem;
1678
#ifndef QT_MAC_USE_COCOA
1679
InsertMenuItemTextWithCFString(action->menu, 0, qMax(1, before_index+1), 0, action->command);
1681
[menu insertItem:newItem atIndex:qMax(1, before_index + 1)];
1683
index = before_index;
1685
#ifndef QT_MAC_USE_COCOA
1686
AppendMenuItemTextWithCFString(action->menu, 0, 0, action->command, &index);
1688
[menu addItem:newItem];
1691
#ifndef QT_MAC_USE_COCOA
1692
SetMenuItemProperty(action->menu, index, kMenuCreatorQt, kMenuPropertyQAction, sizeof(action),
1695
[newItem setTag:long(static_cast<QAction *>(action->action))];
1701
QMenuBarPrivate::QMacMenuBarPrivate::syncAction(QMacMenuAction *action)
1703
if (!action || !menu)
1705
#ifndef QT_MAC_USE_COCOA
1706
const int index = qt_mac_menu_find_action(action->menu, action);
1708
QMacCocoaAutoReleasePool pool;
1709
NSMenuItem *item = action->menuItem;
1712
OSMenuRef submenu = 0;
1713
bool release_submenu = false;
1714
if (action->action->menu()) {
1715
if ((submenu = action->action->menu()->macMenu(apple_menu))) {
1716
#ifndef QT_MAC_USE_COCOA
1717
QWidget *caused = 0;
1718
GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(caused), 0, &caused);
1719
SetMenuItemProperty(submenu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), &caused);
1721
if ([submenu supermenu] != nil)
1724
[item setSubmenu:submenu];
1727
#ifndef QT_MAC_USE_COCOA
1728
} else { // create a submenu to act as menu
1729
release_submenu = true;
1730
CreateNewMenu(0, 0, &submenu);
1735
bool visible = actualMenuItemVisibility(this, action);
1736
#ifndef QT_MAC_USE_COCOA
1737
SetMenuItemHierarchicalMenu(action->menu, index, submenu);
1738
SetMenuTitleWithCFString(submenu, QCFString(qt_mac_removeMnemonics(action->action->text())));
1740
ChangeMenuAttributes(submenu, 0, kMenuAttrHidden);
1742
ChangeMenuAttributes(submenu, kMenuAttrHidden, 0);
1744
[item setSubmenu: submenu];
1745
[submenu setTitle:qt_mac_QStringToNSString(qt_mac_removeMnemonics(action->action->text()))];
1746
syncNSMenuItemVisiblity(item, visible);
1748
if (release_submenu) { //no pointers to it
1749
#ifndef QT_MAC_USE_COCOA
1750
ReleaseMenu(submenu);
1756
qWarning("QMenu: No OSMenuRef created for popup menu");
1761
QMenuBarPrivate::QMacMenuBarPrivate::removeAction(QMacMenuAction *action)
1763
if (!action || !menu)
1765
#ifndef QT_MAC_USE_COCOA
1766
DeleteMenuItem(action->menu, qt_mac_menu_find_action(action->menu, action));
1768
QMacCocoaAutoReleasePool pool;
1769
[action->menu removeItem:action->menuItem];
1771
actionItems.removeAll(action);
1775
QMenuBarPrivate::macCreateMenuBar(QWidget *parent)
1778
static int dontUseNativeMenuBar = -1;
1779
// We call the isNativeMenuBar function here
1780
// because that will make sure that local overrides
1781
// are dealt with correctly.
1782
bool qt_mac_no_native_menubar = !q->isNativeMenuBar();
1783
if (qt_mac_no_native_menubar == false && dontUseNativeMenuBar < 0) {
1784
bool isPlugin = QApplication::testAttribute(Qt::AA_MacPluginApplication);
1785
bool environmentSaysNo = !qgetenv("QT_MAC_NO_NATIVE_MENUBAR").isEmpty();
1786
dontUseNativeMenuBar = isPlugin || environmentSaysNo;
1787
QApplication::instance()->setAttribute(Qt::AA_DontUseNativeMenuBar, dontUseNativeMenuBar);
1788
qt_mac_no_native_menubar = !q->isNativeMenuBar();
1790
if (!qt_mac_no_native_menubar) {
1791
extern void qt_event_request_menubarupdate(); //qapplication_mac.cpp
1792
qt_event_request_menubarupdate();
1793
if (!parent && !fallback) {
1795
mac_menubar = new QMacMenuBarPrivate;
1796
} else if (parent && parent->isWindow()) {
1797
menubars()->insert(q->window(), q);
1798
mac_menubar = new QMacMenuBarPrivate;
1803
void QMenuBarPrivate::macDestroyMenuBar()
1806
QMacCocoaAutoReleasePool pool;
1810
QWidget *tlw = q->window();
1811
menubars()->remove(tlw);
1814
if (qt_mac_current_menubar.qmenubar == q) {
1815
extern void qt_event_request_menubarupdate(); //qapplication_mac.cpp
1816
qt_event_request_menubarupdate();
1820
OSMenuRef QMenuBarPrivate::macMenu()
1823
if (!q->isNativeMenuBar() || !mac_menubar) {
1825
} else if (!mac_menubar->menu) {
1826
mac_menubar->menu = qt_mac_create_menu(q);
1827
ProcessSerialNumber mine, front;
1828
if (GetCurrentProcess(&mine) == noErr && GetFrontProcess(&front) == noErr) {
1829
if (!qt_mac_no_menubar_merge && !mac_menubar->apple_menu) {
1830
mac_menubar->apple_menu = qt_mac_create_menu(q);
1831
#ifndef QT_MAC_USE_COCOA
1832
MenuItemIndex index;
1833
AppendMenuItemTextWithCFString(mac_menubar->menu, 0, 0, 0, &index);
1835
SetMenuTitleWithCFString(mac_menubar->apple_menu, QCFString(QString(QChar(0x14))));
1836
SetMenuItemHierarchicalMenu(mac_menubar->menu, index, mac_menubar->apple_menu);
1837
SetMenuItemProperty(mac_menubar->apple_menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(q), &q);
1839
[mac_menubar->apple_menu setTitle:qt_mac_QStringToNSString(QString(QChar(0x14)))];
1840
NSMenuItem *apple_menuItem = [[NSMenuItem alloc] init];
1841
[apple_menuItem setSubmenu:mac_menubar->menu];
1842
[mac_menubar->apple_menu addItem:apple_menuItem];
1843
[apple_menuItem release];
1846
if (mac_menubar->apple_menu) {
1847
#ifndef QT_MAC_USE_COCOA
1848
SetMenuItemProperty(mac_menubar->menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
1849
sizeof(mac_menubar->apple_menu), &mac_menubar->apple_menu);
1851
QMenuPrivate::mergeMenuHash.insert(mac_menubar->menu, mac_menubar->apple_menu);
1854
QList<QAction*> items = q->actions();
1855
for(int i = 0; i < items.count(); i++)
1856
mac_menubar->addAction(items[i]);
1859
return mac_menubar->menu;
1865
This function will return the OSMenuRef used to create the native menu bar
1866
bindings. This OSMenuRef is then set as the root menu for the Menu
1869
\warning This function is not portable.
1871
\sa QMenu::macMenu()
1873
OSMenuRef QMenuBar::macMenu() { return d_func()->macMenu(); }
1877
Ancestor function that crosses windows (QWidget::isAncestorOf
1878
only considers widgets within the same window).
1880
static bool qt_mac_is_ancestor(QWidget* possibleAncestor, QWidget *child)
1882
if (!possibleAncestor)
1885
QWidget * current = child->parentWidget();
1886
while (current != 0) {
1887
if (current == possibleAncestor)
1889
current = current->parentWidget();
1896
Returns true if the entries of menuBar should be disabled,
1897
based on the modality type of modalWidget.
1899
static bool qt_mac_should_disable_menu(QMenuBar *menuBar, QWidget *modalWidget)
1901
if (modalWidget == 0 || menuBar == 0)
1904
// If there is an application modal window on
1905
// screen, the entries of the menubar should be disabled:
1906
QWidget *w = modalWidget;
1908
if (w->isVisible() && w->windowModality() == Qt::ApplicationModal)
1910
w = w->parentWidget();
1913
// INVARIANT: modalWidget is window modal. Disable menu entries
1914
// if the menu bar belongs to an ancestor of modalWidget:
1915
return qt_mac_is_ancestor(menuBar->parentWidget(), modalWidget);
1918
static void cancelAllMenuTracking()
1920
#ifdef QT_MAC_USE_COCOA
1921
QMacCocoaAutoReleasePool pool;
1922
NSMenu *mainMenu = [NSApp mainMenu];
1923
[mainMenu cancelTracking];
1924
for (NSMenuItem *item in [mainMenu itemArray]) {
1925
if ([item submenu]) {
1926
[[item submenu] cancelTracking];
1935
This function will update the current menu bar and set it as the
1936
active menu bar in the Menu Manager.
1938
\warning This function is not portable.
1940
\sa QMenu::macMenu(), QMenuBar::macMenu()
1942
bool QMenuBar::macUpdateMenuBar()
1944
cancelAllMenuTracking();
1947
QWidget *w = qApp->activeWindow();
1950
QWidgetList tlws = QApplication::topLevelWidgets();
1951
for(int i = 0; i < tlws.size(); ++i) {
1952
QWidget *tlw = tlws.at(i);
1953
if ((tlw->isVisible() && tlw->windowType() != Qt::Tool &&
1954
tlw->windowType() != Qt::Popup)) {
1961
mb = menubars()->value(w);
1962
#ifndef QT_NO_MAINWINDOW
1963
QDockWidget *dw = qobject_cast<QDockWidget *>(w);
1965
QMainWindow *mw = qobject_cast<QMainWindow *>(dw->parentWidget());
1966
if (mw && (mb = menubars()->value(mw)))
1971
mb = menubars()->value((w = w->parentWidget()));
1977
if (mb && mb->isNativeMenuBar()) {
1978
#ifdef QT_MAC_USE_COCOA
1979
QMacCocoaAutoReleasePool pool;
1981
if (OSMenuRef menu = mb->macMenu()) {
1982
#ifndef QT_MAC_USE_COCOA
1985
QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
1986
[loader ensureAppMenuInMenu:menu];
1987
[NSApp setMainMenu:menu];
1988
syncMenuBarItemsVisiblity(mb->d_func()->mac_menubar);
1990
if (OSMenuRef tmpMerge = QMenuPrivate::mergeMenuHash.value(menu)) {
1991
if (QMenuMergeList *mergeList
1992
= QMenuPrivate::mergeMenuItemsHash.value(tmpMerge)) {
1993
const int mergeListSize = mergeList->size();
1995
for (int i = 0; i < mergeListSize; ++i) {
1996
const QMenuMergeItem &mergeItem = mergeList->at(i);
1997
// Ideally we would call QMenuPrivate::syncAction, but that requires finding
1998
// the original QMen and likely doing more work than we need.
1999
// For example, enabled is handled below.
2000
[mergeItem.menuItem setTag:reinterpret_cast<long>(
2001
static_cast<QAction *>(mergeItem.action->action))];
2002
[mergeItem.menuItem setHidden:!(mergeItem.action->action->isVisible())];
2007
QWidget *modalWidget = qApp->activeModalWidget();
2008
if (mb != menubars()->value(modalWidget)) {
2009
qt_mac_set_modal_state(menu, qt_mac_should_disable_menu(mb, modalWidget));
2012
qt_mac_current_menubar.qmenubar = mb;
2013
qt_mac_current_menubar.modal = QApplicationPrivate::modalState();
2015
} else if (qt_mac_current_menubar.qmenubar && qt_mac_current_menubar.qmenubar->isNativeMenuBar()) {
2016
const bool modal = QApplicationPrivate::modalState();
2017
if (modal != qt_mac_current_menubar.modal) {
2019
if (OSMenuRef menu = qt_mac_current_menubar.qmenubar->macMenu()) {
2020
#ifndef QT_MAC_USE_COCOA
2023
QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
2024
[loader ensureAppMenuInMenu:menu];
2025
[NSApp setMainMenu:menu];
2026
syncMenuBarItemsVisiblity(qt_mac_current_menubar.qmenubar->d_func()->mac_menubar);
2028
QWidget *modalWidget = qApp->activeModalWidget();
2029
if (qt_mac_current_menubar.qmenubar != menubars()->value(modalWidget)) {
2030
qt_mac_set_modal_state(menu, qt_mac_should_disable_menu(mb, modalWidget));
2033
qt_mac_current_menubar.modal = modal;
2037
qt_mac_clear_menubar();
2041
QHash<OSMenuRef, OSMenuRef> QMenuPrivate::mergeMenuHash;
2042
QHash<OSMenuRef, QMenuMergeList*> QMenuPrivate::mergeMenuItemsHash;
2044
bool QMenuPrivate::QMacMenuPrivate::merged(const QAction *action) const
2046
#ifndef QT_MAC_USE_COCOA
2048
GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
2049
sizeof(merge), 0, &merge);
2051
QMenuMergeList *list = 0;
2052
if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
2053
sizeof(list), 0, &list) == noErr && list) {
2054
for(int i = 0; i < list->size(); ++i) {
2055
QMenuMergeItem item = list->at(i);
2056
if (item.action->action == action)
2062
if (OSMenuRef merge = mergeMenuHash.value(menu)) {
2063
if (QMenuMergeList *list = mergeMenuItemsHash.value(merge)) {
2064
for(int i = 0; i < list->size(); ++i) {
2065
const QMenuMergeItem &item = list->at(i);
2066
if (item.action->action == action)
2075
//creation of the OSMenuRef
2076
static OSMenuRef qt_mac_create_menu(QWidget *w)
2079
#ifndef QT_MAC_USE_COCOA
2081
if (CreateNewMenu(0, 0, &ret) == noErr) {
2082
qt_mac_create_menu_event_handler();
2083
SetMenuItemProperty(ret, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(w), &w);
2085
// kEventMenuMatchKey is only sent to the menu itself and not to
2086
// the application, install a separate handler for that event.
2087
EventHandlerRef eventHandlerRef;
2088
InstallMenuEventHandler(ret, qt_mac_menu_event,
2089
GetEventTypeCount(menu_menu_events),
2090
menu_menu_events, 0, &eventHandlerRef);
2091
menu_eventHandlers_hash()->insert(ret, eventHandlerRef);
2093
qWarning("QMenu: Internal error");
2096
if (QMenu *qmenu = qobject_cast<QMenu *>(w)){
2097
ret = [[QT_MANGLE_NAMESPACE(QCocoaMenu) alloc] initWithQMenu:qmenu];
2099
ret = [[NSMenu alloc] init];