source: trunk/src/gui/widgets/qmenu_mac.mm@ 500

Last change on this file since 500 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 72.4 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
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_native_menubar = false;
75bool qt_mac_no_menubar_merge = false;
76bool qt_mac_quit_menu_item_enabled = true;
77int qt_mac_menus_open_count = 0;
78
79static OSMenuRef qt_mac_create_menu(QWidget *w);
80
81#ifndef QT_MAC_USE_COCOA
82static uint qt_mac_menu_static_cmd_id = 'QT00';
83const UInt32 kMenuCreatorQt = 'cute';
84enum {
85 kMenuPropertyQAction = 'QAcT',
86 kMenuPropertyQWidget = 'QWId',
87 kMenuPropertyCausedQWidget = 'QCAU',
88 kMenuPropertyMergeMenu = 'QApP',
89 kMenuPropertyMergeList = 'QAmL',
90 kMenuPropertyWidgetActionWidget = 'QWid',
91 kMenuPropertyWidgetMenu = 'QWMe',
92
93 kHICommandAboutQt = 'AOQT',
94 kHICommandCustomMerge = 'AQt0'
95};
96#endif
97
98static struct {
99 QPointer<QMenuBar> qmenubar;
100 bool modal;
101} qt_mac_current_menubar = { 0, false };
102
103
104
105
106/*****************************************************************************
107 Externals
108 *****************************************************************************/
109extern OSViewRef qt_mac_hiview_for(const QWidget *w); //qwidget_mac.cpp
110extern HIViewRef qt_mac_hiview_for(OSWindowRef w); //qwidget_mac.cpp
111extern IconRef qt_mac_create_iconref(const QPixmap &px); //qpixmap_mac.cpp
112extern QWidget * mac_keyboard_grabber; //qwidget_mac.cpp
113extern bool qt_sendSpontaneousEvent(QObject*, QEvent*); //qapplication_xxx.cpp
114RgnHandle qt_mac_get_rgn(); //qregion_mac.cpp
115void qt_mac_dispose_rgn(RgnHandle r); //qregion_mac.cpp
116
117/*****************************************************************************
118 QMenu utility functions
119 *****************************************************************************/
120bool qt_mac_watchingAboutToShow(QMenu *menu)
121{
122 return menu && menu->receivers(SIGNAL(aboutToShow()));
123}
124
125static int qt_mac_CountMenuItems(OSMenuRef menu)
126{
127 if (menu) {
128#ifndef QT_MAC_USE_COCOA
129 int ret = 0;
130 const int items = CountMenuItems(menu);
131 for(int i = 0; i < items; i++) {
132 MenuItemAttributes attr;
133 if (GetMenuItemAttributes(menu, i+1, &attr) == noErr &&
134 attr & kMenuItemAttrHidden)
135 continue;
136 ++ret;
137 }
138 return ret;
139#else
140 return [menu numberOfItems];
141#endif
142 }
143 return 0;
144}
145
146static bool actualMenuItemVisibility(const QMenuBarPrivate::QMacMenuBarPrivate *mbp,
147 const QMacMenuAction *action)
148{
149 bool visible = action->action->isVisible();
150 if (visible && action->action->text() == QString(QChar(0x14)))
151 return false;
152 if (visible && action->action->menu() && !action->action->menu()->actions().isEmpty() &&
153 !qt_mac_CountMenuItems(action->action->menu()->macMenu(mbp->apple_menu)) &&
154 !qt_mac_watchingAboutToShow(action->action->menu())) {
155 return false;
156 }
157 return visible;
158}
159
160#ifndef QT_MAC_USE_COCOA
161bool qt_mac_activate_action(MenuRef menu, uint command, QAction::ActionEvent action_e, bool by_accel)
162{
163 //fire event
164 QMacMenuAction *action = 0;
165 if (GetMenuCommandProperty(menu, command, kMenuCreatorQt, kMenuPropertyQAction, sizeof(action), 0, &action) != noErr) {
166 QMenuMergeList *list = 0;
167 GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
168 sizeof(list), 0, &list);
169 if (!list && qt_mac_current_menubar.qmenubar) {
170 MenuRef apple_menu = qt_mac_current_menubar.qmenubar->d_func()->mac_menubar->apple_menu;
171 GetMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList, sizeof(list), 0, &list);
172 if (list)
173 menu = apple_menu;
174 }
175 if (list) {
176 for(int i = 0; i < list->size(); ++i) {
177 QMenuMergeItem item = list->at(i);
178 if (item.command == command && item.action) {
179 action = item.action;
180 break;
181 }
182 }
183 }
184 if (!action)
185 return false;
186 }
187
188 if (action_e == QAction::Trigger && by_accel && action->ignore_accel) //no, not a real accel (ie tab)
189 return false;
190
191 // Unhighlight the highlighted menu item before triggering the action to
192 // prevent items from staying highlighted while a modal dialog is shown.
193 // This also fixed the problem that parentless modal dialogs leave
194 // the menu item highlighted (since the menu bar is cleared for these types of dialogs).
195 if (action_e == QAction::Trigger)
196 HiliteMenu(0);
197
198 action->action->activate(action_e);
199
200 //now walk up firing for each "caused" widget (like in the platform independent menu)
201 QWidget *caused = 0;
202 if (GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), 0, &caused) == noErr) {
203 MenuRef caused_menu = 0;
204 if (QMenu *qmenu2 = qobject_cast<QMenu*>(caused))
205 caused_menu = qmenu2->macMenu();
206 else if (QMenuBar *qmenubar2 = qobject_cast<QMenuBar*>(caused))
207 caused_menu = qmenubar2->macMenu();
208 else
209 caused_menu = 0;
210 while(caused_menu) {
211 //fire
212 QWidget *widget = 0;
213 GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(widget), 0, &widget);
214 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
215 if (action_e == QAction::Trigger) {
216 emit qmenu->triggered(action->action);
217 } else if (action_e == QAction::Hover) {
218 action->action->showStatusText(widget);
219 emit qmenu->hovered(action->action);
220 }
221 } else if (QMenuBar *qmenubar = qobject_cast<QMenuBar*>(widget)) {
222 if (action_e == QAction::Trigger) {
223 emit qmenubar->triggered(action->action);
224 } else if (action_e == QAction::Hover) {
225 action->action->showStatusText(widget);
226 emit qmenubar->hovered(action->action);
227 }
228 break; //nothing more..
229 }
230
231 //walk up
232 if (GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget,
233 sizeof(caused), 0, &caused) != noErr)
234 break;
235 if (QMenu *qmenu2 = qobject_cast<QMenu*>(caused))
236 caused_menu = qmenu2->macMenu();
237 else if (QMenuBar *qmenubar2 = qobject_cast<QMenuBar*>(caused))
238 caused_menu = qmenubar2->macMenu();
239 else
240 caused_menu = 0;
241 }
242 }
243 return true;
244}
245
246//lookup a QMacMenuAction in a menu
247static int qt_mac_menu_find_action(MenuRef menu, MenuCommand cmd)
248{
249 MenuItemIndex ret_idx;
250 MenuRef ret_menu;
251 if (GetIndMenuItemWithCommandID(menu, cmd, 1, &ret_menu, &ret_idx) == noErr) {
252 if (ret_menu == menu)
253 return (int)ret_idx;
254 }
255 return -1;
256}
257static int qt_mac_menu_find_action(MenuRef menu, QMacMenuAction *action)
258{
259 return qt_mac_menu_find_action(menu, action->command);
260}
261
262typedef QMultiHash<OSMenuRef, EventHandlerRef> EventHandlerHash;
263Q_GLOBAL_STATIC(EventHandlerHash, menu_eventHandlers_hash)
264
265static EventTypeSpec widget_in_menu_events[] = {
266 { kEventClassMenu, kEventMenuMeasureItemWidth },
267 { kEventClassMenu, kEventMenuMeasureItemHeight },
268 { kEventClassMenu, kEventMenuDrawItem },
269 { kEventClassMenu, kEventMenuCalculateSize }
270};
271
272static OSStatus qt_mac_widget_in_menu_eventHandler(EventHandlerCallRef er, EventRef event, void *)
273{
274 UInt32 ekind = GetEventKind(event);
275 UInt32 eclass = GetEventClass(event);
276 OSStatus result = eventNotHandledErr;
277 switch (eclass) {
278 case kEventClassMenu:
279 switch (ekind) {
280 default:
281 break;
282 case kEventMenuMeasureItemWidth: {
283 MenuItemIndex item;
284 GetEventParameter(event, kEventParamMenuItemIndex, typeMenuItemIndex,
285 0, sizeof(item), 0, &item);
286 OSMenuRef menu;
287 GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
288 QWidget *widget;
289 if (GetMenuItemProperty(menu, item, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
290 sizeof(widget), 0, &widget) == noErr) {
291 short width = short(widget->sizeHint().width());
292 SetEventParameter(event, kEventParamMenuItemWidth, typeSInt16,
293 sizeof(short), &width);
294 result = noErr;
295 }
296 break; }
297 case kEventMenuMeasureItemHeight: {
298 MenuItemIndex item;
299 GetEventParameter(event, kEventParamMenuItemIndex, typeMenuItemIndex,
300 0, sizeof(item), 0, &item);
301 OSMenuRef menu;
302 GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
303 QWidget *widget;
304 if (GetMenuItemProperty(menu, item, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
305 sizeof(widget), 0, &widget) == noErr && widget) {
306 short height = short(widget->sizeHint().height());
307 SetEventParameter(event, kEventParamMenuItemHeight, typeSInt16,
308 sizeof(short), &height);
309 result = noErr;
310 }
311 break; }
312 case kEventMenuDrawItem:
313 result = noErr;
314 break;
315 case kEventMenuCalculateSize: {
316 result = CallNextEventHandler(er, event);
317 if (result == noErr) {
318 OSMenuRef menu;
319 GetEventParameter(event, kEventParamDirectObject, typeMenuRef, 0, sizeof(menu), 0, &menu);
320 HIViewRef content;
321 HIMenuGetContentView(menu, kThemeMenuTypePullDown, &content);
322 UInt16 count = CountMenuItems(menu);
323 for (MenuItemIndex i = 1; i <= count; ++i) {
324 QWidget *widget;
325 if (GetMenuItemProperty(menu, i, kMenuCreatorQt, kMenuPropertyWidgetActionWidget,
326 sizeof(widget), 0, &widget) == noErr && widget) {
327 RgnHandle itemRgn = qt_mac_get_rgn();
328 GetControlRegion(content, i, itemRgn);
329
330 Rect bounds;
331 GetRegionBounds( itemRgn, &bounds );
332 qt_mac_dispose_rgn(itemRgn);
333 widget->setGeometry(bounds.left, bounds.top,
334 bounds.right - bounds.left, bounds.bottom - bounds.top);
335 }
336 }
337 }
338 break; }
339 }
340 }
341 return result;
342}
343
344//handling of events for menurefs created by Qt..
345static EventTypeSpec menu_events[] = {
346 { kEventClassCommand, kEventCommandProcess },
347 { kEventClassMenu, kEventMenuTargetItem },
348 { kEventClassMenu, kEventMenuOpening },
349 { kEventClassMenu, kEventMenuClosed }
350};
351
352// Special case for kEventMenuMatchKey, see qt_mac_create_menu below.
353static EventTypeSpec menu_menu_events[] = {
354 { kEventClassMenu, kEventMenuMatchKey }
355};
356
357OSStatus qt_mac_menu_event(EventHandlerCallRef er, EventRef event, void *)
358{
359 QScopedLoopLevelCounter loopLevelCounter(QApplicationPrivate::instance()->threadData);
360
361 bool handled_event = true;
362 UInt32 ekind = GetEventKind(event), eclass = GetEventClass(event);
363 switch(eclass) {
364 case kEventClassCommand:
365 if (ekind == kEventCommandProcess) {
366 UInt32 context;
367 GetEventParameter(event, kEventParamMenuContext, typeUInt32,
368 0, sizeof(context), 0, &context);
369 HICommand cmd;
370 GetEventParameter(event, kEventParamDirectObject, typeHICommand,
371 0, sizeof(cmd), 0, &cmd);
372 if (!mac_keyboard_grabber && (context & kMenuContextKeyMatching)) {
373 QMacMenuAction *action = 0;
374 if (GetMenuCommandProperty(cmd.menu.menuRef, cmd.commandID, kMenuCreatorQt,
375 kMenuPropertyQAction, sizeof(action), 0, &action) == noErr) {
376 QWidget *widget = 0;
377 if (qApp->activePopupWidget())
378 widget = (qApp->activePopupWidget()->focusWidget() ?
379 qApp->activePopupWidget()->focusWidget() : qApp->activePopupWidget());
380 else if (QApplicationPrivate::focus_widget)
381 widget = QApplicationPrivate::focus_widget;
382 if (widget) {
383 int key = action->action->shortcut();
384 QKeyEvent accel_ev(QEvent::ShortcutOverride, (key & (~Qt::KeyboardModifierMask)),
385 Qt::KeyboardModifiers(key & Qt::KeyboardModifierMask));
386 accel_ev.ignore();
387 qt_sendSpontaneousEvent(widget, &accel_ev);
388 if (accel_ev.isAccepted()) {
389 handled_event = false;
390 break;
391 }
392 }
393 }
394 }
395 handled_event = qt_mac_activate_action(cmd.menu.menuRef, cmd.commandID,
396 QAction::Trigger, context & kMenuContextKeyMatching);
397 }
398 break;
399 case kEventClassMenu: {
400 MenuRef menu;
401 GetEventParameter(event, kEventParamDirectObject, typeMenuRef, NULL, sizeof(menu), NULL, &menu);
402 if (ekind == kEventMenuMatchKey) {
403 // Don't activate any actions if we are showing a native modal dialog,
404 // the key events should go to the dialog in this case.
405 if (QApplicationPrivate::native_modal_dialog_active)
406 return menuItemNotFoundErr;
407
408 handled_event = false;
409 } else if (ekind == kEventMenuTargetItem) {
410 MenuCommand command;
411 GetEventParameter(event, kEventParamMenuCommand, typeMenuCommand,
412 0, sizeof(command), 0, &command);
413 handled_event = qt_mac_activate_action(menu, command, QAction::Hover, false);
414 } else if (ekind == kEventMenuOpening || ekind == kEventMenuClosed) {
415 qt_mac_menus_open_count += (ekind == kEventMenuOpening) ? 1 : -1;
416 MenuRef mr;
417 GetEventParameter(event, kEventParamDirectObject, typeMenuRef,
418 0, sizeof(mr), 0, &mr);
419
420 QWidget *widget = 0;
421 if (GetMenuItemProperty(mr, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(widget), 0, &widget) == noErr) {
422 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
423 handled_event = true;
424 if (ekind == kEventMenuOpening) {
425 emit qmenu->aboutToShow();
426
427 int merged = 0;
428 const QMenuPrivate::QMacMenuPrivate *mac_menu = qmenu->d_func()->mac_menu;
429 for(int i = 0; i < mac_menu->actionItems.size(); ++i) {
430 QMacMenuAction *action = mac_menu->actionItems.at(i);
431 if (action->action->isSeparator()) {
432 bool hide = false;
433 if(!action->action->isVisible()) {
434 hide = true;
435 } else if (merged && merged == i) {
436 hide = true;
437 } else {
438 for(int l = i+1; l < mac_menu->actionItems.size(); ++l) {
439 QMacMenuAction *action = mac_menu->actionItems.at(l);
440 if (action->merged) {
441 hide = true;
442 } else if (action->action->isSeparator()) {
443 if (hide)
444 break;
445 } else if (!action->merged) {
446 hide = false;
447 break;
448 }
449 }
450 }
451
452 const int index = qt_mac_menu_find_action(mr, action);
453 if (hide) {
454 ++merged;
455 ChangeMenuItemAttributes(mr, index, kMenuItemAttrHidden, 0);
456 } else {
457 ChangeMenuItemAttributes(mr, index, 0, kMenuItemAttrHidden);
458 }
459 } else if (action->merged) {
460 ++merged;
461 }
462 }
463 } else {
464 emit qmenu->aboutToHide();
465 }
466 }
467 }
468 } else {
469 handled_event = false;
470 }
471 break; }
472 default:
473 handled_event = false;
474 break;
475 }
476 if (!handled_event) //let the event go through
477 return CallNextEventHandler(er, event);
478 return noErr; //we eat the event
479}
480static EventHandlerRef mac_menu_event_handler = 0;
481static EventHandlerUPP mac_menu_eventUPP = 0;
482static void qt_mac_cleanup_menu_event()
483{
484 if (mac_menu_event_handler) {
485 RemoveEventHandler(mac_menu_event_handler);
486 mac_menu_event_handler = 0;
487 }
488 if (mac_menu_eventUPP) {
489 DisposeEventHandlerUPP(mac_menu_eventUPP);
490 mac_menu_eventUPP = 0;
491 }
492}
493static inline void qt_mac_create_menu_event_handler()
494{
495 if (!mac_menu_event_handler) {
496 mac_menu_eventUPP = NewEventHandlerUPP(qt_mac_menu_event);
497 InstallEventHandler(GetApplicationEventTarget(), mac_menu_eventUPP,
498 GetEventTypeCount(menu_events), menu_events, 0,
499 &mac_menu_event_handler);
500 qAddPostRoutine(qt_mac_cleanup_menu_event);
501 }
502}
503
504
505//enabling of commands
506static void qt_mac_command_set_enabled(MenuRef menu, UInt32 cmd, bool b)
507{
508 if (cmd == kHICommandQuit)
509 qt_mac_quit_menu_item_enabled = b;
510
511 if (b) {
512 EnableMenuCommand(menu, cmd);
513 if (MenuRef dock_menu = GetApplicationDockTileMenu())
514 EnableMenuCommand(dock_menu, cmd);
515 } else {
516 DisableMenuCommand(menu, cmd);
517 if (MenuRef dock_menu = GetApplicationDockTileMenu())
518 DisableMenuCommand(dock_menu, cmd);
519 }
520}
521
522static bool qt_mac_auto_apple_menu(MenuCommand cmd)
523{
524 return (cmd == kHICommandPreferences || cmd == kHICommandQuit);
525}
526
527static void qt_mac_get_accel(quint32 accel_key, quint32 *modif, quint32 *key) {
528 if (modif) {
529 *modif = 0;
530 if ((accel_key & Qt::CTRL) != Qt::CTRL)
531 *modif |= kMenuNoCommandModifier;
532 if ((accel_key & Qt::META) == Qt::META)
533 *modif |= kMenuControlModifier;
534 if ((accel_key & Qt::ALT) == Qt::ALT)
535 *modif |= kMenuOptionModifier;
536 if ((accel_key & Qt::SHIFT) == Qt::SHIFT)
537 *modif |= kMenuShiftModifier;
538 }
539
540 accel_key &= ~(Qt::MODIFIER_MASK | Qt::UNICODE_ACCEL);
541 if (key) {
542 *key = 0;
543 if (accel_key == Qt::Key_Return)
544 *key = kMenuReturnGlyph;
545 else if (accel_key == Qt::Key_Enter)
546 *key = kMenuEnterGlyph;
547 else if (accel_key == Qt::Key_Tab)
548 *key = kMenuTabRightGlyph;
549 else if (accel_key == Qt::Key_Backspace)
550 *key = kMenuDeleteLeftGlyph;
551 else if (accel_key == Qt::Key_Delete)
552 *key = kMenuDeleteRightGlyph;
553 else if (accel_key == Qt::Key_Escape)
554 *key = kMenuEscapeGlyph;
555 else if (accel_key == Qt::Key_PageUp)
556 *key = kMenuPageUpGlyph;
557 else if (accel_key == Qt::Key_PageDown)
558 *key = kMenuPageDownGlyph;
559 else if (accel_key == Qt::Key_Up)
560 *key = kMenuUpArrowGlyph;
561 else if (accel_key == Qt::Key_Down)
562 *key = kMenuDownArrowGlyph;
563 else if (accel_key == Qt::Key_Left)
564 *key = kMenuLeftArrowGlyph;
565 else if (accel_key == Qt::Key_Right)
566 *key = kMenuRightArrowGlyph;
567 else if (accel_key == Qt::Key_CapsLock)
568 *key = kMenuCapsLockGlyph;
569 else if (accel_key >= Qt::Key_F1 && accel_key <= Qt::Key_F15)
570 *key = (accel_key - Qt::Key_F1) + kMenuF1Glyph;
571 else if (accel_key == Qt::Key_Home)
572 *key = kMenuNorthwestArrowGlyph;
573 else if (accel_key == Qt::Key_End)
574 *key = kMenuSoutheastArrowGlyph;
575 }
576}
577#else // Cocoa
578static inline void syncNSMenuItemVisiblity(NSMenuItem *menuItem, bool actionVisibility)
579{
580 [menuItem setHidden:NO];
581 [menuItem setHidden:YES];
582 [menuItem setHidden:!actionVisibility];
583}
584
585static inline void syncMenuBarItemsVisiblity(const QMenuBarPrivate::QMacMenuBarPrivate *mac_menubar)
586{
587 const QList<QMacMenuAction *> &menubarActions = mac_menubar->actionItems;
588 for (int i = 0; i < menubarActions.size(); ++i) {
589 const QMacMenuAction *action = menubarActions.at(i);
590 syncNSMenuItemVisiblity(action->menuItem, actualMenuItemVisibility(mac_menubar, action));
591 }
592}
593
594static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader()
595{
596 return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
597}
598
599static NSMenuItem *createNSMenuItem(const QString &title)
600{
601 NSMenuItem *item = [[NSMenuItem alloc]
602 initWithTitle:reinterpret_cast<const NSString *>(static_cast<CFStringRef>(QCFString(title)))
603 action:@selector(qtDispatcherToQAction:) keyEquivalent:@""];
604 [item setTarget:getMenuLoader()];
605 return item;
606}
607#endif
608
609
610
611// helper that recurses into a menu structure and en/dis-ables them
612void qt_mac_set_modal_state_helper_recursive(OSMenuRef menu, OSMenuRef merge, bool on)
613{
614#ifndef QT_MAC_USE_COCOA
615 for (int i = 0; i < CountMenuItems(menu); i++) {
616 OSMenuRef submenu;
617 GetMenuItemHierarchicalMenu(menu, i+1, &submenu);
618 if (submenu != merge) {
619 if (submenu)
620 qt_mac_set_modal_state_helper_recursive(submenu, merge, on);
621 if (on)
622 DisableMenuItem(submenu, 0);
623 else
624 EnableMenuItem(submenu, 0);
625 }
626 }
627#else
628 for (NSMenuItem *item in [menu itemArray]) {
629 OSMenuRef submenu = [item submenu];
630 if (submenu != merge) {
631 if (submenu)
632 qt_mac_set_modal_state_helper_recursive(submenu, merge, on);
633 if (!on) {
634 // The item should follow what the QAction has.
635 if ([item tag]) {
636 QAction *action = reinterpret_cast<QAction *>([item tag]);
637 [item setEnabled:action->isEnabled()];
638 } else {
639 [item setEnabled:YES];
640 }
641 } else {
642 [item setEnabled:NO];
643 }
644 }
645 }
646#endif
647}
648
649//toggling of modal state
650static void qt_mac_set_modal_state(OSMenuRef menu, bool on)
651{
652#ifndef QT_MAC_USE_COCOA
653 OSMenuRef merge = 0;
654 GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeMenu,
655 sizeof(merge), 0, &merge);
656
657 qt_mac_set_modal_state_helper_recursive(menu, merge, on);
658
659 UInt32 commands[] = { kHICommandQuit, kHICommandPreferences, kHICommandAbout, kHICommandAboutQt, 0 };
660 for(int c = 0; commands[c]; c++) {
661 bool enabled = !on;
662 if (enabled) {
663 QMacMenuAction *action = 0;
664 GetMenuCommandProperty(menu, commands[c], kMenuCreatorQt, kMenuPropertyQAction,
665 sizeof(action), 0, &action);
666 if (!action && merge) {
667 GetMenuCommandProperty(merge, commands[c], kMenuCreatorQt, kMenuPropertyQAction,
668 sizeof(action), 0, &action);
669 if (!action) {
670 QMenuMergeList *list = 0;
671 GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
672 sizeof(list), 0, &list);
673 for(int i = 0; list && i < list->size(); ++i) {
674 QMenuMergeItem item = list->at(i);
675 if (item.command == commands[c] && item.action) {
676 action = item.action;
677 break;
678 }
679 }
680 }
681 }
682
683 if (!action) {
684 if (commands[c] != kHICommandQuit)
685 enabled = false;
686 } else {
687 enabled = action->action ? action->action->isEnabled() : 0;
688 }
689 }
690 qt_mac_command_set_enabled(menu, commands[c], enabled);
691 }
692#else
693 OSMenuRef merge = QMenuPrivate::mergeMenuHash.value(menu);
694 qt_mac_set_modal_state_helper_recursive(menu, merge, on);
695 // I'm ignoring the special items now, since they should get handled via a syncAction()
696#endif
697}
698
699bool qt_mac_menubar_is_open()
700{
701 return qt_mac_menus_open_count > 0;
702}
703
704void qt_mac_clear_menubar()
705{
706#ifndef QT_MAC_USE_COCOA
707 MenuRef clear_menu = 0;
708 if (CreateNewMenu(0, 0, &clear_menu) == noErr) {
709 SetRootMenu(clear_menu);
710 ReleaseMenu(clear_menu);
711 } else {
712 qWarning("QMenu: Internal error at %s:%d", __FILE__, __LINE__);
713 }
714 ClearMenuBar();
715 qt_mac_command_set_enabled(0, kHICommandPreferences, false);
716 InvalMenuBar();
717#else
718 QMacCocoaAutoReleasePool pool;
719 QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
720 NSMenu *menu = [loader menu];
721 [loader ensureAppMenuInMenu:menu];
722 [NSApp setMainMenu:menu];
723#endif
724}
725
726
727QMacMenuAction::~QMacMenuAction()
728{
729#ifdef QT_MAC_USE_COCOA
730 [menu release];
731 [menuItem setTag:nil];
732 [menuItem release];
733#endif
734}
735
736#ifndef QT_MAC_USE_COCOA
737static MenuCommand qt_mac_menu_merge_action(MenuRef merge, QMacMenuAction *action)
738#else
739static NSMenuItem *qt_mac_menu_merge_action(OSMenuRef merge, QMacMenuAction *action)
740#endif
741{
742 if (qt_mac_no_menubar_merge || action->action->menu() || action->action->isSeparator()
743 || action->action->menuRole() == QAction::NoRole)
744 return 0;
745
746 QString t = qt_mac_removeMnemonics(action->action->text().toLower());
747 int st = t.lastIndexOf(QLatin1Char('\t'));
748 if (st != -1)
749 t.remove(st, t.length()-st);
750 t.replace(QRegExp(QString::fromLatin1("\\.*$")), QLatin1String("")); //no ellipses
751 //now the fun part
752#ifndef QT_MAC_USE_COCOA
753 MenuCommand ret = 0;
754#else
755 NSMenuItem *ret = 0;
756 QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
757#endif
758 switch (action->action->menuRole()) {
759 case QAction::NoRole:
760 ret = 0;
761 break;
762 case QAction::ApplicationSpecificRole:
763#ifndef QT_MAC_USE_COCOA
764 {
765 QMenuMergeList *list = 0;
766 if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
767 sizeof(list), 0, &list) == noErr && list) {
768 MenuCommand lastCustom = kHICommandCustomMerge;
769 for(int i = 0; i < list->size(); ++i) {
770 QMenuMergeItem item = list->at(i);
771 if (item.command == lastCustom)
772 ++lastCustom;
773 }
774 ret = lastCustom;
775 } else {
776 // The list hasn't been created, so, must be the first one.
777 ret = kHICommandCustomMerge;
778 }
779 }
780#else
781 ret = [loader appSpecificMenuItem];
782#endif
783 break;
784 case QAction::AboutRole:
785#ifndef QT_MAC_USE_COCOA
786 ret = kHICommandAbout;
787#else
788 ret = [loader aboutMenuItem];
789#endif
790 break;
791 case QAction::AboutQtRole:
792#ifndef QT_MAC_USE_COCOA
793 ret = kHICommandAboutQt;
794#else
795 ret = [loader aboutQtMenuItem];
796#endif
797 break;
798 case QAction::QuitRole:
799#ifndef QT_MAC_USE_COCOA
800 ret = kHICommandQuit;
801#else
802 ret = [loader quitMenuItem];
803#endif
804 break;
805 case QAction::PreferencesRole:
806#ifndef QT_MAC_USE_COCOA
807 ret = kHICommandPreferences;
808#else
809 ret = [loader preferencesMenuItem];
810#endif
811 break;
812 case QAction::TextHeuristicRole: {
813 QString aboutString = QMenuBar::tr("About").toLower();
814 if (t.startsWith(aboutString) || t.endsWith(aboutString)) {
815 if (t.indexOf(QRegExp(QString::fromLatin1("qt$"), Qt::CaseInsensitive)) == -1) {
816#ifndef QT_MAC_USE_COCOA
817 ret = kHICommandAbout;
818#else
819 ret = [loader aboutMenuItem];
820#endif
821 } else {
822#ifndef QT_MAC_USE_COCOA
823 ret = kHICommandAboutQt;
824#else
825 ret = [loader aboutQtMenuItem];
826#endif
827 }
828 } else if (t.startsWith(QMenuBar::tr("Config").toLower())
829 || t.startsWith(QMenuBar::tr("Preference").toLower())
830 || t.startsWith(QMenuBar::tr("Options").toLower())
831 || t.startsWith(QMenuBar::tr("Setting").toLower())
832 || t.startsWith(QMenuBar::tr("Setup").toLower())) {
833#ifndef QT_MAC_USE_COCOA
834 ret = kHICommandPreferences;
835#else
836 ret = [loader preferencesMenuItem];
837#endif
838 } else if (t.startsWith(QMenuBar::tr("Quit").toLower())
839 || t.startsWith(QMenuBar::tr("Exit").toLower())) {
840#ifndef QT_MAC_USE_COCOA
841 ret = kHICommandQuit;
842#else
843 ret = [loader quitMenuItem];
844#endif
845 }
846 }
847 break;
848 }
849
850#ifndef QT_MAC_USE_COCOA
851 QMenuMergeList *list = 0;
852 if (GetMenuItemProperty(merge, 0, kMenuCreatorQt, kMenuPropertyMergeList,
853 sizeof(list), 0, &list) == noErr && list) {
854 for(int i = 0; i < list->size(); ++i) {
855 QMenuMergeItem item = list->at(i);
856 if (item.command == ret && item.action)
857 return 0;
858 }
859 }
860
861 QAction *cmd_action = 0;
862 if (GetMenuCommandProperty(merge, ret, kMenuCreatorQt, kMenuPropertyQAction,
863 sizeof(cmd_action), 0, &cmd_action) == noErr && cmd_action)
864 return 0; //already taken
865#else
866 if (QMenuMergeList *list = QMenuPrivate::mergeMenuItemsHash.value(merge)) {
867 for(int i = 0; i < list->size(); ++i) {
868 const QMenuMergeItem &item = list->at(i);
869 if (item.menuItem == ret && item.action)
870 return 0;
871 }
872 }
873
874 if ([ret tag] != 0)
875 ret = 0; // already taken
876#endif
877 return ret;
878}
879
880static QString qt_mac_menu_merge_text(QMacMenuAction *action)
881{
882 QString ret;
883#ifdef QT_MAC_USE_COCOA
884 QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
885#endif
886 if (action->action->menuRole() == QAction::ApplicationSpecificRole)
887 ret = action->action->text();
888#ifndef QT_MAC_USE_COCOA
889 else if (action->command == kHICommandAbout)
890 ret = QMenuBar::tr("About %1").arg(qAppName());
891 else if (action->command == kHICommandAboutQt)
892 ret = QMenuBar::tr("About Qt");
893 else if (action->command == kHICommandPreferences)
894 ret = QMenuBar::tr("Preferences");
895 else if (action->command == kHICommandQuit)
896 ret = QMenuBar::tr("Quit %1").arg(qAppName());
897#else
898 else if (action->menuItem == [loader aboutMenuItem])
899 ret = QMenuBar::tr("About %1").arg(qAppName());
900 else if (action->menuItem == [loader aboutQtMenuItem])
901 ret = QMenuBar::tr("About Qt");
902 else if (action->menuItem == [loader preferencesMenuItem])
903 ret = QMenuBar::tr("Preferences");
904 else if (action->menuItem == [loader quitMenuItem])
905 ret = QMenuBar::tr("Quit %1").arg(qAppName());
906#endif
907 return ret;
908}
909
910static QKeySequence qt_mac_menu_merge_accel(QMacMenuAction *action)
911{
912 QKeySequence ret;
913#ifdef QT_MAC_USE_COCOA
914 QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
915#endif
916 if (action->action->menuRole() == QAction::ApplicationSpecificRole)
917 ret = action->action->shortcut();
918#ifndef QT_MAC_USE_COCOA
919 else if (action->command == kHICommandPreferences)
920 ret = QKeySequence(Qt::CTRL+Qt::Key_Comma);
921 else if (action->command == kHICommandQuit)
922 ret = QKeySequence(Qt::CTRL+Qt::Key_Q);
923#else
924 else if (action->menuItem == [loader preferencesMenuItem])
925 ret = QKeySequence(Qt::CTRL+Qt::Key_Comma);
926 else if (action->menuItem == [loader quitMenuItem])
927 ret = QKeySequence(Qt::CTRL+Qt::Key_Q);
928#endif
929 return ret;
930}
931
932void Q_GUI_EXPORT qt_mac_set_menubar_icons(bool b)
933{ QApplication::instance()->setAttribute(Qt::AA_DontShowIconsInMenus, !b); }
934void Q_GUI_EXPORT qt_mac_set_native_menubar(bool b) { qt_mac_no_native_menubar = !b; }
935void Q_GUI_EXPORT qt_mac_set_menubar_merge(bool b) { qt_mac_no_menubar_merge = !b; }
936
937/*****************************************************************************