1 /****************************************************************************
3 ** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner <james.turner@kdab.com>
4 ** Contact: http://www.qt-project.org/legal
6 ** This file is part of the plugins of the Qt Toolkit.
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 Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
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 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
40 ****************************************************************************/
42 #include <Cocoa/Cocoa.h>
44 #include "qcocoamenubar.h"
45 #include "qcocoawindow.h"
46 #include "qcocoamenuloader.h"
47 #include "qcocoaapplication.h" // for custom application category
48 #include "qcocoaautoreleasepool.h"
50 #include <QtGui/QGuiApplication>
51 #include <QtCore/QDebug>
53 static QList<QCocoaMenuBar*> static_menubars;
55 static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader()
57 return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)];
61 QCocoaMenuBar::QCocoaMenuBar() :
64 static_menubars.append(this);
66 m_nativeMenu = [[NSMenu alloc] init];
67 #ifdef QT_COCOA_ENABLE_MENU_DEBUG
68 qDebug() << "Construct QCocoaMenuBar" << this << m_nativeMenu;
72 QCocoaMenuBar::~QCocoaMenuBar()
74 #ifdef QT_COCOA_ENABLE_MENU_DEBUG
75 qDebug() << "~QCocoaMenuBar" << this;
77 [m_nativeMenu release];
78 static_menubars.removeOne(this);
81 m_window->setMenubar(0);
84 void QCocoaMenuBar::insertMenu(QPlatformMenu *platformMenu, QPlatformMenu *before)
86 QCocoaAutoReleasePool pool;
87 QCocoaMenu *menu = static_cast<QCocoaMenu *>(platformMenu);
88 QCocoaMenu *beforeMenu = static_cast<QCocoaMenu *>(before);
89 #ifdef QT_COCOA_ENABLE_MENU_DEBUG
90 qDebug() << "QCocoaMenuBar" << this << "insertMenu" << menu << "before" << before;
93 if (m_menus.contains(menu)) {
94 qWarning() << Q_FUNC_INFO << "This menu already belongs to the menubar, remove it first";
98 if (!m_menus.contains(beforeMenu)) {
99 qWarning() << Q_FUNC_INFO << "The before menu does not belong to the menubar";
102 m_menus.insert(m_menus.indexOf(beforeMenu), menu);
103 NSUInteger nativeIndex = [m_nativeMenu indexOfItem:beforeMenu->nsMenuItem()];
104 [m_nativeMenu insertItem: menu->nsMenuItem() atIndex: nativeIndex];
106 m_menus.append(menu);
107 [m_nativeMenu addItem: menu->nsMenuItem()];
110 [m_nativeMenu setSubmenu: menu->nsMenu() forItem: menu->nsMenuItem()];
113 void QCocoaMenuBar::removeMenu(QPlatformMenu *platformMenu)
115 QCocoaMenu *menu = static_cast<QCocoaMenu *>(platformMenu);
116 if (!m_menus.contains(menu)) {
117 qWarning() << Q_FUNC_INFO << "Trying to remove a menu that does not belong to the menubar";
120 m_menus.removeOne(menu);
122 NSUInteger realIndex = [m_nativeMenu indexOfItem:menu->nsMenuItem()];
123 [m_nativeMenu removeItemAtIndex: realIndex];
126 void QCocoaMenuBar::syncMenu(QPlatformMenu *menu)
131 void QCocoaMenuBar::handleReparent(QWindow *newParentWindow)
133 #ifdef QT_COCOA_ENABLE_MENU_DEBUG
134 qDebug() << "QCocoaMenuBar" << this << "handleReparent" << newParentWindow;
138 m_window->setMenubar(NULL);
140 if (newParentWindow == NULL) {
143 m_window = static_cast<QCocoaWindow*>(newParentWindow->handle());
144 m_window->setMenubar(this);
147 updateMenuBarImmediately();
150 QCocoaWindow *QCocoaMenuBar::findWindowForMenubar()
152 if (qApp->focusWindow())
153 return static_cast<QCocoaWindow*>(qApp->focusWindow()->handle());
158 QCocoaMenuBar *QCocoaMenuBar::findGlobalMenubar()
160 foreach (QCocoaMenuBar *mb, static_menubars) {
161 if (mb->m_window == NULL)
168 void QCocoaMenuBar::updateMenuBarImmediately()
170 QCocoaAutoReleasePool pool;
171 QCocoaMenuBar *mb = findGlobalMenubar();
172 QCocoaWindow *cw = findWindowForMenubar();
173 if (cw && cw->menubar())
179 #ifdef QT_COCOA_ENABLE_MENU_DEBUG
180 qDebug() << "QCocoaMenuBar" << "updateMenuBarImmediately" << cw;
182 bool disableForModal = mb->shouldDisable(cw);
184 foreach (QCocoaMenu *m, mb->m_menus) {
186 m->syncModalState(disableForModal);
189 QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader();
190 [loader ensureAppMenuInMenu:mb->nsMenu()];
192 NSMutableSet *mergedItems = [[NSMutableSet setWithCapacity:0] retain];
193 foreach (QCocoaMenuItem *m, mb->merged()) {
194 [mergedItems addObject:m->nsItem()];
198 // hide+disable all mergeable items we're not currently using
199 for (NSMenuItem *mergeable in [loader mergeable]) {
200 if (![mergedItems containsObject:mergeable]) {
201 [mergeable setHidden:YES];
202 [mergeable setEnabled:NO];
206 [mergedItems release];
207 [NSApp setMainMenu:mb->nsMenu()];
208 [loader qtTranslateApplicationMenu];
211 QList<QCocoaMenuItem*> QCocoaMenuBar::merged() const
213 QList<QCocoaMenuItem*> r;
214 foreach (QCocoaMenu* menu, m_menus)
215 r.append(menu->merged());
220 bool QCocoaMenuBar::shouldDisable(QCocoaWindow *active) const
222 if (active && (active->window()->modality() == Qt::NonModal))
225 if (m_window == active) {
226 // modal window owns us, we should be enabled!
230 QWindowList topWindows(qApp->topLevelWindows());
231 // When there is an application modal window on screen, the entries of
232 // the menubar should be disabled. The exception in Qt is that if the
233 // modal window is the only window on screen, then we enable the menu bar.
234 foreach (QWindow *w, topWindows) {
235 if (w->isVisible() && w->modality() == Qt::ApplicationModal) {
236 // check for other visible windows
237 foreach (QWindow *other, topWindows) {
238 if ((w != other) && (other->isVisible())) {
239 // INVARIANT: we found another visible window
240 // on screen other than our modalWidget. We therefore
241 // disable the menu bar to follow normal modality logic:
246 // INVARIANT: We have only one window on screen that happends
247 // to be application modal. We choose to enable the menu bar
248 // in that case to e.g. enable the quit menu item.
256 QPlatformMenu *QCocoaMenuBar::menuForTag(quintptr tag) const
258 foreach (QCocoaMenu *menu, m_menus) {
259 if (menu->tag() == tag)