/**************************************************************************** ** ** Copyright (C) 2012 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author James Turner ** Contact: http://www.qt-project.org/legal ** ** This file is part of the plugins of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Digia. For licensing terms and ** conditions see http://qt.digia.com/licensing. For further information ** use the contact form at http://qt.digia.com/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Digia gives you certain additional ** rights. These rights are described in the Digia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3.0 as published by the Free Software ** Foundation and appearing in the file LICENSE.GPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU General Public License version 3.0 requirements will be ** met: http://www.gnu.org/copyleft/gpl.html. ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include "qcocoamenubar.h" #include "qcocoawindow.h" #include "qcocoamenuloader.h" #include "qcocoaapplication.h" // for custom application category #include "qcocoaautoreleasepool.h" #include #include static QList static_menubars; static inline QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *getMenuLoader() { return [NSApp QT_MANGLE_NAMESPACE(qt_qcocoamenuLoader)]; } QCocoaMenuBar::QCocoaMenuBar() : m_window(0) { static_menubars.append(this); m_nativeMenu = [[NSMenu alloc] init]; #ifdef QT_COCOA_ENABLE_MENU_DEBUG qDebug() << "Construct QCocoaMenuBar" << this << m_nativeMenu; #endif } QCocoaMenuBar::~QCocoaMenuBar() { #ifdef QT_COCOA_ENABLE_MENU_DEBUG qDebug() << "~QCocoaMenuBar" << this; #endif [m_nativeMenu release]; static_menubars.removeOne(this); if (m_window && m_window->menubar() == this) { m_window->setMenubar(0); updateMenuBarImmediately(); } } void QCocoaMenuBar::insertMenu(QPlatformMenu *platformMenu, QPlatformMenu *before) { QCocoaAutoReleasePool pool; QCocoaMenu *menu = static_cast(platformMenu); QCocoaMenu *beforeMenu = static_cast(before); #ifdef QT_COCOA_ENABLE_MENU_DEBUG qDebug() << "QCocoaMenuBar" << this << "insertMenu" << menu << "before" << before; #endif if (m_menus.contains(menu)) { qWarning() << Q_FUNC_INFO << "This menu already belongs to the menubar, remove it first"; return; } if (beforeMenu) { if (!m_menus.contains(beforeMenu)) { qWarning() << Q_FUNC_INFO << "The before menu does not belong to the menubar"; return; } m_menus.insert(m_menus.indexOf(beforeMenu), menu); NSUInteger nativeIndex = [m_nativeMenu indexOfItem:beforeMenu->nsMenuItem()]; [m_nativeMenu insertItem: menu->nsMenuItem() atIndex: nativeIndex]; } else { m_menus.append(menu); [m_nativeMenu addItem: menu->nsMenuItem()]; } [m_nativeMenu setSubmenu: menu->nsMenu() forItem: menu->nsMenuItem()]; } void QCocoaMenuBar::removeMenu(QPlatformMenu *platformMenu) { QCocoaMenu *menu = static_cast(platformMenu); if (!m_menus.contains(menu)) { qWarning() << Q_FUNC_INFO << "Trying to remove a menu that does not belong to the menubar"; return; } m_menus.removeOne(menu); NSUInteger realIndex = [m_nativeMenu indexOfItem:menu->nsMenuItem()]; [m_nativeMenu removeItemAtIndex: realIndex]; } void QCocoaMenuBar::syncMenu(QPlatformMenu *menu) { Q_UNUSED(menu); } void QCocoaMenuBar::handleReparent(QWindow *newParentWindow) { #ifdef QT_COCOA_ENABLE_MENU_DEBUG qDebug() << "QCocoaMenuBar" << this << "handleReparent" << newParentWindow; #endif if (m_window) m_window->setMenubar(NULL); if (newParentWindow == NULL) { m_window = NULL; } else { m_window = static_cast(newParentWindow->handle()); m_window->setMenubar(this); } updateMenuBarImmediately(); } QCocoaWindow *QCocoaMenuBar::findWindowForMenubar() { if (qApp->focusWindow()) return static_cast(qApp->focusWindow()->handle()); return NULL; } QCocoaMenuBar *QCocoaMenuBar::findGlobalMenubar() { foreach (QCocoaMenuBar *mb, static_menubars) { if (mb->m_window == NULL) return mb; } return NULL; } void QCocoaMenuBar::updateMenuBarImmediately() { QCocoaAutoReleasePool pool; QCocoaMenuBar *mb = findGlobalMenubar(); QCocoaWindow *cw = findWindowForMenubar(); if (cw && cw->menubar()) mb = cw->menubar(); if (!mb) return; #ifdef QT_COCOA_ENABLE_MENU_DEBUG qDebug() << "QCocoaMenuBar" << "updateMenuBarImmediately" << cw; #endif bool disableForModal = mb->shouldDisable(cw); // force a sync? foreach (QCocoaMenu *m, mb->m_menus) { mb->syncMenu(m); m->syncModalState(disableForModal); } QT_MANGLE_NAMESPACE(QCocoaMenuLoader) *loader = getMenuLoader(); [loader ensureAppMenuInMenu:mb->nsMenu()]; NSMutableSet *mergedItems = [[NSMutableSet setWithCapacity:0] retain]; foreach (QCocoaMenuItem *m, mb->merged()) { [mergedItems addObject:m->nsItem()]; m->syncMerged(); } // hide+disable all mergeable items we're not currently using for (NSMenuItem *mergeable in [loader mergeable]) { if (![mergedItems containsObject:mergeable]) { [mergeable setHidden:YES]; [mergeable setEnabled:NO]; } } [mergedItems release]; [NSApp setMainMenu:mb->nsMenu()]; [loader qtTranslateApplicationMenu]; } QList QCocoaMenuBar::merged() const { QList r; foreach (QCocoaMenu* menu, m_menus) r.append(menu->merged()); return r; } bool QCocoaMenuBar::shouldDisable(QCocoaWindow *active) const { if (active && (active->window()->modality() == Qt::NonModal)) return false; if (m_window == active) { // modal window owns us, we should be enabled! return false; } QWindowList topWindows(qApp->topLevelWindows()); // When there is an application modal window on screen, the entries of // the menubar should be disabled. The exception in Qt is that if the // modal window is the only window on screen, then we enable the menu bar. foreach (QWindow *w, topWindows) { if (w->isVisible() && w->modality() == Qt::ApplicationModal) { // check for other visible windows foreach (QWindow *other, topWindows) { if ((w != other) && (other->isVisible())) { // INVARIANT: we found another visible window // on screen other than our modalWidget. We therefore // disable the menu bar to follow normal modality logic: return true; } } // INVARIANT: We have only one window on screen that happends // to be application modal. We choose to enable the menu bar // in that case to e.g. enable the quit menu item. return false; } } return true; } QPlatformMenu *QCocoaMenuBar::menuForTag(quintptr tag) const { foreach (QCocoaMenu *menu, m_menus) { if (menu->tag() == tag) return menu; } return 0; }