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

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

trunk: Merged in qt 4.6.2 sources.

File size: 75.9 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this 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 have questions regarding the use of this file, please contact
37** Nokia 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_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 bool actualMenuItemVisibility(const QMenuBarPrivate::QMacMenuBarPrivate *mbp,
179 const QMacMenuAction *action)
180{
181 bool visible = action->action->isVisible();
182 if (visible && action->action->text() == QString(QChar(0x14)))
183 return false;
184 if (visible && action->action->menu() && !action->action->menu()->actions().isEmpty() &&
185 !qt_mac_CountMenuItems(action->action->menu()->macMenu(mbp->apple_menu)) &&
186 !qt_mac_watchingAboutToShow(action->action->menu())) {
187 return false;
188 }
189 return visible;
190}
191
192#ifndef QT_MAC_USE_COCOA
193bool qt_mac_activate_action(MenuRef menu, uint command, QAction::ActionEvent action_e, bool by_accel)
194{
195 //fire event
196 QMacMenuAction *action = 0;
197 if (GetMenuCommandProperty(menu, command, kMenuCreatorQt, kMenuPropertyQAction, sizeof(action), 0, &action) != noErr) {
198 QMenuMergeList *list = 0;
199 GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyMergeList,
200 sizeof(list), 0, &list);
201 if (!list && qt_mac_current_menubar.qmenubar && qt_mac_current_menubar.qmenubar->isNativeMenuBar()) {
202 MenuRef apple_menu = qt_mac_current_menubar.qmenubar->d_func()->mac_menubar->apple_menu;
203 GetMenuItemProperty(apple_menu, 0, kMenuCreatorQt, kMenuPropertyMergeList, sizeof(list), 0, &list);
204 if (list)
205 menu = apple_menu;
206 }
207 if (list) {
208 for(int i = 0; i < list->size(); ++i) {
209 QMenuMergeItem item = list->at(i);
210 if (item.command == command && item.action) {
211 action = item.action;
212 break;
213 }
214 }
215 }
216 if (!action)
217 return false;
218 }
219
220 if (action_e == QAction::Trigger && by_accel && action->ignore_accel) //no, not a real accel (ie tab)
221 return false;
222
223 // Unhighlight the highlighted menu item before triggering the action to
224 // prevent items from staying highlighted while a modal dialog is shown.
225 // This also fixed the problem that parentless modal dialogs leave
226 // the menu item highlighted (since the menu bar is cleared for these types of dialogs).
227 if (action_e == QAction::Trigger)
228 HiliteMenu(0);
229
230 action->action->activate(action_e);
231
232 //now walk up firing for each "caused" widget (like in the platform independent menu)
233 QWidget *caused = 0;
234 if (GetMenuItemProperty(menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget, sizeof(caused), 0, &caused) == noErr) {
235 MenuRef caused_menu = 0;
236 if (QMenu *qmenu2 = qobject_cast<QMenu*>(caused))
237 caused_menu = qmenu2->macMenu();
238 else if (QMenuBar *qmenubar2 = qobject_cast<QMenuBar*>(caused))
239 caused_menu = qmenubar2->macMenu();
240 else
241 caused_menu = 0;
242 while(caused_menu) {
243 //fire
244 QWidget *widget = 0;
245 GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyQWidget, sizeof(widget), 0, &widget);
246 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
247 if (action_e == QAction::Trigger) {
248 emit qmenu->triggered(action->action);
249 } else if (action_e == QAction::Hover) {
250 action->action->showStatusText(widget);
251 emit qmenu->hovered(action->action);
252 }
253 } else if (QMenuBar *qmenubar = qobject_cast<QMenuBar*>(widget)) {
254 if (action_e == QAction::Trigger) {
255 emit qmenubar->triggered(action->action);
256 } else if (action_e == QAction::Hover) {
257 action->action->showStatusText(widget);
258 emit qmenubar->hovered(action->action);
259 }
260 break; //nothing more..
261 }
262
263 //walk up
264 if (GetMenuItemProperty(caused_menu, 0, kMenuCreatorQt, kMenuPropertyCausedQWidget,
265 sizeof(caused), 0, &caused) != noErr)
266 break;
267 if (QMenu *qmenu2 = qobject_cast<QMenu*>(caused))
268 caused_menu = qmenu2->macMenu();
269 else if (QMenuBar *qmenubar2 = qobject_cast<QMenuBar*>(caused))
270 caused_menu = qmenubar2->macMenu();
271 else
272 caused_menu = 0;
273 }
274 }
275 return true;
276}
277
278//lookup a QMacMenuAction in a menu
279static int qt_mac_menu_find_action(MenuRef menu, MenuCommand cmd)
280{