summaryrefslogtreecommitdiffstats
path: root/src/gui/widgets/qmenu_mac.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/widgets/qmenu_mac.mm')
-rw-r--r--src/gui/widgets/qmenu_mac.mm243
1 files changed, 142 insertions, 101 deletions
diff --git a/src/gui/widgets/qmenu_mac.mm b/src/gui/widgets/qmenu_mac.mm
index 804c4c8..6056119 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)
@@ -526,15 +558,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);
@@ -599,7 +623,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;
@@ -728,6 +752,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,27 +951,28 @@ 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; }
/*****************************************************************************
QMenu bindings
*****************************************************************************/
-QMenuPrivate::QMacMenuPrivate::QMacMenuPrivate() : menu(0)
+QMenuPrivate::QMacMenuPrivate::QMacMenuPrivate(QMenuPrivate *menu) : menu(0), qmenu(menu)
{
}
@@ -1206,58 +1243,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
@@ -1300,22 +1300,61 @@ QMenuPrivate::QMacMenuPrivate::syncAction(QMacMenuAction *action)
#else
int itemIndex = [menu indexOfItem:item];
Q_ASSERT(itemIndex != -1);
- if (action->action->isSeparator()) {
+
+ // Separator handling: Menu items and separators can be added to a QMenu in
+ // any order (for example, add all the separators first and then "fill inn"
+ // the menu items). Create NSMenuItem seperatorItems for the Qt separators,
+ // and make sure that there are no double separators and no seprators
+ // at the top or bottom of the menu.
+ bool itemIsSeparator = action->action->isSeparator();
+ bool previousItemIsSeparator = false;
+ if (itemIndex > 0) {
+ if ([[menu itemAtIndex : itemIndex - 1] isSeparatorItem])
+ previousItemIsSeparator = true;
+ }
+ bool nexItemIsSeparator = false;
+ if (itemIndex > 0 && itemIndex < [menu numberOfItems] -1) {
+ if ([[menu itemAtIndex : itemIndex + 1] isSeparatorItem])
+ nexItemIsSeparator = true;
+ }
+ bool itemIsAtBottomOfMenu = (itemIndex == [menu numberOfItems] - 1);
+ bool itemIsAtTopOfMenu = (itemIndex == 0);
+
+
+ if (itemIsSeparator) {
+ // Create separators items for actions that are now separators
action->menuItem = [NSMenuItem separatorItem];
[action->menuItem retain];
+
+ // Hide duplicate/top/bottom separators.
+ if (qmenu->collapsibleSeparators && (previousItemIsSeparator || itemIsAtBottomOfMenu || itemIsAtTopOfMenu)) {
+ [action->menuItem setHidden : true];
+ }
+
[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;
+ } else {
+ // Create standard menu items for actions that are no longer separators
+ if ([item isSeparatorItem]) {
+ action->menuItem = createNSMenuItem(action->action->text());
+ [menu insertItem:action->menuItem atIndex:itemIndex];
+ [menu removeItem:item];
+ [item release];
+ item = action->menuItem;
+ }
+
+ // Show separators that should now be visible since a non-separator
+ // item (the current item) was added.
+ if (previousItemIsSeparator) {
+ [[menu itemAtIndex : itemIndex - 1] setHidden : false];
+ } else if (itemIsAtTopOfMenu && nexItemIsSeparator) {
+ [[menu itemAtIndex : itemIndex + 1] setHidden : false];
+ }
}
+
#endif
//find text (and accel)
@@ -1379,18 +1418,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()];
@@ -1499,7 +1538,7 @@ QMenuPrivate::macMenu(OSMenuRef merge)
if (mac_menu && mac_menu->menu)
return mac_menu->menu;
if (!mac_menu)
- mac_menu = new QMacMenuPrivate;
+ mac_menu = new QMacMenuPrivate(this);
mac_menu->menu = qt_mac_create_menu(q);
if (merge) {
#ifndef QT_MAC_USE_COCOA
@@ -1699,7 +1738,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
@@ -1733,9 +1772,14 @@ QMenuBarPrivate::macCreateMenuBar(QWidget *parent)
{
Q_Q(QMenuBar);
static int checkEnv = -1;
+ // We call the isNativeMenuBar function here
+ // becasue 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 && checkEnv < 0) {
checkEnv = !qgetenv("QT_MAC_NO_NATIVE_MENUBAR").isEmpty();
- qt_mac_no_native_menubar = checkEnv;
+ QApplication::instance()->setAttribute(Qt::AA_DontUseNativeMenuBar, checkEnv);
+ qt_mac_no_native_menubar = !q->isNativeMenuBar();
}
if (!qt_mac_no_native_menubar) {
extern void qt_event_request_menubarupdate(); //qapplication_mac.cpp
@@ -1770,7 +1814,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);
@@ -1786,7 +1830,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];
@@ -1829,6 +1873,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)
@@ -1847,22 +1894,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()
@@ -1891,9 +1935,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
@@ -1927,7 +1968,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
@@ -1965,7 +2006,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;