diff options
Diffstat (limited to 'src/gui/widgets/qmenu_mac.mm')
-rw-r--r-- | src/gui/widgets/qmenu_mac.mm | 240 |
1 files changed, 138 insertions, 102 deletions
diff --git a/src/gui/widgets/qmenu_mac.mm b/src/gui/widgets/qmenu_mac.mm index 354161d..4fc3d3d 100644 --- a/src/gui/widgets/qmenu_mac.mm +++ b/src/gui/widgets/qmenu_mac.mm @@ -71,7 +71,6 @@ QT_BEGIN_NAMESPACE /***************************************************************************** QMenu globals *****************************************************************************/ -bool qt_mac_no_native_menubar = false; bool qt_mac_no_menubar_merge = false; bool qt_mac_quit_menu_item_enabled = true; int qt_mac_menus_open_count = 0; @@ -143,6 +142,39 @@ static int qt_mac_CountMenuItems(OSMenuRef menu) 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 bool actualMenuItemVisibility(const QMenuBarPrivate::QMacMenuBarPrivate *mbp, const QMacMenuAction *action) { @@ -166,7 +198,7 @@ bool qt_mac_activate_action(MenuRef menu, uint command, QAction::ActionEvent act QMenuMergeList *list = 0; GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeList, sizeof(list), 0, &list); - if (!list && qt_mac_current_menubar.qmenubar) { + 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) @@ -426,7 +458,8 @@ OSStatus qt_mac_menu_event(EventHandlerCallRef er, EventRef event, void *) int merged = 0; const QMenuPrivate::QMacMenuPrivate *mac_menu = qmenu->d_func()->mac_menu; - for(int i = 0; i < mac_menu->actionItems.size(); ++i) { + 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; @@ -526,15 +559,7 @@ static bool qt_mac_auto_apple_menu(MenuCommand cmd) static void qt_mac_get_accel(quint32 accel_key, quint32 *modif, quint32 *key) { if (modif) { - *modif = 0; - if ((accel_key & Qt::CTRL) != Qt::CTRL) - *modif |= kMenuNoCommandModifier; - if ((accel_key & Qt::META) == Qt::META) - *modif |= kMenuControlModifier; - if ((accel_key & Qt::ALT) == Qt::ALT) - *modif |= kMenuOptionModifier; - if ((accel_key & Qt::SHIFT) == Qt::SHIFT) - *modif |= kMenuShiftModifier; + *modif = constructModifierMask(accel_key); } accel_key &= ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL); @@ -582,6 +607,13 @@ static inline void syncNSMenuItemVisiblity(NSMenuItem *menuItem, bool actionVisi [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; @@ -599,7 +631,7 @@ static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader() static NSMenuItem *createNSMenuItem(const QString &title) { NSMenuItem *item = [[NSMenuItem alloc] - initWithTitle:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(title))) + initWithTitle:qt_mac_QStringToNSString(title) action:@selector(qtDispatcherToQAction:) keyEquivalent:@""]; [item setTarget:getMenuLoader()]; return item; @@ -634,12 +666,12 @@ void qt_mac_set_modal_state_helper_recursive(OSMenuRef menu, OSMenuRef merge, bo // The item should follow what the QAction has. if ([item tag]) { QAction *action = reinterpret_cast<QAction *>([item tag]); - [item setEnabled:action->isEnabled()]; + syncNSMenuItemEnabled(item, action->isEnabled()); } else { - [item setEnabled:YES]; + syncNSMenuItemEnabled(item, YES); } } else { - [item setEnabled:NO]; + syncNSMenuItemEnabled(item, NO); } } } @@ -703,6 +735,9 @@ bool qt_mac_menubar_is_open() 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) { @@ -728,6 +763,18 @@ QMacMenuAction::~QMacMenuAction() { #ifdef QT_MAC_USE_COCOA [menu release]; + if (action) { + 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 @@ -915,21 +962,22 @@ static QKeySequence qt_mac_menu_merge_accel(QMacMenuAction *action) ret = action->action->shortcut(); #ifndef QT_MAC_USE_COCOA else if (action->command == kHICommandPreferences) - ret = QKeySequence(Qt::CTRL+Qt::Key_Comma); + ret = QKeySequence(QKeySequence::Preferences); else if (action->command == kHICommandQuit) - ret = QKeySequence(Qt::CTRL+Qt::Key_Q); + ret = QKeySequence(QKeySequence::Quit); #else else if (action->menuItem == [loader preferencesMenuItem]) - ret = QKeySequence(Qt::CTRL+Qt::Key_Comma); + ret = QKeySequence(QKeySequence::Preferences); else if (action->menuItem == [loader quitMenuItem]) - ret = QKeySequence(Qt::CTRL+Qt::Key_Q); + 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) { qt_mac_no_native_menubar = !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; } /***************************************************************************** @@ -1143,7 +1191,7 @@ QMenuPrivate::QMacMenuPrivate::addAction(QMacMenuAction *action, QMacMenuAction #endif } - QWidget *widget = qmenu ? qmenu->widgetItems.value(action->action) : 0; + QWidget *widget = qmenu ? qmenu->widgetItems.value(qmenu->actions.indexOf(action->action)) : 0; if (widget) { #ifndef QT_MAC_USE_COCOA ChangeMenuAttributes(action->menu, kMenuAttrDoNotCacheImage, 0); @@ -1206,58 +1254,21 @@ QMenuPrivate::QMacMenuPrivate::addAction(QMacMenuAction *action, QMacMenuAction NSString *keySequenceToKeyEqivalent(const QKeySequence &accel) { quint32 accel_key = (accel[0] & ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL)); - unichar keyEquiv[1] = { 0 }; - if (accel_key == Qt::Key_Return) - keyEquiv[0] = kReturnCharCode; - else if (accel_key == Qt::Key_Enter) - keyEquiv[0] = kEnterCharCode; - else if (accel_key == Qt::Key_Tab) - keyEquiv[0] = kTabCharCode; - else if (accel_key == Qt::Key_Backspace) - keyEquiv[0] = kBackspaceCharCode; - else if (accel_key == Qt::Key_Delete) - keyEquiv[0] = NSDeleteFunctionKey; - else if (accel_key == Qt::Key_Escape) - keyEquiv[0] = kEscapeCharCode; - else if (accel_key == Qt::Key_PageUp) - keyEquiv[0] = NSPageUpFunctionKey; - else if (accel_key == Qt::Key_PageDown) - keyEquiv[0] = NSPageDownFunctionKey; - else if (accel_key == Qt::Key_Up) - keyEquiv[0] = NSUpArrowFunctionKey; - else if (accel_key == Qt::Key_Down) - keyEquiv[0] = NSDownArrowFunctionKey; - else if (accel_key == Qt::Key_Left) - keyEquiv[0] = NSLeftArrowFunctionKey; - else if (accel_key == Qt::Key_Right) - keyEquiv[0] = NSRightArrowFunctionKey; - else if (accel_key == Qt::Key_CapsLock) - keyEquiv[0] = kMenuCapsLockGlyph; // ### Cocoa has no equivalent - else if (accel_key >= Qt::Key_F1 && accel_key <= Qt::Key_F15) - keyEquiv[0] = (accel_key - Qt::Key_F1) + NSF1FunctionKey; - else if (accel_key == Qt::Key_Home) - keyEquiv[0] = NSHomeFunctionKey; - else if (accel_key == Qt::Key_End) - keyEquiv[0] = NSEndFunctionKey; - else - keyEquiv[0] = unichar(QChar(accel_key).toLower().unicode()); - return [NSString stringWithCharacters:keyEquiv length:1]; + 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) { - NSUInteger ret = 0; - quint32 accel_key = accel[0]; - if ((accel_key & Qt::CTRL) == Qt::CTRL) - ret |= NSCommandKeyMask; - if ((accel_key & Qt::META) == Qt::META) - ret |= NSControlKeyMask; - if ((accel_key & Qt::ALT) == Qt::ALT) - ret |= NSAlternateKeyMask; - if ((accel_key & Qt::SHIFT) == Qt::SHIFT) - ret |= NSShiftKeyMask; - return ret; + return constructModifierMask(accel[0]); } void @@ -1337,8 +1348,9 @@ QMenuPrivate::QMacMenuPrivate::syncAction(QMacMenuAction *action) accel = qt_mac_menu_merge_accel(action); } } + // Show multiple key sequences as part of the menu text. if (accel.count() > 1) - text += QLatin1String(" (****)"); //just to denote a multi stroke shortcut + text += QLatin1String(" (") + accel.toString(QKeySequence::NativeText) + QLatin1String(")"); QString finalString = qt_mac_removeMnemonics(text); @@ -1379,18 +1391,18 @@ QMenuPrivate::QMacMenuPrivate::syncAction(QMacMenuAction *action) // Cocoa Font and title if (action->action->font().resolve()) { const QFont &actionFont = action->action->font(); - NSFont *customMenuFont = [NSFont fontWithName:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(actionFont.family()))) + 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:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(finalString))) + NSAttributedString *str = [[[NSAttributedString alloc] initWithString:qt_mac_QStringToNSString(finalString) attributes:attributes] autorelease]; [item setAttributedTitle: str]; } else { - [item setTitle: reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(finalString)))]; + [item setTitle: qt_mac_QStringToNSString(finalString)]; } - [item setTitle:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(qt_mac_removeMnemonics(text))))]; + [item setTitle:qt_mac_QStringToNSString(qt_mac_removeMnemonics(text))]; // Cocoa Enabled [item setEnabled: action->action->isEnabled()]; @@ -1428,14 +1440,15 @@ QMenuPrivate::QMacMenuPrivate::syncAction(QMacMenuAction *action) data.whichData |= kMenuItemDataCmdKey; data.whichData |= kMenuItemDataCmdKeyModifiers; data.whichData |= kMenuItemDataCmdKeyGlyph; - if (!accel.isEmpty()) { + 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]; - if (!accel.isEmpty()) { + // No key equivalent set for multiple key QKeySequence. + if (accel.count() == 1) { [item setKeyEquivalent:keySequenceToKeyEqivalent(accel)]; [item setKeyEquivalentModifierMask:keySequenceModifierMask(accel)]; } else { @@ -1519,12 +1532,31 @@ QMenuPrivate::macMenu(OSMenuRef merge) 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)) @@ -1710,7 +1742,7 @@ QMenuBarPrivate::QMacMenuBarPrivate::syncAction(QMacMenuAction *action) ChangeMenuAttributes(submenu, kMenuAttrHidden, 0); #else [item setSubmenu: submenu]; - [submenu setTitle:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(qt_mac_removeMnemonics(action->action->text()))))]; + [submenu setTitle:qt_mac_QStringToNSString(qt_mac_removeMnemonics(action->action->text()))]; syncNSMenuItemVisiblity(item, visible); #endif if (release_submenu) { //no pointers to it @@ -1743,10 +1775,17 @@ void QMenuBarPrivate::macCreateMenuBar(QWidget *parent) { Q_Q(QMenuBar); - static int checkEnv = -1; - if (qt_mac_no_native_menubar == false && checkEnv < 0) { - checkEnv = !qgetenv("QT_MAC_NO_NATIVE_MENUBAR").isEmpty(); - qt_mac_no_native_menubar = checkEnv; + static int dontUseNativeMenuBar = -1; + // We call the isNativeMenuBar function here + // because that will make sure that local overrides + // are dealt with correctly. + bool qt_mac_no_native_menubar = !q->isNativeMenuBar(); + if (qt_mac_no_native_menubar == false && dontUseNativeMenuBar < 0) { + 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) { extern void qt_event_request_menubarupdate(); //qapplication_mac.cpp @@ -1781,7 +1820,7 @@ void QMenuBarPrivate::macDestroyMenuBar() OSMenuRef QMenuBarPrivate::macMenu() { Q_Q(QMenuBar); - if (!mac_menubar) { + if (!q->isNativeMenuBar() || !mac_menubar) { return 0; } else if (!mac_menubar->menu) { mac_menubar->menu = qt_mac_create_menu(q); @@ -1797,7 +1836,7 @@ OSMenuRef QMenuBarPrivate::macMenu() 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:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(QString(QChar(0x14)))))]; + [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]; @@ -1840,6 +1879,9 @@ OSMenuRef QMenuBar::macMenu() { return d_func()->macMenu(); } */ static bool qt_mac_is_ancestor(QWidget* possibleAncestor, QWidget *child) { + if (!possibleAncestor) + return false; + QWidget * current = child->parentWidget(); while (current != 0) { if (current == possibleAncestor) @@ -1858,22 +1900,19 @@ static bool qt_mac_should_disable_menu(QMenuBar *menuBar, QWidget *modalWidget) { if (modalWidget == 0 || menuBar == 0) return false; - const Qt::WindowModality modality = modalWidget->windowModality(); - if (modality == Qt::ApplicationModal) { - return true; - } else if (modality == Qt::WindowModal) { - QWidget * parent = menuBar->parentWidget(); - - // Special case for the global menu bar: It's not associated - // with a window so don't disable it. - if (parent == 0) - return false; - // Disable menu entries in menu bars that belong to ancestors of - // the modal widget, leave entries in unrelated menu bars enabled. - return qt_mac_is_ancestor(parent, modalWidget); + // If there is an application modal window on + // screen, the entries of the menubar should be disabled: + QWidget *w = modalWidget; + while (w) { + if (w->isVisible() && w->windowModality() == Qt::ApplicationModal) + return true; + w = w->parentWidget(); } - return false; // modality == NonModal + + // INVARIANT: modalWidget is window modal. Disable menu entries + // if the menu bar belongs to an ancestor of modalWidget: + return qt_mac_is_ancestor(menuBar->parentWidget(), modalWidget); } static void cancelAllMenuTracking() @@ -1902,9 +1941,6 @@ static void cancelAllMenuTracking() */ bool QMenuBar::macUpdateMenuBar() { - if (qt_mac_no_native_menubar) //nothing to be done.. - return true; - cancelAllMenuTracking(); QMenuBar *mb = 0; //find a menu bar @@ -1938,7 +1974,7 @@ bool QMenuBar::macUpdateMenuBar() mb = fallback; //now set it bool ret = false; - if (mb) { + if (mb && mb->isNativeMenuBar()) { #ifdef QT_MAC_USE_COCOA QMacCocoaAutoReleasePool pool; #endif @@ -1976,7 +2012,7 @@ bool QMenuBar::macUpdateMenuBar() qt_mac_current_menubar.qmenubar = mb; qt_mac_current_menubar.modal = QApplicationPrivate::modalState(); ret = true; - } else if (qt_mac_current_menubar.qmenubar) { + } else if (qt_mac_current_menubar.qmenubar && qt_mac_current_menubar.qmenubar->isNativeMenuBar()) { const bool modal = QApplicationPrivate::modalState(); if (modal != qt_mac_current_menubar.modal) { ret = true; |