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