| /**************************************************************************** |
| ** |
| ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). |
| ** All rights reserved. |
| ** Contact: Nokia Corporation (qt-info@nokia.com) |
| ** |
| ** This file is part of the QtGui module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** GNU Lesser General Public License Usage |
| ** This file may be used under the terms of the GNU Lesser General Public |
| ** License version 2.1 as published by the Free Software Foundation and |
| ** appearing in the file LICENSE.LGPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU Lesser |
| ** General Public License version 2.1 requirements will be met: |
| ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
| ** |
| ** In addition, as a special exception, Nokia gives you certain additional |
| ** rights. These rights are described in the Nokia Qt LGPL Exception |
| ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU General |
| ** Public License version 3.0 as published by the Free Software Foundation |
| ** and appearing in the file LICENSE.GPL included in the packaging of this |
| ** file. Please review the following information to ensure the GNU General |
| ** Public License version 3.0 requirements will be met: |
| ** http://www.gnu.org/copyleft/gpl.html. |
| ** |
| ** Other Usage |
| ** Alternatively, this file may be used in accordance with the terms and |
| ** conditions contained in a signed written agreement between you and Nokia. |
| ** |
| ** |
| ** |
| ** |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qmenu.h" |
| #include "qhash.h" |
| #include <qdebug.h> |
| #include "qapplication.h" |
| #include <private/qt_mac_p.h> |
| #include "qregexp.h" |
| #include "qmainwindow.h" |
| #include "qdockwidget.h" |
| #include "qtoolbar.h" |
| #include "qevent.h" |
| #include "qstyle.h" |
| #include "qwidgetaction.h" |
| #include "qmacnativewidget_mac.h" |
| |
| #include <private/qapplication_p.h> |
| #include <private/qcocoaapplication_mac_p.h> |
| #include <private/qmenu_p.h> |
| #include <private/qmenubar_p.h> |
| #include <private/qcocoamenuloader_mac_p.h> |
| #include <private/qcocoamenu_mac_p.h> |
| #include <private/qt_cocoa_helpers_mac_p.h> |
| #include <Cocoa/Cocoa.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| /***************************************************************************** |
| QMenu debug facilities |
| *****************************************************************************/ |
| |
| /***************************************************************************** |
| QMenu globals |
| *****************************************************************************/ |
| bool qt_mac_no_menubar_merge = false; |
| bool qt_mac_quit_menu_item_enabled = true; |
| int qt_mac_menus_open_count = 0; |
| |
| static OSMenuRef qt_mac_create_menu(QWidget *w); |
| |
| #ifndef QT_MAC_USE_COCOA |
| static uint qt_mac_menu_static_cmd_id = 'QT00'; |
| const UInt32 kMenuCreatorQt = 'cute'; |
| enum { |
| kMenuPropertyQAction = 'QAcT', |
| kMenuPropertyQWidget = 'QWId', |
| kMenuPropertyCausedQWidget = 'QCAU', |
| kMenuPropertyMergeMenu = 'QApP', |
| kMenuPropertyMergeList = 'QAmL', |
| kMenuPropertyWidgetActionWidget = 'QWid', |
| kMenuPropertyWidgetMenu = 'QWMe', |
| |
| kHICommandAboutQt = 'AOQT', |
| kHICommandCustomMerge = 'AQt0' |
| }; |
| #endif |
| |
| static struct { |
| QPointer<QMenuBar> qmenubar; |
| bool modal; |
| } qt_mac_current_menubar = { 0, false }; |
| |
| |
| |
| |
| /***************************************************************************** |
| Externals |
| *****************************************************************************/ |
| extern OSViewRef qt_mac_hiview_for(const QWidget *w); //qwidget_mac.cpp |
| extern HIViewRef qt_mac_hiview_for(OSWindowRef w); //qwidget_mac.cpp |
| extern IconRef qt_mac_create_iconref(const QPixmap &px); //qpixmap_mac.cpp |
| extern QWidget * mac_keyboard_grabber; //qwidget_mac.cpp |
| extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); //qapplication_xxx.cpp |
| RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp |
| void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp |
| |
| /***************************************************************************** |
| QMenu utility functions |
| *****************************************************************************/ |
| bool qt_mac_watchingAboutToShow(QMenu *menu) |
| { |
| return menu && menu->receivers(SIGNAL(aboutToShow())); |
| } |
| |
| static int qt_mac_CountMenuItems(OSMenuRef menu) |
| { |
| if (menu) { |
| #ifndef QT_MAC_USE_COCOA |
| int ret = 0; |
| const int items = CountMenuItems(menu); |
| for(int i = 0; i < items; i++) { |
| MenuItemAttributes attr; |
| if (GetMenuItemAttributes(menu, i+1, &attr) == noErr && |
| attr & kMenuItemAttrHidden) |
| continue; |
| ++ret; |
| } |
| return ret; |
| #else |
| return [menu numberOfItems]; |
| #endif |
| } |
| return 0; |
| } |
| |
| static quint32 constructModifierMask(quint32 accel_key) |
| { |
| quint32 ret = 0; |
| const bool dontSwap = qApp->testAttribute(Qt::AA_MacDontSwapCtrlAndMeta); |
| #ifndef QT_MAC_USE_COCOA |
| if ((accel_key & Qt::ALT) == Qt::ALT) |
| ret |= kMenuOptionModifier; |
| if ((accel_key & Qt::SHIFT) == Qt::SHIFT) |
| ret |= kMenuShiftModifier; |
| if (dontSwap) { |
| if ((accel_key & Qt::META) != Qt::META) |
| ret |= kMenuNoCommandModifier; |
| if ((accel_key & Qt::CTRL) == Qt::CTRL) |
| ret |= kMenuControlModifier; |
| } else { |
| if ((accel_key & Qt::CTRL) != Qt::CTRL) |
| ret |= kMenuNoCommandModifier; |
| if ((accel_key & Qt::META) == Qt::META) |
| ret |= kMenuControlModifier; |
| } |
| #else |
| if ((accel_key & Qt::CTRL) == Qt::CTRL) |
| ret |= (dontSwap ? NSControlKeyMask : NSCommandKeyMask); |
| if ((accel_key & Qt::META) == Qt::META) |
| ret |= (dontSwap ? NSCommandKeyMask : NSControlKeyMask); |
| if ((accel_key & Qt::ALT) == Qt::ALT) |
| ret |= NSAlternateKeyMask; |
| if ((accel_key & Qt::SHIFT) == Qt::SHIFT) |
| ret |= NSShiftKeyMask; |
| #endif |
| return ret; |
| } |
| |
| static void cancelAllMenuTracking() |
| { |
| #ifdef QT_MAC_USE_COCOA |
| QMacCocoaAutoReleasePool pool; |
| NSMenu *mainMenu = [NSApp mainMenu]; |
| [mainMenu cancelTracking]; |
| for (NSMenuItem *item in [mainMenu itemArray]) { |
| if ([item submenu]) { |
| [[item submenu] cancelTracking]; |
| } |
| } |
| #else |
| CancelMenuTracking(AcquireRootMenu(), true, 0); |
| #endif |
| } |
| |
| static bool actualMenuItemVisibility(const QMenuBarPrivate::QMacMenuBarPrivate *mbp, |
| const QMacMenuAction *action) |
| { |
| bool visible = action->action->isVisible(); |
| if (visible && action->action->text() == QString(QChar(0x14))) |
| return false; |
| if (visible && action->action->menu() && !action->action->menu()->actions().isEmpty() && |
| !qt_mac_CountMenuItems(action->action->menu()->macMenu(mbp->apple_menu)) && |
| !qt_mac_watchingAboutToShow(action->action->menu())) { |
| return false; |
| } |
| return visible; |
| } |
| |
| #ifndef QT_MAC_USE_COCOA |
| bool qt_mac_activate_action(MenuRef menu, uint command, QAction::ActionEvent action_e, bool by_accel) |
| { |
| //fire event |
| QMacMenuAction *action = 0; |
| if (GetMenuCommandProperty(menu, command, kMenuCreatorQt, kMenuPropertyQAction, sizeof(action), 0, &action) != noErr) { |
| QMenuMergeList *list = 0; |
| GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeList, |
| sizeof(list), 0, &list); |
| if (!list && qt_mac_current_menubar.qmenubar && qt_mac_current_menubar.qmenubar->isNativeMenuBar()) { |
| MenuRef apple_menu = qt_mac_current_menubar.qmenubar->d_func()->mac_menubar->apple_menu; |
| GetMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList, sizeof(list), 0, &list); |
| if (list) |
| menu = apple_menu; |
| } |
| if (list) { |
| for(int i = 0; i < list->size(); ++i) { |
| QMenuMergeItem item = list->at(i); |
| if (item.command == command && item.action) { |
| action = item.action; |
| break; |
| } |
| } |
| } |
| if (!action) |
| return false; |
| } |
| |
| if (action_e == QAction::Trigger && by_accel && action->ignore_accel) //no, not a real accel (ie tab) |
| return false; |
| |
| // Unhighlight the highlighted menu item before triggering the action to |
| // prevent items from staying highlighted while a modal dialog is shown. |
| // This also fixed the problem that parentless modal dialogs leave |
| // the menu item highlighted (since the menu bar is cleared for these types of dialogs). |
| if (action_e == QAction::Trigger) |
| HiliteMenu(0); |
| |
| action->action->activate(action_e); |
| |
| //now walk up firing for each "caused" widget (like in the platform independent menu) |
| QWidget *caused = 0; |
| if (action_e == QAction::Hover && GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), 0, &caused) == noErr) { |
| MenuRef caused_menu = 0; |
| if (QMenu *qmenu2 = qobject_cast<QMenu*>(caused)) |
| caused_menu = qmenu2->macMenu(); |
| else if (QMenuBar *qmenubar2 = qobject_cast<QMenuBar*>(caused)) |
| caused_menu = qmenubar2->macMenu(); |
| else |
| caused_menu = 0; |
| while(caused_menu) { |
| //fire |
| QWidget *widget = 0; |
| GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(widget), 0, &widget); |
| if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) { |
| action->action->showStatusText(widget); |
| emit qmenu->hovered(action->action); |
| } else if (QMenuBar *qmenubar = qobject_cast<QMenuBar*>(widget)) { |
| action->action->showStatusText(widget); |
| emit qmenubar->hovered(action->action); |
| break; //nothing more.. |
| } |
| |
| //walk up |
| if (GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, |
| sizeof(caused), 0, &caused) != noErr) |
| break; |
| if (QMenu *qmenu2 = qobject_cast<QMenu*>(caused)) |
| caused_menu = qmenu2->macMenu(); |
| else if (QMenuBar *qmenubar2 = qobject_cast<QMenuBar*>(caused)) |
| caused_menu = qmenubar2->macMenu(); |
| else |
| caused_menu = 0; |
| } |
| } |
| return true; |
| } |
| |
| //lookup a QMacMenuAction in a menu |
| static int qt_mac_menu_find_action(MenuRef menu, MenuCommand cmd) |
| { |
| MenuItemIndex ret_idx; |
| MenuRef ret_menu; |
| if (GetIndMenuItemWithCommandID(menu, cmd, 1, &ret_menu, &ret_idx) == noErr) { |
| if (ret_menu == menu) |
| return (int)ret_idx; |
| } |
| return -1; |
| } |
| static int qt_mac_menu_find_action(MenuRef menu, QMacMenuAction *action) |
| { |
| return qt_mac_menu_find_action(menu, action->command); |
| } |
| |
| typedef QMultiHash<OSMenuRef, EventHandlerRef> EventHandlerHash; |
| Q_GLOBAL_STATIC(EventHandlerHash, menu_eventHandlers_hash) |
| |
| static EventTypeSpec widget_in_menu_events[] = { |
| { kEventClassMenu, kEventMenuMeasureItemWidth }, |
| { kEventClassMenu, kEventMenuMeasureItemHeight }, |
| { kEventClassMenu, kEventMenuDrawItem }, |
| { kEventClassMenu, kEventMenuCalculateSize } |
| }; |
| |
| static OSStatus qt_mac_widget_in_menu_eventHandler(EventHandlerCallRef er, EventRef event, void *) |
| { |
| UInt32 ekind = GetEventKind(event); |
| UInt32 eclass = GetEventClass(event); |
| OSStatus result = eventNotHandledErr; |
| switch (eclass) { |
| case kEventClassMenu: |
| switch (ekind) { |
| default: |
| break; |
| case kEventMenuMeasureItemWidth: { |
| MenuItemIndex item; |
| GetEventParameter(event, kEventParamMenuItemIndex, typeMenuItemIndex, |
| 0, sizeof(item), 0, &item); |
| OSMenuRef menu; |
| GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu); |
| QWidget *widget; |
| if (GetMenuItemProperty(menu, item, kMenuCreatorQt, kMenuPropertyWidgetActionWidget, |
| sizeof(widget), 0, &widget) == noErr) { |
| short width = short(widget->sizeHint().width()); |
| SetEventParameter(event, kEventParamMenuItemWidth, typeSInt16, |
| sizeof(short), &width); |
| result = noErr; |
| } |
| break; } |
| case kEventMenuMeasureItemHeight: { |
| MenuItemIndex item; |
| GetEventParameter(event, kEventParamMenuItemIndex, typeMenuItemIndex, |
| 0, sizeof(item), 0, &item); |
| OSMenuRef menu; |
| GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu); |
| QWidget *widget; |
| if (GetMenuItemProperty(menu, item, kMenuCreatorQt, kMenuPropertyWidgetActionWidget, |
| sizeof(widget), 0, &widget) == noErr && widget) { |
| short height = short(widget->sizeHint().height()); |
| SetEventParameter(event, kEventParamMenuItemHeight, typeSInt16, |
| sizeof(short), &height); |
| result = noErr; |
| } |
| break; } |
| case kEventMenuDrawItem: |
| result = noErr; |
| break; |
| case kEventMenuCalculateSize: { |
| result = CallNextEventHandler(er, event); |
| if (result == noErr) { |
| OSMenuRef menu; |
| GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu); |
| HIViewRef content; |
| HIMenuGetContentView(menu, kThemeMenuTypePullDown, &content); |
| UInt16 count = CountMenuItems(menu); |
| for (MenuItemIndex i = 1; i <= count; ++i) { |
| QWidget *widget; |
| if (GetMenuItemProperty(menu, i, kMenuCreatorQt, kMenuPropertyWidgetActionWidget, |
| sizeof(widget), 0, &widget) == noErr && widget) { |
| RgnHandle itemRgn = qt_mac_get_rgn(); |
| GetControlRegion(content, i, itemRgn); |
| |
| Rect bounds; |
| GetRegionBounds( itemRgn, &bounds ); |
| qt_mac_dispose_rgn(itemRgn); |
| widget->setGeometry(bounds.left, bounds.top, |
| bounds.right - bounds.left, bounds.bottom - bounds.top); |
| } |
| } |
| } |
| break; } |
| } |
| } |
| return result; |
| } |
| |
| //handling of events for menurefs created by Qt.. |
| static EventTypeSpec menu_events[] = { |
| { kEventClassCommand, kEventCommandProcess }, |
| { kEventClassMenu, kEventMenuTargetItem }, |
| { kEventClassMenu, kEventMenuOpening }, |
| { kEventClassMenu, kEventMenuClosed } |
| }; |
| |
| // Special case for kEventMenuMatchKey, see qt_mac_create_menu below. |
| static EventTypeSpec menu_menu_events[] = { |
| { kEventClassMenu, kEventMenuMatchKey } |
| }; |
| |
| OSStatus qt_mac_menu_event(EventHandlerCallRef er, EventRef event, void *) |
| { |
| QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData); |
| |
| bool handled_event = true; |
| UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event); |
| switch(eclass) { |
| case kEventClassCommand: |
| if (ekind == kEventCommandProcess) { |
| UInt32 context; |
| GetEventParameter(event, kEventParamMenuContext, typeUInt32, |
| 0, sizeof(context), 0, &context); |
| HICommand cmd; |
| GetEventParameter(event, kEventParamDirectObject, typeHICommand, |
| 0, sizeof(cmd), 0, &cmd); |
| if (!mac_keyboard_grabber && (context & kMenuContextKeyMatching)) { |
| QMacMenuAction *action = 0; |
| if (GetMenuCommandProperty(cmd.menu.menuRef, cmd.commandID, kMenuCreatorQt, |
| kMenuPropertyQAction, sizeof(action), 0, &action) == noErr) { |
| QWidget *widget = 0; |
| if (qApp->activePopupWidget()) |
| widget = (qApp->activePopupWidget()->focusWidget() ? |
| qApp->activePopupWidget()->focusWidget() : qApp->activePopupWidget()); |
| else if (QApplicationPrivate::focus_widget) |
| widget = QApplicationPrivate::focus_widget; |
| if (widget) { |
| int key = action->action->shortcut(); |
| QKeyEvent accel_ev(QEvent::ShortcutOverride, (key & (~Qt::KeyboardModifierMask)), |
| Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask)); |
| accel_ev.ignore(); |
| qt_sendSpontaneousEvent(widget, &accel_ev); |
| if (accel_ev.isAccepted()) { |
| handled_event = false; |
| break; |
| } |
| } |
| } |
| } |
| handled_event = qt_mac_activate_action(cmd.menu.menuRef, cmd.commandID, |
| QAction::Trigger, context & kMenuContextKeyMatching); |
| } |
| break; |
| case kEventClassMenu: { |
| MenuRef menu; |
| GetEventParameter(event, kEventParamDirectObject, typeMenuRef, NULL, sizeof(menu), NULL, &menu); |
| if (ekind == kEventMenuMatchKey) { |
| // Don't activate any actions if we are showing a native modal dialog, |
| // the key events should go to the dialog in this case. |
| if (QApplicationPrivate::native_modal_dialog_active) |
| return menuItemNotFoundErr; |
| |
| handled_event = false; |
| } else if (ekind == kEventMenuTargetItem) { |
| MenuCommand command; |
| GetEventParameter(event, kEventParamMenuCommand, typeMenuCommand, |
| 0, sizeof(command), 0, &command); |
| handled_event = qt_mac_activate_action(menu, command, QAction::Hover, false); |
| } else if (ekind == kEventMenuOpening || ekind == kEventMenuClosed) { |
| qt_mac_menus_open_count += (ekind == kEventMenuOpening) ? 1 : -1; |
| MenuRef mr; |
| GetEventParameter(event, kEventParamDirectObject, typeMenuRef, |
| 0, sizeof(mr), 0, &mr); |
| |
| QWidget *widget = 0; |
| if (GetMenuItemProperty(mr, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(widget), 0, &widget) == noErr) { |
| if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) { |
| handled_event = true; |
| if (ekind == kEventMenuOpening) { |
| emit qmenu->aboutToShow(); |
| |
| int merged = 0; |
| const QMenuPrivate::QMacMenuPrivate *mac_menu = qmenu->d_func()->mac_menu; |
| const int ActionItemsCount = mac_menu->actionItems.size(); |
| for(int i = 0; i < ActionItemsCount; ++i) { |
| QMacMenuAction *action = mac_menu->actionItems.at(i); |
| if (action->action->isSeparator()) { |
| bool hide = false; |
| if(!action->action->isVisible()) { |
| hide = true; |
| } else if (merged && merged == i) { |
| hide = true; |
| } else { |
| for(int l = i+1; l < mac_menu->actionItems.size(); ++l) { |
| QMacMenuAction *action = mac_menu->actionItems.at(l); |
| if (action->merged) { |
| hide = true; |
| } else if (action->action->isSeparator()) { |
| if (hide) |
| break; |
| } else if (!action->merged) { |
| hide = false; |
| break; |
| } |
| } |
| } |
| |
| const int index = qt_mac_menu_find_action(mr, action); |
| if (hide) { |
| ++merged; |
| ChangeMenuItemAttributes(mr, index, kMenuItemAttrHidden, 0); |
| } else { |
| ChangeMenuItemAttributes(mr, index, 0, kMenuItemAttrHidden); |
| } |
| } else if (action->merged) { |
| ++merged; |
| } |
| } |
| } else { |
| emit qmenu->aboutToHide(); |
| } |
| } |
| } |
| } else { |
| handled_event = false; |
| } |
| break; } |
| default: |
| handled_event = false; |
| break; |
| } |
| if (!handled_event) //let the event go through |
| return CallNextEventHandler(er, event); |
| return noErr; //we eat the event |
| } |
| static EventHandlerRef mac_menu_event_handler = 0; |
| static EventHandlerUPP mac_menu_eventUPP = 0; |
| static void qt_mac_cleanup_menu_event() |
| { |
| if (mac_menu_event_handler) { |
| RemoveEventHandler(mac_menu_event_handler); |
| mac_menu_event_handler = 0; |
| } |
| if (mac_menu_eventUPP) { |
| DisposeEventHandlerUPP(mac_menu_eventUPP); |
| mac_menu_eventUPP = 0; |
| } |
| } |
| static inline void qt_mac_create_menu_event_handler() |
| { |
| if (!mac_menu_event_handler) { |
| mac_menu_eventUPP = NewEventHandlerUPP(qt_mac_menu_event); |
| InstallEventHandler(GetApplicationEventTarget(), mac_menu_eventUPP, |
| GetEventTypeCount(menu_events), menu_events, 0, |
| &mac_menu_event_handler); |
| qAddPostRoutine(qt_mac_cleanup_menu_event); |
| } |
| } |
| |
| |
| //enabling of commands |
| static void qt_mac_command_set_enabled(MenuRef menu, UInt32 cmd, bool b) |
| { |
| if (cmd == kHICommandQuit) |
| qt_mac_quit_menu_item_enabled = b; |
| |
| if (b) { |
| EnableMenuCommand(menu, cmd); |
| if (MenuRef dock_menu = GetApplicationDockTileMenu()) |
| EnableMenuCommand(dock_menu, cmd); |
| } else { |
| DisableMenuCommand(menu, cmd); |
| if (MenuRef dock_menu = GetApplicationDockTileMenu()) |
| DisableMenuCommand(dock_menu, cmd); |
| } |
| } |
| |
| static bool qt_mac_auto_apple_menu(MenuCommand cmd) |
| { |
| return (cmd == kHICommandPreferences || cmd == kHICommandQuit); |
| } |
| |
| static void qt_mac_get_accel(quint32 accel_key, quint32 *modif, quint32 *key) { |
| if (modif) { |
| *modif = constructModifierMask(accel_key); |
| } |
| |
| accel_key &= ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL); |
| if (key) { |
| *key = 0; |
| if (accel_key == Qt::Key_Return) |
| *key = kMenuReturnGlyph; |
| else if (accel_key == Qt::Key_Enter) |
| *key = kMenuEnterGlyph; |
| else if (accel_key == Qt::Key_Tab) |
| *key = kMenuTabRightGlyph; |
| else if (accel_key == Qt::Key_Backspace) |
| *key = kMenuDeleteLeftGlyph; |
| else if (accel_key == Qt::Key_Delete) |
| *key = kMenuDeleteRightGlyph; |
| else if (accel_key == Qt::Key_Escape) |
| *key = kMenuEscapeGlyph; |
| else if (accel_key == Qt::Key_PageUp) |
| *key = kMenuPageUpGlyph; |
| else if (accel_key == Qt::Key_PageDown) |
| *key = kMenuPageDownGlyph; |
| else if (accel_key == Qt::Key_Up) |
| *key = kMenuUpArrowGlyph; |
| else if (accel_key == Qt::Key_Down) |
| *key = kMenuDownArrowGlyph; |
| else if (accel_key == Qt::Key_Left) |
| *key = kMenuLeftArrowGlyph; |
| else if (accel_key == Qt::Key_Right) |
| *key = kMenuRightArrowGlyph; |
| else if (accel_key == Qt::Key_CapsLock) |
| *key = kMenuCapsLockGlyph; |
| else if (accel_key >= Qt::Key_F1 && accel_key <= Qt::Key_F15) |
| *key = (accel_key - Qt::Key_F1) + kMenuF1Glyph; |
| else if (accel_key == Qt::Key_Home) |
| *key = kMenuNorthwestArrowGlyph; |
| else if (accel_key == Qt::Key_End) |
| *key = kMenuSoutheastArrowGlyph; |
| } |
| } |
| #else // Cocoa |
| static inline void syncNSMenuItemVisiblity(NSMenuItem *menuItem, bool actionVisibility) |
| { |
| [menuItem setHidden:NO]; |
| [menuItem setHidden:YES]; |
| [menuItem setHidden:!actionVisibility]; |
| } |
| |
| static inline void syncNSMenuItemEnabled(NSMenuItem *menuItem, bool enabled) |
| { |
| [menuItem setEnabled:NO]; |
| [menuItem setEnabled:YES]; |
| [menuItem setEnabled:enabled]; |
| } |
| |
| static inline void syncMenuBarItemsVisiblity(const QMenuBarPrivate::QMacMenuBarPrivate *mac_menubar) |
| { |
| const QList<QMacMenuAction *> &menubarActions = mac_menubar->actionItems; |
| for (int i = 0; i < menubarActions.size(); ++i) { |
| const QMacMenuAction *action = menubarActions.at(i); |
| syncNSMenuItemVisiblity(action->menuItem, actualMenuItemVisibility(mac_menubar, action)); |
| } |
| } |
| |
| static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader() |
| { |
| return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)]; |
| } |
| |
| static NSMenuItem *createNSMenuItem(const QString &title) |
| { |
| NSMenuItem *item = [[NSMenuItem alloc] |
| initWithTitle:qt_mac_QStringToNSString(title) |
| action:@selector(qtDispatcherToQAction:) keyEquivalent:@""]; |
| [item setTarget:nil]; |
| return item; |
| } |
| #endif |
| |
| |
| |
| // helper that recurses into a menu structure and en/dis-ables them |
| void qt_mac_set_modal_state_helper_recursive(OSMenuRef menu, OSMenuRef merge, bool on) |
| { |
| #ifndef QT_MAC_USE_COCOA |
| for (int i = 0; i < CountMenuItems(menu); i++) { |
| OSMenuRef submenu; |
| GetMenuItemHierarchicalMenu(menu, i+1, &submenu); |
| if (submenu != merge) { |
| if (submenu) |
| qt_mac_set_modal_state_helper_recursive(submenu, merge, on); |
| if (on) |
| DisableMenuItem(submenu, 0); |
| else |
| EnableMenuItem(submenu, 0); |
| } |
| } |
| #else |
| bool modalWindowOnScreen = qApp->activeModalWidget() != 0; |
| for (NSMenuItem *item in [menu itemArray]) { |
| OSMenuRef submenu = [item submenu]; |
| if (submenu != merge) { |
| if (submenu) |
| qt_mac_set_modal_state_helper_recursive(submenu, merge, on); |
| if (!on) { |
| // The item should follow what the QAction has. |
| if ([item tag]) { |
| QAction *action = reinterpret_cast<QAction *>([item tag]); |
| syncNSMenuItemEnabled(item, action->isEnabled()); |
| } else { |
| syncNSMenuItemEnabled(item, YES); |
| } |
| // We sneak in some extra code here to handle a menu problem: |
| // If there is no window on screen, we cannot set 'nil' as |
| // menu item target, because then cocoa will disable the item |
| // (guess it assumes that there will be no first responder to |
| // catch the trigger anyway?) OTOH, If we have a modal window, |
| // then setting the menu loader as target will make cocoa not |
| // deliver the trigger because the loader is then seen as modally |
| // shaddowed). So either way there are shortcomings. Instead, we |
| // decide the target as late as possible: |
| [item setTarget:modalWindowOnScreen ? nil : getMenuLoader()]; |
| } else { |
| syncNSMenuItemEnabled(item, NO); |
| } |
| } |
| } |
| #endif |
| } |
| |
| //toggling of modal state |
| static void qt_mac_set_modal_state(OSMenuRef menu, bool on) |
| { |
| #ifndef QT_MAC_USE_COCOA |
| OSMenuRef merge = 0; |
| GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu, |
| sizeof(merge), 0, &merge); |
| |
| qt_mac_set_modal_state_helper_recursive(menu, merge, on); |
| |
| UInt32 commands[] = { kHICommandQuit, kHICommandPreferences, kHICommandAbout, kHICommandAboutQt, 0 }; |
| for(int c = 0; commands[c]; c++) { |
| bool enabled = !on; |
| if (enabled) { |
| QMacMenuAction *action = 0; |
| GetMenuCommandProperty(menu, commands[c], kMenuCreatorQt, kMenuPropertyQAction, |
| sizeof(action), 0, &action); |
| if (!action && merge) { |
| GetMenuCommandProperty(merge, commands[c], kMenuCreatorQt, kMenuPropertyQAction, |
| sizeof(action), 0, &action); |
| if (!action) { |
| QMenuMergeList *list = 0; |
| GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList, |
| sizeof(list), 0, &list); |
| for(int i = 0; list && i < list->size(); ++i) { |
| QMenuMergeItem item = list->at(i); |
| if (item.command == commands[c] && item.action) { |
| action = item.action; |
| break; |
| } |
| } |
| } |
| } |
| |
| if (!action) { |
| if (commands[c] != kHICommandQuit) |
| enabled = false; |
| } else { |
| enabled = action->action ? action->action->isEnabled() : 0; |
| } |
| } |
| qt_mac_command_set_enabled(menu, commands[c], enabled); |
| } |
| #else |
| OSMenuRef merge = QMenuPrivate::mergeMenuHash.value(menu); |
| qt_mac_set_modal_state_helper_recursive(menu, merge, on); |
| // I'm ignoring the special items now, since they should get handled via a syncAction() |
| #endif |
| } |
| |
| bool qt_mac_menubar_is_open() |
| { |
| return qt_mac_menus_open_count > 0; |
| } |
| |
| QMacMenuAction::~QMacMenuAction() |
| { |
| #ifdef QT_MAC_USE_COCOA |
| [menu release]; |
| // Update the menu item if this action still owns it. For some items |
| // (like 'Quit') ownership will be transferred between all menu bars... |
| if (action && action.data() == reinterpret_cast<QAction *>([menuItem tag])) { |
| QAction::MenuRole role = action->menuRole(); |
| // Check if the item is owned by Qt, and should be hidden to keep it from causing |
| // problems. Do it for everything but the quit menu item since that should always |
| // be visible. |
| if (role > QAction::ApplicationSpecificRole && role < QAction::QuitRole) { |
| [menuItem setHidden:YES]; |
| } else if (role == QAction::TextHeuristicRole |
| && menuItem != [getMenuLoader() quitMenuItem]) { |
| [menuItem setHidden:YES]; |
| } |
| [menuItem setTag:nil]; |
| } |
| [menuItem release]; |
| #endif |
| } |
| |
| #ifndef QT_MAC_USE_COCOA |
| static MenuCommand qt_mac_menu_merge_action(MenuRef merge, QMacMenuAction *action) |
| #else |
| static NSMenuItem *qt_mac_menu_merge_action(OSMenuRef merge, QMacMenuAction *action) |
| #endif |
| { |
| if (qt_mac_no_menubar_merge || action->action->menu() || action->action->isSeparator() |
| || action->action->menuRole() == QAction::NoRole) |
| return 0; |
| |
| QString t = qt_mac_removeMnemonics(action->action->text().toLower()); |
| int st = t.lastIndexOf(QLatin1Char('\t')); |
| if (st != -1) |
| t.remove(st, t.length()-st); |
| t.replace(QRegExp(QString::fromLatin1("\\.*$")), QLatin1String("")); //no ellipses |
| //now the fun part |
| #ifndef QT_MAC_USE_COCOA |
| MenuCommand ret = 0; |
| #else |
| NSMenuItem *ret = 0; |
| QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader(); |
| #endif |
| switch (action->action->menuRole()) { |
| case QAction::NoRole: |
| ret = 0; |
| break; |
| case QAction::ApplicationSpecificRole: |
| #ifndef QT_MAC_USE_COCOA |
| { |
| QMenuMergeList *list = 0; |
| if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList, |
| sizeof(list), 0, &list) == noErr && list) { |
| MenuCommand lastCustom = kHICommandCustomMerge; |
| for(int i = 0; i < list->size(); ++i) { |
| QMenuMergeItem item = list->at(i); |
| if (item.command == lastCustom) |
| ++lastCustom; |
| } |
| ret = lastCustom; |
| } else { |
| // The list hasn't been created, so, must be the first one. |
| ret = kHICommandCustomMerge; |
| } |
| } |
| #else |
| ret = [loader appSpecificMenuItem]; |
| #endif |
| break; |
| case QAction::AboutRole: |
| #ifndef QT_MAC_USE_COCOA |
| ret = kHICommandAbout; |
| #else |
| ret = [loader aboutMenuItem]; |
| #endif |
| break; |
| case QAction::AboutQtRole: |
| #ifndef QT_MAC_USE_COCOA |
| ret = kHICommandAboutQt; |
| #else |
| ret = [loader aboutQtMenuItem]; |
| #endif |
| break; |
| case QAction::QuitRole: |
| #ifndef QT_MAC_USE_COCOA |
| ret = kHICommandQuit; |
| #else |
| ret = [loader quitMenuItem]; |
| #endif |
| break; |
| case QAction::PreferencesRole: |
| #ifndef QT_MAC_USE_COCOA |
| ret = kHICommandPreferences; |
| #else |
| ret = [loader preferencesMenuItem]; |
| #endif |
| break; |
| case QAction::TextHeuristicRole: { |
| QString aboutString = QMenuBar::tr("About").toLower(); |
| if (t.startsWith(aboutString) || t.endsWith(aboutString)) { |
| if (t.indexOf(QRegExp(QString::fromLatin1("qt$"), Qt::CaseInsensitive)) == -1) { |
| #ifndef QT_MAC_USE_COCOA |
| ret = kHICommandAbout; |
| #else |
| ret = [loader aboutMenuItem]; |
| #endif |
| } else { |
| #ifndef QT_MAC_USE_COCOA |
| ret = kHICommandAboutQt; |
| #else |
| ret = [loader aboutQtMenuItem]; |
| #endif |
| } |
| } else if (t.startsWith(QMenuBar::tr("Config").toLower()) |
| || t.startsWith(QMenuBar::tr("Preference").toLower()) |
| || t.startsWith(QMenuBar::tr("Options").toLower()) |
| || t.startsWith(QMenuBar::tr("Setting").toLower()) |
| || t.startsWith(QMenuBar::tr("Setup").toLower())) { |
| #ifndef QT_MAC_USE_COCOA |
| ret = kHICommandPreferences; |
| #else |
| ret = [loader preferencesMenuItem]; |
| #endif |
| } else if (t.startsWith(QMenuBar::tr("Quit").toLower()) |
| || t.startsWith(QMenuBar::tr("Exit").toLower())) { |
| #ifndef QT_MAC_USE_COCOA |
| ret = kHICommandQuit; |
| #else |
| ret = [loader quitMenuItem]; |
| #endif |
| } |
| } |
| break; |
| } |
| |
| #ifndef QT_MAC_USE_COCOA |
| QMenuMergeList *list = 0; |
| if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList, |
| sizeof(list), 0, &list) == noErr && list) { |
| for(int i = 0; i < list->size(); ++i) { |
| QMenuMergeItem item = list->at(i); |
| if (item.command == ret && item.action) |
| return 0; |
| } |
| } |
| |
| QAction *cmd_action = 0; |
| if (GetMenuCommandProperty(merge, ret, kMenuCreatorQt, kMenuPropertyQAction, |
| sizeof(cmd_action), 0, &cmd_action) == noErr && cmd_action) |
| return 0; //already taken |
| #else |
| if (QMenuMergeList *list = QMenuPrivate::mergeMenuItemsHash.value(merge)) { |
| for(int i = 0; i < list->size(); ++i) { |
| const QMenuMergeItem &item = list->at(i); |
| if (item.menuItem == ret && item.action) |
| return 0; |
| } |
| } |
| |
| #endif |
| return ret; |
| } |
| |
| static QString qt_mac_menu_merge_text(QMacMenuAction *action) |
| { |
| QString ret; |
| extern QString qt_mac_applicationmenu_string(int type); |
| #ifdef QT_MAC_USE_COCOA |
| QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader(); |
| #endif |
| if (action->action->menuRole() == QAction::ApplicationSpecificRole) |
| ret = action->action->text(); |
| #ifndef QT_MAC_USE_COCOA |
| else if (action->command == kHICommandAbout) |
| ret = qt_mac_applicationmenu_string(6).arg(qAppName()); |
| else if (action->command == kHICommandAboutQt) |
| ret = QMenuBar::tr("About Qt"); |
| else if (action->command == kHICommandPreferences) |
| ret = qt_mac_applicationmenu_string(4); |
| else if (action->command == kHICommandQuit) |
| ret = qt_mac_applicationmenu_string(5).arg(qAppName()); |
| #else |
| else if (action->menuItem == [loader aboutMenuItem]) { |
| ret = qt_mac_applicationmenu_string(6).arg(qAppName()); |
| } else if (action->menuItem == [loader aboutQtMenuItem]) { |
| if (action->action->text() == QString("About Qt")) |
| ret = QMenuBar::tr("About Qt"); |
| else |
| ret = action->action->text(); |
| } else if (action->menuItem == [loader preferencesMenuItem]) { |
| ret = qt_mac_applicationmenu_string(4); |
| } else if (action->menuItem == [loader quitMenuItem]) { |
| ret = qt_mac_applicationmenu_string(5).arg(qAppName()); |
| } |
| #endif |
| return ret; |
| } |
| |
| static QKeySequence qt_mac_menu_merge_accel(QMacMenuAction *action) |
| { |
| QKeySequence ret; |
| #ifdef QT_MAC_USE_COCOA |
| QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader(); |
| #endif |
| if (action->action->menuRole() == QAction::ApplicationSpecificRole) |
| ret = action->action->shortcut(); |
| #ifndef QT_MAC_USE_COCOA |
| else if (action->command == kHICommandPreferences) |
| ret = QKeySequence(QKeySequence::Preferences); |
| else if (action->command == kHICommandQuit) |
| ret = QKeySequence(QKeySequence::Quit); |
| #else |
| else if (action->menuItem == [loader preferencesMenuItem]) |
| ret = QKeySequence(QKeySequence::Preferences); |
| else if (action->menuItem == [loader quitMenuItem]) |
| ret = QKeySequence(QKeySequence::Quit); |
| #endif |
| return ret; |
| } |
| |
| void Q_GUI_EXPORT qt_mac_set_menubar_icons(bool b) |
| { QApplication::instance()->setAttribute(Qt::AA_DontShowIconsInMenus, !b); } |
| void Q_GUI_EXPORT qt_mac_set_native_menubar(bool b) |
| { QApplication::instance()->setAttribute(Qt::AA_DontUseNativeMenuBar, !b); } |
| void Q_GUI_EXPORT qt_mac_set_menubar_merge(bool b) { qt_mac_no_menubar_merge = !b; } |
| |
| /***************************************************************************** |
| QMenu bindings |
| *****************************************************************************/ |
| QMenuPrivate::QMacMenuPrivate::QMacMenuPrivate() : menu(0) |
| { |
| } |
| |
| QMenuPrivate::QMacMenuPrivate::~QMacMenuPrivate() |
| { |
| #ifndef QT_MAC_USE_COCOA |
| for(QList<QMacMenuAction*>::Iterator it = actionItems.begin(); it != actionItems.end(); ++it) { |
| QMacMenuAction *action = (*it); |
| RemoveMenuCommandProperty(action->menu, action->command, kMenuCreatorQt, kMenuPropertyQAction); |
| if (action->merged) { |
| QMenuMergeList *list = 0; |
| GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyMergeList, |
| sizeof(list), 0, &list); |
| for(int i = 0; list && i < list->size(); ) { |
| QMenuMergeItem item = list->at(i); |
| if (item.action == action) |
| list->removeAt(i); |
| else |
| ++i; |
| } |
| } |
| delete action; |
| } |
| if (menu) { |
| EventHandlerHash::iterator it = menu_eventHandlers_hash()->find(menu); |
| while (it != menu_eventHandlers_hash()->end() && it.key() == menu) { |
| RemoveEventHandler(it.value()); |
| ++it; |
| } |
| menu_eventHandlers_hash()->remove(menu); |
| ReleaseMenu(menu); |
| } |
| #else |
| QMacCocoaAutoReleasePool pool; |
| while (actionItems.size()) { |
| QMacMenuAction *action = actionItems.takeFirst(); |
| if (QMenuMergeList *list = mergeMenuItemsHash.value(action->menu)) { |
| int i = 0; |
| while (i < list->size()) { |
| const QMenuMergeItem &item = list->at(i); |
| if (item.action == action) |
| list->removeAt(i); |
| else |
| ++i; |
| } |
| } |
| delete action; |
| } |
| mergeMenuHash.remove(menu); |
| mergeMenuItemsHash.remove(menu); |
| [menu release]; |
| #endif |
| } |
| |
| void |
| QMenuPrivate::QMacMenuPrivate::addAction(QAction *a, QMacMenuAction *before, QMenuPrivate *qmenu) |
| { |
| QMacMenuAction *action = new QMacMenuAction; |
| action->action = a; |
| action->ignore_accel = 0; |
| action->merged = 0; |
| action->menu = 0; |
| #ifndef QT_MAC_USE_COCOA |
| action->command = qt_mac_menu_static_cmd_id++; |
| #endif |
| addAction(action, before, qmenu); |
| } |
| |
| void |
| QMenuPrivate::QMacMenuPrivate::addAction(QMacMenuAction *action, QMacMenuAction *before, QMenuPrivate *qmenu) |
| { |
| #ifdef QT_MAC_USE_COCOA |
| QMacCocoaAutoReleasePool pool; |
| Q_UNUSED(qmenu); |
| #endif |
| if (!action) |
| return; |
| int before_index = actionItems.indexOf(before); |
| if (before_index < 0) { |
| before = 0; |
| before_index = actionItems.size(); |
| } |
| actionItems.insert(before_index, action); |
| |
| #ifndef QT_MAC_USE_COCOA |
| int index = qt_mac_menu_find_action(menu, action); |
| #else |
| [menu retain]; |
| [action->menu release]; |
| #endif |
| action->menu = menu; |
| |
| /* When the action is considered a mergable action it |
| will stay that way, until removed.. */ |
| if (!qt_mac_no_menubar_merge) { |
| #ifndef QT_MAC_USE_COCOA |
| MenuRef merge = 0; |
| GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu, |
| sizeof(merge), 0, &merge); |
| #else |
| OSMenuRef merge = QMenuPrivate::mergeMenuHash.value(menu); |
| #endif |
| if (merge) { |
| #ifndef QT_MAC_USE_COCOA |
| if (MenuCommand cmd = qt_mac_menu_merge_action(merge, action)) { |
| action->merged = 1; |
| action->menu = merge; |
| action->command = cmd; |
| if (qt_mac_auto_apple_menu(cmd)) |
| index = 0; //no need |
| |
| QMenuMergeList *list = 0; |
| if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList, |
| sizeof(list), 0, &list) != noErr || !list) { |
| list = new QMenuMergeList; |
| SetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList, |
| sizeof(list), &list); |
| } |
| list->append(QMenuMergeItem(cmd, action)); |
| } |
| #else |
| if (NSMenuItem *cmd = qt_mac_menu_merge_action(merge, action)) { |
| action->merged = 1; |
| [merge retain]; |
| [action->menu release]; |
| action->menu = merge; |
| [cmd retain]; |
| [cmd setAction:@selector(qtDispatcherToQAction:)]; |
| [cmd setTarget:nil]; |
| [action->menuItem release]; |
| action->menuItem = cmd; |
| QMenuMergeList *list = QMenuPrivate::mergeMenuItemsHash.value(merge); |
| if (!list) { |
| list = new QMenuMergeList; |
| QMenuPrivate::mergeMenuItemsHash.insert(merge, list); |
| } |
| list->append(QMenuMergeItem(cmd, action)); |
| } |
| #endif |
| } |
| } |
| |
| #ifdef QT_MAC_USE_COCOA |
| NSMenuItem *newItem = action->menuItem; |
| #endif |
| if ( |
| #ifndef QT_MAC_USE_COCOA |
| index == -1 |
| #else |
| newItem == 0 |
| #endif |
| ) { |
| #ifndef QT_MAC_USE_COCOA |
| index = before_index; |
| MenuItemAttributes attr = kMenuItemAttrAutoRepeat; |
| #else |
| newItem = createNSMenuItem(action->action->text()); |
| action->menuItem = newItem; |
| #endif |
| if (before) { |
| #ifndef QT_MAC_USE_COCOA |
| InsertMenuItemTextWithCFString(action->menu, 0, qMax(before_index, 0), attr, action->command); |
| #else |
| [menu insertItem:newItem atIndex:qMax(before_index, 0)]; |
| #endif |
| } else { |
| #ifndef QT_MAC_USE_COCOA |
| // Append the menu item to the menu. If it is a kHICommandAbout or a kHICommandAboutQt append |
| // a separator also (to get a separator above "Preferences"), but make sure that we don't |
| // add separators between two "about" items. |
| |
| // Build a set of all commands that could possibly be before the separator. |
| QSet<MenuCommand> mergedItems; |
| mergedItems.insert(kHICommandAbout); |
| mergedItems.insert(kHICommandAboutQt); |
| mergedItems.insert(kHICommandCustomMerge); |
| |
| QMenuMergeList *list = 0; |
| if (GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyMergeList, |
| sizeof(list), 0, &list) == noErr && list) { |
| for (int i = 0; i < list->size(); ++i) { |
| MenuCommand command = list->at(i).command; |
| if (command > kHICommandCustomMerge) { |
| mergedItems.insert(command); |
| } |
| } |
| } |
| |
| const int itemCount = CountMenuItems(action->menu); |
| MenuItemAttributes testattr; |
| GetMenuItemAttributes(action->menu, itemCount , &testattr); |
| if (mergedItems.contains(action->command) |
| && (testattr & kMenuItemAttrSeparator)) { |
| InsertMenuItemTextWithCFString(action->menu, 0, qMax(itemCount - 1, 0), attr, action->command); |
| index = itemCount; |
| } else { |
| MenuItemIndex tmpIndex; |
| AppendMenuItemTextWithCFString(action->menu, 0, attr, action->command, &tmpIndex); |
| index = tmpIndex; |
| if (mergedItems.contains(action->command)) |
| AppendMenuItemTextWithCFString(action->menu, 0, kMenuItemAttrSeparator, 0, &tmpIndex); |
| } |
| #else |
| [menu addItem:newItem]; |
| #endif |
| } |
| |
| QWidget *widget = qmenu ? qmenu->widgetItems.value(action->action) : 0; |
| if (widget) { |
| #ifndef QT_MAC_USE_COCOA |
| ChangeMenuAttributes(action->menu, kMenuAttrDoNotCacheImage, 0); |
| attr = kMenuItemAttrCustomDraw; |
| SetMenuItemProperty(action->menu, index, kMenuCreatorQt, kMenuPropertyWidgetActionWidget, |
| sizeof(QWidget *), &widget); |
| HIViewRef content; |
| HIMenuGetContentView(action->menu, kThemeMenuTypePullDown, &content); |
| |
| EventHandlerRef eventHandlerRef; |
| InstallMenuEventHandler(action->menu, qt_mac_widget_in_menu_eventHandler, |
| GetEventTypeCount(widget_in_menu_events), |
| widget_in_menu_events, 0, &eventHandlerRef); |
| menu_eventHandlers_hash()->insert(action->menu, eventHandlerRef); |
| |
| QWidget *menuWidget = 0; |
| GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyWidgetMenu, |
| sizeof(menuWidget), 0, &menuWidget); |
| if(!menuWidget) { |
| menuWidget = new QMacNativeWidget(content); |
| SetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyWidgetMenu, |
| sizeof(menuWidget), &menuWidget); |
| menuWidget->show(); |
| } |
| widget->setParent(menuWidget); |
| #else |
| QMacNativeWidget *container = new QMacNativeWidget(0); |
| container->resize(widget->sizeHint()); |
| widget->setAttribute(Qt::WA_LayoutUsesWidgetRect); |
| widget->setParent(container); |
| |
| NSView *containerView = qt_mac_nativeview_for(container); |
| [containerView setAutoresizesSubviews:YES]; |
| [containerView setAutoresizingMask:NSViewWidthSizable]; |
| [qt_mac_nativeview_for(widget) setAutoresizingMask:NSViewWidthSizable]; |
| |
| [newItem setView:containerView]; |
| container->show(); |
| #endif |
| widget->show(); |
| } |
| |
| } else { |
| #ifndef QT_MAC_USE_COCOA |
| qt_mac_command_set_enabled(action->menu, action->command, !QApplicationPrivate::modalState()); |
| #else |
| [newItem setEnabled:!QApplicationPrivate::modalState()]; |
| #endif |
| } |
| #ifndef QT_MAC_USE_COCOA |
| SetMenuCommandProperty(action->menu, action->command, kMenuCreatorQt, kMenuPropertyQAction, |
| sizeof(action), &action); |
| #else |
| [newItem setTag:long(static_cast<QAction *>(action->action))]; |
| #endif |
| syncAction(action); |
| } |
| |
| // return an autoreleased string given a QKeySequence (currently only looks at the first one). |
| NSString *keySequenceToKeyEqivalent(const QKeySequence &accel) |
| { |
| quint32 accel_key = (accel[0] & ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL)); |
| extern QChar qt_macSymbolForQtKey(int key); // qkeysequence.cpp |
| QChar keyEquiv = qt_macSymbolForQtKey(accel_key); |
| if (keyEquiv.isNull()) { |
| if (accel_key >= Qt::Key_F1 && accel_key <= Qt::Key_F15) |
| keyEquiv = (accel_key - Qt::Key_F1) + NSF1FunctionKey; |
| else |
| keyEquiv = unichar(QChar(accel_key).toLower().unicode()); |
| } |
| return [NSString stringWithCharacters:&keyEquiv.unicode() length:1]; |
| } |
| |
| // return the cocoa modifier mask for the QKeySequence (currently only looks at the first one). |
| NSUInteger keySequenceModifierMask(const QKeySequence &accel) |
| { |
| return constructModifierMask(accel[0]); |
| } |
| |
| void |
| QMenuPrivate::QMacMenuPrivate::syncAction(QMacMenuAction *action) |
| { |
| if (!action) |
| return; |
| |
| #ifndef QT_MAC_USE_COCOA |
| const int index = qt_mac_menu_find_action(action->menu, action); |
| if (index == -1) |
| return; |
| #else |
| NSMenuItem *item = action->menuItem; |
| if (!item) |
| return; |
| #endif |
| |
| #ifndef QT_MAC_USE_COCOA |
| if (!action->action->isVisible()) { |
| ChangeMenuItemAttributes(action->menu, index, kMenuItemAttrHidden, 0); |
| return; |
| } |
| ChangeMenuItemAttributes(action->menu, index, 0, kMenuItemAttrHidden); |
| #else |
| QMacCocoaAutoReleasePool pool; |
| NSMenu *menu = [item menu]; |
| bool actionVisible = action->action->isVisible(); |
| [item setHidden:!actionVisible]; |
| if (!actionVisible) |
| return; |
| #endif |
| |
| #ifndef QT_MAC_USE_COCOA |
| if (action->action->isSeparator()) { |
| ChangeMenuItemAttributes(action->menu, index, kMenuItemAttrSeparator, 0); |
| return; |
| } |
| ChangeMenuItemAttributes(action->menu, index, 0, kMenuItemAttrSeparator); |
| #else |
| int itemIndex = [menu indexOfItem:item]; |
| Q_ASSERT(itemIndex != -1); |
| if (action->action->isSeparator()) { |
| action->menuItem = [NSMenuItem separatorItem]; |
| [action->menuItem retain]; |
| [menu insertItem: action->menuItem atIndex:itemIndex]; |
| [menu removeItem:item]; |
| [item release]; |
| item = action->menuItem; |
| return; |
| } else if ([item isSeparatorItem]) { |
| // I'm no longer a separator... |
| action->menuItem = createNSMenuItem(action->action->text()); |
| [menu insertItem:action->menuItem atIndex:itemIndex]; |
| [menu removeItem:item]; |
| [item release]; |
| item = action->menuItem; |
| } |
| #endif |
| |
| //find text (and accel) |
| action->ignore_accel = 0; |
| QString text = action->action->text(); |
| QKeySequence accel = action->action->shortcut(); |
| { |
| int st = text.lastIndexOf(QLatin1Char('\t')); |
| if (st != -1) { |
| action->ignore_accel = 1; |
| accel = QKeySequence(text.right(text.length()-(st+1))); |
| text.remove(st, text.length()-st); |
| } |
| } |
| { |
| QString cmd_text = qt_mac_menu_merge_text(action); |
| if (!cmd_text.isEmpty()) { |
| text = cmd_text; |
| accel = qt_mac_menu_merge_accel(action); |
| } |
| } |
| // Show multiple key sequences as part of the menu text. |
| if (accel.count() > 1) |
| text += QLatin1String(" (") + accel.toString(QKeySequence::NativeText) + QLatin1String(")"); |
| |
| QString finalString = qt_mac_removeMnemonics(text); |
| |
| #ifndef QT_MAC_USE_COCOA |
| MenuItemDataRec data; |
| memset(&data, '\0', sizeof(data)); |
| |
| //Carbon text |
| data.whichData |= kMenuItemDataCFString; |
| QCFString cfstring(finalString); // Hold the reference to the end of the function. |
| data.cfText = cfstring; |
| |
| // Carbon enabled |
| data.whichData |= kMenuItemDataEnabled; |
| data.enabled = action->action->isEnabled(); |
| // Carbon icon |
| data.whichData |= kMenuItemDataIconHandle; |
| if (!action->action->icon().isNull() |
| && action->action->isIconVisibleInMenu()) { |
| data.iconType = kMenuIconRefType; |
| data.iconHandle = (Handle)qt_mac_create_iconref(action->action->icon().pixmap(16, QIcon::Normal)); |
| } else { |
| data.iconType = kMenuNoIcon; |
| } |
| if (action->action->font().resolve()) { // Carbon font |
| if (action->action->font().bold()) |
| data.style |= bold; |
| if (action->action->font().underline()) |
| data.style |= underline; |
| if (action->action->font().italic()) |
| data.style |= italic; |
| if (data.style) |
| data.whichData |= kMenuItemDataStyle; |
| data.whichData |= kMenuItemDataFontID; |
| data.fontID = action->action->font().macFontID(); |
| } |
| #else |
| // Cocoa Font and title |
| if (action->action->font().resolve()) { |
| const QFont &actionFont = action->action->font(); |
| NSFont *customMenuFont = [NSFont fontWithName:qt_mac_QStringToNSString(actionFont.family()) |
| size:actionFont.pointSize()]; |
| NSArray *keys = [NSArray arrayWithObjects:NSFontAttributeName, nil]; |
| NSArray *objects = [NSArray arrayWithObjects:customMenuFont, nil]; |
| NSDictionary *attributes = [NSDictionary dictionaryWithObjects:objects forKeys:keys]; |
| NSAttributedString *str = [[[NSAttributedString alloc] initWithString:qt_mac_QStringToNSString(finalString) |
| attributes:attributes] autorelease]; |
| [item setAttributedTitle: str]; |
| } else { |
| [item setTitle: qt_mac_QStringToNSString(finalString)]; |
| } |
| |
| if (action->action->menuRole() == QAction::AboutRole || action->action->menuRole() == QAction::QuitRole) |
| [item setTitle:qt_mac_QStringToNSString(text)]; |
| else |
| [item setTitle:qt_mac_QStringToNSString(qt_mac_removeMnemonics(text))]; |
| |
| // Cocoa Enabled |
| [item setEnabled: action->action->isEnabled()]; |
| |
| // Cocoa icon |
| NSImage *nsimage = 0; |
| if (!action->action->icon().isNull() && action->action->isIconVisibleInMenu()) { |
| nsimage = static_cast<NSImage *>(qt_mac_create_nsimage(action->action->icon().pixmap(16, QIcon::Normal))); |
| } |
| [item setImage:nsimage]; |
| [nsimage release]; |
| #endif |
| |
| if (action->action->menu()) { //submenu |
| #ifndef QT_MAC_USE_COCOA |
| data.whichData |= kMenuItemDataSubmenuHandle; |
| data.submenuHandle = action->action->menu()->macMenu(); |
| QWidget *caused = 0; |
| GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(caused), 0, &caused); |
| SetMenuItemProperty(data.submenuHandle, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), &caused); |
| #else |
| NSMenu *subMenu = static_cast<NSMenu *>(action->action->menu()->macMenu()); |
| if ([subMenu supermenu] && [subMenu supermenu] != [item menu]) { |
| // The menu is already a sub-menu of another one. Cocoa will throw an exception, |
| // in such cases. For the time being, a new QMenu with same set of actions is the |
| // only workaround. |
| action->action->setEnabled(false); |
| } else { |
| [item setSubmenu:subMenu]; |
| } |
| #endif |
| } else { //respect some other items |
| #ifndef QT_MAC_USE_COCOA |
| //shortcuts (say we are setting them all so that we can also clear them). |
| data.whichData |= kMenuItemDataCmdKey; |
| data.whichData |= kMenuItemDataCmdKeyModifiers; |
| data.whichData |= kMenuItemDataCmdKeyGlyph; |
| if (accel.count() == 1) { |
| qt_mac_get_accel(accel[0], (quint32*)&data.cmdKeyModifiers, (quint32*)&data.cmdKeyGlyph); |
| if (data.cmdKeyGlyph == 0) |
| data.cmdKey = (UniChar)accel[0]; |
| } |
| #else |
| [item setSubmenu:0]; |
| // No key equivalent set for multiple key QKeySequence. |
| if (accel.count() == 1) { |
| [item setKeyEquivalent:keySequenceToKeyEqivalent(accel)]; |
| [item setKeyEquivalentModifierMask:keySequenceModifierMask(accel)]; |
| } else { |
| [item setKeyEquivalent:@""]; |
| [item setKeyEquivalentModifierMask:NSCommandKeyMask]; |
| } |
| #endif |
| } |
| #ifndef QT_MAC_USE_COCOA |
| //mark glyph |
| data.whichData |= kMenuItemDataMark; |
| if (action->action->isChecked()) { |
| #if 0 |
| if (action->action->actionGroup() && |
| action->action->actionGroup()->isExclusive()) |
| data.mark = diamondMark; |
| else |
| #endif |
| data.mark = checkMark; |
| } else { |
| data.mark = noMark; |
| } |
| |
| //actually set it |
| SetMenuItemData(action->menu, action->command, true, &data); |
| |
| // Free up memory |
| if (data.iconHandle) |
| ReleaseIconRef(IconRef(data.iconHandle)); |
| #else |
| //mark glyph |
| [item setState:action->action->isChecked() ? NSOnState : NSOffState]; |
| #endif |
| } |
| |
| void |
| QMenuPrivate::QMacMenuPrivate::removeAction(QMacMenuAction *action) |
| { |
| if (!action) |
| return; |
| #ifndef QT_MAC_USE_COCOA |
| if (action->command == kHICommandQuit || action->command == kHICommandPreferences) |
| qt_mac_command_set_enabled(action->menu, action->command, false); |
| else |
| DeleteMenuItem(action->menu, qt_mac_menu_find_action(action->menu, action)); |
| #else |
| QMacCocoaAutoReleasePool pool; |
| if (action->merged) { |
| if (reinterpret_cast<QAction *>([action->menuItem tag]) == action->action) { |
| QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader(); |
| [action->menuItem setEnabled:false]; |
| if (action->menuItem != [loader quitMenuItem] |
| && action->menuItem != [loader preferencesMenuItem]) { |
| [[action->menuItem menu] removeItem:action->menuItem]; |
| } |
| } |
| } else { |
| [[action->menuItem menu] removeItem:action->menuItem]; |
| } |
| #endif |
| actionItems.removeAll(action); |
| } |
| |
| OSMenuRef |
| QMenuPrivate::macMenu(OSMenuRef merge) |
| { |
| Q_UNUSED(merge); |
| Q_Q(QMenu); |
| if (mac_menu && mac_menu->menu) |
| return mac_menu->menu; |
| if (!mac_menu) |
| mac_menu = new QMacMenuPrivate; |
| mac_menu->menu = qt_mac_create_menu(q); |
| if (merge) { |
| #ifndef QT_MAC_USE_COCOA |
| SetMenuItemProperty(mac_menu->menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu, sizeof(merge), &merge); |
| #else |
| mergeMenuHash.insert(mac_menu->menu, merge); |
| #endif |
| } |
| QList<QAction*> items = q->actions(); |
| for(int i = 0; i < items.count(); i++) |
| mac_menu->addAction(items[i], 0, this); |
| syncSeparatorsCollapsible(collapsibleSeparators); |
| return mac_menu->menu; |
| } |
| |
| /*! |
| \internal |
| */ |
| void |
| QMenuPrivate::syncSeparatorsCollapsible(bool collapse) |
| { |
| #ifndef QT_MAC_USE_COCOA |
| if (collapse) |
| ChangeMenuAttributes(mac_menu->menu, kMenuAttrCondenseSeparators, 0); |
| else |
| ChangeMenuAttributes(mac_menu->menu, 0, kMenuAttrCondenseSeparators); |
| #else |
| qt_mac_menu_collapseSeparators(mac_menu->menu, collapse); |
| #endif |
| } |
| |
| |
| |
| /*! |
| \internal |
| */ |
| void QMenuPrivate::setMacMenuEnabled(bool enable) |
| { |
| if (!macMenu(0)) |
| return; |
| |
| QMacCocoaAutoReleasePool pool; |
| if (enable) { |
| for (int i = 0; i < mac_menu->actionItems.count(); ++i) { |
| QMacMenuAction *menuItem = mac_menu->actionItems.at(i); |
| if (menuItem && menuItem->action && menuItem->action->isEnabled()) { |
| #ifndef QT_MAC_USE_COCOA |
| // Only enable those items which contains an enabled QAction. |
| // i == 0 -> the menu itself, hence i + 1 for items. |
| EnableMenuItem(mac_menu->menu, i + 1); |
| #else |
| [menuItem->menuItem setEnabled:true]; |
| #endif |
| } |
| } |
| } else { |
| #ifndef QT_MAC_USE_COCOA |
| DisableAllMenuItems(mac_menu->menu); |
| #else |
| NSMenu *menu = mac_menu->menu; |
| for (NSMenuItem *item in [menu itemArray]) { |
| [item setEnabled:false]; |
| } |
| #endif |
| } |
| } |
| |
| /*! |
| \internal |
| |
| This function will return the OSMenuRef used to create the native menu bar |
| bindings. |
| |
| If Qt is built against Carbon, the OSMenuRef is a MenuRef that can be used |
| with Carbon's Menu Manager API. |
| |
| If Qt is built against Cocoa, the OSMenuRef is a NSMenu pointer. |
| |
| \warning This function is not portable. |
| |
| \sa QMenuBar::macMenu() |
| */ |
| OSMenuRef QMenu::macMenu(OSMenuRef merge) { return d_func()->macMenu(merge); } |
| |
| /***************************************************************************** |
| QMenuBar bindings |
| *****************************************************************************/ |
| typedef QHash<QWidget *, QMenuBar *> MenuBarHash; |
| Q_GLOBAL_STATIC(MenuBarHash, menubars) |
| static QMenuBar *fallback = 0; |
| QMenuBarPrivate::QMacMenuBarPrivate::QMacMenuBarPrivate() : menu(0), apple_menu(0) |
| { |
| } |
| |
| QMenuBarPrivate::QMacMenuBarPrivate::~QMacMenuBarPrivate() |
| { |
| for(QList<QMacMenuAction*>::Iterator it = actionItems.begin(); it != actionItems.end(); ++it) |
| delete (*it); |
| #ifndef QT_MAC_USE_COCOA |
| if (apple_menu) { |
| QMenuMergeList *list = 0; |
| GetMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList, |
| sizeof(list), 0, &list); |
| if (list) { |
| RemoveMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList); |
| delete list; |
| } |
| ReleaseMenu(apple_menu); |
| } |
| if (menu) |
| ReleaseMenu(menu); |
| #else |
| [apple_menu release]; |
| [menu release]; |
| #endif |
| } |
| |
| void |
| QMenuBarPrivate::QMacMenuBarPrivate::addAction(QAction *a, QMacMenuAction *before) |
| { |
| if (a->isSeparator() || !menu) |
| return; |
| QMacMenuAction *action = new QMacMenuAction; |
| action->action = a; |
| action->ignore_accel = 1; |
| #ifndef QT_MAC_USE_COCOA |
| action->command = qt_mac_menu_static_cmd_id++; |
| #endif |
| addAction(action, before); |
| } |
| |
| void |
| QMenuBarPrivate::QMacMenuBarPrivate::addAction(QMacMenuAction *action, QMacMenuAction *before) |
| { |
| if (!action || !menu) |
| return; |
| |
| int before_index = actionItems.indexOf(before); |
| if (before_index < 0) { |
| before = 0; |
| before_index = actionItems.size(); |
| } |
| actionItems.insert(before_index, action); |
| |
| MenuItemIndex index = actionItems.size()-1; |
| |
| action->menu = menu; |
| #ifdef QT_MAC_USE_COCOA |
| QMacCocoaAutoReleasePool pool; |
| [action->menu retain]; |
| NSMenuItem *newItem = createNSMenuItem(action->action->text()); |
| action->menuItem = newItem; |
| #endif |
| if (before) { |
| #ifndef QT_MAC_USE_COCOA |
| InsertMenuItemTextWithCFString(action->menu, 0, qMax(1, before_index+1), 0, action->command); |
| #else |
| [menu insertItem:newItem atIndex:qMax(1, before_index + 1)]; |
| #endif |
| index = before_index; |
| } else { |
| #ifndef QT_MAC_USE_COCOA |
| AppendMenuItemTextWithCFString(action->menu, 0, 0, action->command, &index); |
| #else |
| [menu addItem:newItem]; |
| #endif |
| } |
| #ifndef QT_MAC_USE_COCOA |
| SetMenuItemProperty(action->menu, index, kMenuCreatorQt, kMenuPropertyQAction, sizeof(action), |
| &action); |
| #else |
| [newItem setTag:long(static_cast<QAction *>(action->action))]; |
| #endif |
| syncAction(action); |
| } |
| |
| void |
| QMenuBarPrivate::QMacMenuBarPrivate::syncAction(QMacMenuAction *action) |
| { |
| if (!action || !menu) |
| return; |
| #ifndef QT_MAC_USE_COCOA |
| const int index = qt_mac_menu_find_action(action->menu, action); |
| #else |
| QMacCocoaAutoReleasePool pool; |
| NSMenuItem *item = action->menuItem; |
| #endif |
| |
| OSMenuRef submenu = 0; |
| bool release_submenu = false; |
| if (action->action->menu()) { |
| if ((submenu = action->action->menu()->macMenu(apple_menu))) { |
| #ifndef QT_MAC_USE_COCOA |
| QWidget *caused = 0; |
| GetMenuItemProperty(action->menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(caused), 0, &caused); |
| SetMenuItemProperty(submenu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), &caused); |
| #else |
| if ([submenu supermenu] && [submenu supermenu] != [item menu]) |
| return; |
| else |
| [item setSubmenu:submenu]; |
| #endif |
| } |
| #ifndef QT_MAC_USE_COCOA |
| } else { // create a submenu to act as menu |
| release_submenu = true; |
| CreateNewMenu(0, 0, &submenu); |
| #endif |
| } |
| |
| if (submenu) { |
| bool visible = actualMenuItemVisibility(this, action); |
| #ifndef QT_MAC_USE_COCOA |
| SetMenuItemHierarchicalMenu(action->menu, index, submenu); |
| SetMenuTitleWithCFString(submenu, QCFString(qt_mac_removeMnemonics(action->action->text()))); |
| if (visible) |
| ChangeMenuAttributes(submenu, 0, kMenuAttrHidden); |
| else |
| ChangeMenuAttributes(submenu, kMenuAttrHidden, 0); |
| #else |
| [item setSubmenu: submenu]; |
| [submenu setTitle:qt_mac_QStringToNSString(qt_mac_removeMnemonics(action->action->text()))]; |
| syncNSMenuItemVisiblity(item, visible); |
| #endif |
| if (release_submenu) { //no pointers to it |
| #ifndef QT_MAC_USE_COCOA |
| ReleaseMenu(submenu); |
| #else |
| [submenu release]; |
| #endif |
| } |
| } else { |
| qWarning("QMenu: No OSMenuRef created for popup menu"); |
| } |
| } |
| |
| void |
| QMenuBarPrivate::QMacMenuBarPrivate::removeAction(QMacMenuAction *action) |
| { |
| if (!action || !menu) |
| return; |
| #ifndef QT_MAC_USE_COCOA |
| DeleteMenuItem(action->menu, qt_mac_menu_find_action(action->menu, action)); |
| #else |
| QMacCocoaAutoReleasePool pool; |
| [action->menu removeItem:action->menuItem]; |
| #endif |
| actionItems.removeAll(action); |
| } |
| |
| bool QMenuBarPrivate::macWidgetHasNativeMenubar(QWidget *widget) |
| { |
| // This function is different from q->isNativeMenuBar(), as |
| // it returns true only if a native menu bar is actually |
| // _created_. |
| if (!widget) |
| return false; |
| return menubars()->contains(widget->window()); |
| } |
| |
| void |
| QMenuBarPrivate::macCreateMenuBar(QWidget *parent) |
| { |
| Q_Q(QMenuBar); |
| static int dontUseNativeMenuBar = -1; |
| // We call the isNativeMenuBar function here |
| // because that will make sure that local overrides |
| // are dealt with correctly. q->isNativeMenuBar() will, if not |
| // overridden, depend on the attribute Qt::AA_DontUseNativeMenuBar: |
| bool qt_mac_no_native_menubar = !q->isNativeMenuBar(); |
| if (qt_mac_no_native_menubar == false && dontUseNativeMenuBar < 0) { |
| // The menubar is set to be native. Let's check (one time only |
| // for all menubars) if this is OK with the rest of the environment. |
| // As a result, Qt::AA_DontUseNativeMenuBar is set. NB: the application |
| // might still choose to not respect, or change, this flag. |
| bool isPlugin = QApplication::testAttribute(Qt::AA_MacPluginApplication); |
| bool environmentSaysNo = !qgetenv("QT_MAC_NO_NATIVE_MENUBAR").isEmpty(); |
| dontUseNativeMenuBar = isPlugin || environmentSaysNo; |
| QApplication::instance()->setAttribute(Qt::AA_DontUseNativeMenuBar, dontUseNativeMenuBar); |
| qt_mac_no_native_menubar = !q->isNativeMenuBar(); |
| } |
| if (qt_mac_no_native_menubar == false) { |
| // INVARIANT: Use native menubar. |
| extern void qt_event_request_menubarupdate(); //qapplication_mac.cpp |
| qt_event_request_menubarupdate(); |
| if (!parent && !fallback) { |
| fallback = q; |
| mac_menubar = new QMacMenuBarPrivate; |
| } else if (parent && parent->isWindow()) { |
| menubars()->insert(q->window(), q); |
| mac_menubar = new QMacMenuBarPrivate; |
| } |
| } |
| } |
| |
| void QMenuBarPrivate::macDestroyMenuBar() |
| { |
| Q_Q(QMenuBar); |
| QMacCocoaAutoReleasePool pool; |
| if (fallback == q) |
| fallback = 0; |
| delete mac_menubar; |
| QWidget *tlw = q->window(); |
| menubars()->remove(tlw); |
| mac_menubar = 0; |
| |
| if (!qt_mac_current_menubar.qmenubar || qt_mac_current_menubar.qmenubar == q) { |
| #ifdef QT_MAC_USE_COCOA |
| QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader(); |
| [loader removeActionsFromAppMenu]; |
| #else |
| cancelAllMenuTracking(); |
| #endif |
| extern void qt_event_request_menubarupdate(); //qapplication_mac.cpp |
| qt_event_request_menubarupdate(); |
| } |
| } |
| |
| OSMenuRef QMenuBarPrivate::macMenu() |
| { |
| Q_Q(QMenuBar); |
| if (!q->isNativeMenuBar() || !mac_menubar) { |
| return 0; |
| } else if (!mac_menubar->menu) { |
| mac_menubar->menu = qt_mac_create_menu(q); |
| ProcessSerialNumber mine, front; |
| if (GetCurrentProcess(&mine) == noErr && GetFrontProcess(&front) == noErr) { |
| if (!qt_mac_no_menubar_merge && !mac_menubar->apple_menu) { |
| mac_menubar->apple_menu = qt_mac_create_menu(q); |
| #ifndef QT_MAC_USE_COCOA |
| MenuItemIndex index; |
| AppendMenuItemTextWithCFString(mac_menubar->menu, 0, 0, 0, &index); |
| |
| SetMenuTitleWithCFString(mac_menubar->apple_menu, QCFString(QString(QChar(0x14)))); |
| SetMenuItemHierarchicalMenu(mac_menubar->menu, index, mac_menubar->apple_menu); |
| SetMenuItemProperty(mac_menubar->apple_menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(q), &q); |
| #else |
| [mac_menubar->apple_menu setTitle:qt_mac_QStringToNSString(QString(QChar(0x14)))]; |
| NSMenuItem *apple_menuItem = [[NSMenuItem alloc] init]; |
| [apple_menuItem setSubmenu:mac_menubar->menu]; |
| [mac_menubar->apple_menu addItem:apple_menuItem]; |
| [apple_menuItem release]; |
| #endif |
| } |
| if (mac_menubar->apple_menu) { |
| #ifndef QT_MAC_USE_COCOA |
| SetMenuItemProperty(mac_menubar->menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu, |
| sizeof(mac_menubar->apple_menu), &mac_menubar->apple_menu); |
| #else |
| QMenuPrivate::mergeMenuHash.insert(mac_menubar->menu, mac_menubar->apple_menu); |
| #endif |
| } |
| QList<QAction*> items = q->actions(); |
| for(int i = 0; i < items.count(); i++) |
| mac_menubar->addAction(items[i]); |
| } |
| } |
| return mac_menubar->menu; |
| } |
| |
| /*! |
| \internal |
| |
| This function will return the OSMenuRef used to create the native menu bar |
| bindings. This OSMenuRef is then set as the root menu for the Menu |
| Manager. |
| |
| \warning This function is not portable. |
| |
| \sa QMenu::macMenu() |
| */ |
| OSMenuRef QMenuBar::macMenu() { return d_func()->macMenu(); } |
| |
| /* ! |
| \internal |
| Ancestor function that crosses windows (QWidget::isAncestorOf |
| only considers widgets within the same window). |
| */ |
| static bool qt_mac_is_ancestor(QWidget* possibleAncestor, QWidget *child) |
| { |
| if (!possibleAncestor) |
| return false; |
| |
| QWidget * current = child->parentWidget(); |
| while (current != 0) { |
| if (current == possibleAncestor) |
| return true; |
| current = current->parentWidget(); |
| } |
| return false; |
| } |
| |
| /* ! |
| \internal |
| Returns true if the entries of menuBar should be disabled, |
| based on the modality type of modalWidget. |
| */ |
| static bool qt_mac_should_disable_menu(QMenuBar *menuBar) |
| { |
| QWidget *modalWidget = qApp->activeModalWidget(); |
| if (!modalWidget) |
| return false; |
| |
| if (menuBar && menuBar == menubars()->value(modalWidget)) |
| // The menu bar is owned by the modal widget. |
| // In that case we should enable it: |
| return false; |
| |
| // When there is an application modal window on screen, the entries of |
| // the menubar should be disabled. The exception in Qt is that if the |
| // modal window is the only window on screen, then we enable the menu bar. |
| QWidget *w = modalWidget; |
| QWidgetList topLevelWidgets = QApplication::topLevelWidgets(); |
| while (w) { |
| if (w->isVisible() && w->windowModality() == Qt::ApplicationModal) { |
| for (int i=0; i<topLevelWidgets.size(); ++i) { |
| QWidget *top = topLevelWidgets.at(i); |
| if (w != top && top->isVisible()) { |
| // INVARIANT: we found another visible window |
| // on screen other than our modalWidget. We therefore |
| // disable the menu bar to follow normal modality logic: |
| return true; |
| } |
| } |
| // INVARIANT: We have only one window on screen that happends |
| // to be application modal. We choose to enable the menu bar |
| // in that case to e.g. enable the quit menu item. |
| return false; |
| } |
| w = w->parentWidget(); |
| } |
| |
| // INVARIANT: modalWidget is window modal. Disable menu entries |
| // if the menu bar belongs to an ancestor of modalWidget. If menuBar |
| // is nil, we understand it as the default menu bar set by the nib: |
| return menuBar ? qt_mac_is_ancestor(menuBar->parentWidget(), modalWidget) : false; |
| } |
| |
| static QWidget *findWindowThatShouldDisplayMenubar() |
| { |
| QWidget *w = qApp->activeWindow(); |
| if (!w) { |
| // We have no active window on screen. Try to |
| // find a window from the list of top levels: |
| QWidgetList tlws = QApplication::topLevelWidgets(); |
| for(int i = 0; i < tlws.size(); ++i) { |
| QWidget *tlw = tlws.at(i); |
| if ((tlw->isVisible() && tlw->windowType() != Qt::Tool && |
| tlw->windowType() != Qt::Popup)) { |
| w = tlw; |
| break; |
| } |
| } |
| } |
| return w; |
| } |
| |
| static QMenuBar *findMenubarForWindow(QWidget *w) |
| { |
| QMenuBar *mb = 0; |
| if (w) { |
| mb = menubars()->value(w); |
| #ifndef QT_NO_MAINWINDOW |
| QDockWidget *dw = qobject_cast<QDockWidget *>(w); |
| if (!mb && dw) { |
| QMainWindow *mw = qobject_cast<QMainWindow *>(dw->parentWidget()); |
| if (mw && (mb = menubars()->value(mw))) |
| w = mw; |
| } |
| #endif |
| while(w && !mb) |
| mb = menubars()->value((w = w->parentWidget())); |
| } |
| |
| if (!mb) { |
| // We could not find a menu bar for the window. Lets |
| // check if we have a global (parentless) menu bar instead: |
| mb = fallback; |
| } |
| |
| return mb; |
| } |
| |
| void qt_mac_clear_menubar() |
| { |
| if (QApplication::testAttribute(Qt::AA_MacPluginApplication)) |
| return; |
| |
| #ifndef QT_MAC_USE_COCOA |
| MenuRef clear_menu = 0; |
| if (CreateNewMenu(0, 0, &clear_menu) == noErr) { |
| SetRootMenu(clear_menu); |
| ReleaseMenu(clear_menu); |
| } else { |
| qWarning("QMenu: Internal error at %s:%d", __FILE__, __LINE__); |
| } |
| ClearMenuBar(); |
| qt_mac_command_set_enabled(0, kHICommandPreferences, false); |
| InvalMenuBar(); |
| #else |
| QMacCocoaAutoReleasePool pool; |
| QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader(); |
| NSMenu *menu = [loader menu]; |
| [loader ensureAppMenuInMenu:menu]; |
| [NSApp setMainMenu:menu]; |
| const bool modal = qt_mac_should_disable_menu(0); |
| if (qt_mac_current_menubar.qmenubar || modal != qt_mac_current_menubar.modal) |
| qt_mac_set_modal_state(menu, modal); |
| qt_mac_current_menubar.qmenubar = 0; |
| qt_mac_current_menubar.modal = modal; |
| #endif |
| } |
| |
| /*! |
| \internal |
| |
| This function will update the current menu bar and set it as the |
| active menu bar in the Menu Manager. |
| |
| \warning This function is not portable. |
| |
| \sa QMenu::macMenu(), QMenuBar::macMenu() |
| */ |
| bool QMenuBar::macUpdateMenuBar() |
| { |
| #ifdef QT_MAC_USE_COCOA |
| QMacCocoaAutoReleasePool pool; |
| if (!qt_cocoaPostMessage(getMenuLoader(), @selector(qtUpdateMenubar))) |
| return QMenuBarPrivate::macUpdateMenuBarImmediatly(); |
| return true; |
| #else |
| return QMenuBarPrivate::macUpdateMenuBarImmediatly(); |
| #endif |
| } |
| |
| bool QMenuBarPrivate::macUpdateMenuBarImmediatly() |
| { |
| bool ret = false; |
| cancelAllMenuTracking(); |
| QWidget *w = findWindowThatShouldDisplayMenubar(); |
| QMenuBar *mb = findMenubarForWindow(w); |
| extern bool qt_mac_app_fullscreen; //qapplication_mac.mm |
| |
| // We need to see if we are in full screen mode, if so we need to |
| // switch the full screen mode to be able to show or hide the menubar. |
| if(w && mb) { |
| // This case means we are creating a menubar, check if full screen |
| if(w->isFullScreen()) { |
| // Ok, switch to showing the menubar when hovering over it. |
| SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar); |
| qt_mac_app_fullscreen = true; |
| } |
| } else if(w) { |
| // Removing a menubar |
| if(w->isFullScreen()) { |
| // Ok, switch to not showing the menubar when hovering on it |
| SetSystemUIMode(kUIModeAllHidden, 0); |
| qt_mac_app_fullscreen = true; |
| } |
| } |
| |
| if (mb && mb->isNativeMenuBar()) { |
| bool modal = QApplicationPrivate::modalState(); |
| #ifdef QT_MAC_USE_COCOA |
| QMacCocoaAutoReleasePool pool; |
| #endif |
| if (OSMenuRef menu = mb->macMenu()) { |
| #ifndef QT_MAC_USE_COCOA |
| SetRootMenu(menu); |
| #else |
| QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader(); |
| [loader ensureAppMenuInMenu:menu]; |
| [NSApp setMainMenu:menu]; |
| syncMenuBarItemsVisiblity(mb->d_func()->mac_menubar); |
| |
| if (OSMenuRef tmpMerge = QMenuPrivate::mergeMenuHash.value(menu)) { |
| if (QMenuMergeList *mergeList |
| = QMenuPrivate::mergeMenuItemsHash.value(tmpMerge)) { |
| const int mergeListSize = mergeList->size(); |
| |
| for (int i = 0; i < mergeListSize; ++i) { |
| const QMenuMergeItem &mergeItem = mergeList->at(i); |
| // Ideally we would call QMenuPrivate::syncAction, but that requires finding |
| // the original QMen and likely doing more work than we need. |
| // For example, enabled is handled below. |
| [mergeItem.menuItem setTag:reinterpret_cast<long>( |
| static_cast<QAction *>(mergeItem.action->action))]; |
| [mergeItem.menuItem setHidden:!(mergeItem.action->action->isVisible())]; |
| } |
| } |
| } |
| #endif |
| // Check if menu is modally shaddowed and should be disabled: |
| modal = qt_mac_should_disable_menu(mb); |
| if (mb != qt_mac_current_menubar.qmenubar || modal != qt_mac_current_menubar.modal) |
| qt_mac_set_modal_state(menu, modal); |
| } |
| qt_mac_current_menubar.qmenubar = mb; |
| qt_mac_current_menubar.modal = modal; |
| ret = true; |
| } else if (qt_mac_current_menubar.qmenubar && qt_mac_current_menubar.qmenubar->isNativeMenuBar()) { |
| // INVARIANT: The currently active menu bar (if any) is not native. But we do have a |
| // native menu bar from before. So we need to decide whether or not is should be enabled: |
| const bool modal = qt_mac_should_disable_menu(qt_mac_current_menubar.qmenubar); |
| if (modal != qt_mac_current_menubar.modal) { |
| ret = true; |
| if (OSMenuRef menu = qt_mac_current_menubar.qmenubar->macMenu()) { |
| #ifndef QT_MAC_USE_COCOA |
| SetRootMenu(menu); |
| #else |
| QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader(); |
| [loader ensureAppMenuInMenu:menu]; |
| [NSApp setMainMenu:menu]; |
| syncMenuBarItemsVisiblity(qt_mac_current_menubar.qmenubar->d_func()->mac_menubar); |
| #endif |
| qt_mac_set_modal_state(menu, modal); |
| } |
| qt_mac_current_menubar.modal = modal; |
| } |
| } |
| |
| if (!ret) { |
| qt_mac_clear_menubar(); |
| } |
| return ret; |
| } |
| |
| QHash<OSMenuRef, OSMenuRef> QMenuPrivate::mergeMenuHash; |
| QHash<OSMenuRef, QMenuMergeList*> QMenuPrivate::mergeMenuItemsHash; |
| |
| bool QMenuPrivate::QMacMenuPrivate::merged(const QAction *action) const |
| { |
| #ifndef QT_MAC_USE_COCOA |
| MenuRef merge = 0; |
| GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu, |
| sizeof(merge), 0, &merge); |
| if (merge) { |
| QMenuMergeList *list = 0; |
| if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList, |
| sizeof(list), 0, &list) == noErr && list) { |
| for(int i = 0; i < list->size(); ++i) { |
| QMenuMergeItem item = list->at(i); |
| if (item.action->action == action) |
| return true; |
| } |
| } |
| } |
| #else |
| if (OSMenuRef merge = mergeMenuHash.value(menu)) { |
| if (QMenuMergeList *list = mergeMenuItemsHash.value(merge)) { |
| for(int i = 0; i < list->size(); ++i) { |
| const QMenuMergeItem &item = list->at(i); |
| if (item.action->action == action) |
| return true; |
| } |
| } |
| } |
| #endif |
| return false; |
| } |
| |
| //creation of the OSMenuRef |
| static OSMenuRef qt_mac_create_menu(QWidget *w) |
| { |
| OSMenuRef ret; |
| #ifndef QT_MAC_USE_COCOA |
| ret = 0; |
| if (CreateNewMenu(0, 0, &ret) == noErr) { |
| qt_mac_create_menu_event_handler(); |
| SetMenuItemProperty(ret, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(w), &w); |
| |
| // kEventMenuMatchKey is only sent to the menu itself and not to |
| // the application, install a separate handler for that event. |
| EventHandlerRef eventHandlerRef; |
| InstallMenuEventHandler(ret, qt_mac_menu_event, |
| GetEventTypeCount(menu_menu_events), |
| menu_menu_events, 0, &eventHandlerRef); |
| menu_eventHandlers_hash()->insert(ret, eventHandlerRef); |
| } else { |
| qWarning("QMenu: Internal error"); |
| } |
| #else |
| if (QMenu *qmenu = qobject_cast<QMenu *>(w)){ |
| ret = [[QT_MANGLE_NAMESPACE(QCocoaMenu) alloc] initWithQMenu:qmenu]; |
| } else { |
| ret = [[NSMenu alloc] init]; |
| } |
| #endif |
| return ret; |
| } |
| |
| |
| |
| QT_END_NAMESPACE |
| |