/**************************************************************************** ** ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Quick Controls module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL21$ ** 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 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** 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. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include "qquickmenuitem_p.h" #include "qquickaction_p.h" #include "qquickmenu_p.h" #include "qquickmenuitemcontainer_p.h" #include #include #include #include QT_BEGIN_NAMESPACE QQuickMenuBase::QQuickMenuBase(QObject *parent, int type) : QObject(parent), m_visible(true), m_type(static_cast(type)) , m_parentMenu(0), m_container(0), m_platformItem(0), m_visualItem(0) { if (type >= 0) { m_platformItem = QGuiApplicationPrivate::platformTheme()->createPlatformMenuItem(); if (m_platformItem) m_platformItem->setRole(QPlatformMenuItem::TextHeuristicRole); } } QQuickMenuBase::~QQuickMenuBase() { if (parentMenu()) parentMenu()->removeItem(this); setParentMenu(0); if (m_platformItem) { delete m_platformItem; m_platformItem = 0; } } void QQuickMenuBase::setVisible(bool v) { if (v != m_visible) { m_visible = v; if (m_platformItem) { m_platformItem->setVisible(m_visible); syncWithPlatformMenu(); } emit visibleChanged(); } } QObject *QQuickMenuBase::parentMenuOrMenuBar() const { if (!m_parentMenu) return parent(); return m_parentMenu; } QQuickMenu *QQuickMenuBase::parentMenu() const { return m_parentMenu; } void QQuickMenuBase::setParentMenu(QQuickMenu *parentMenu) { if (m_platformItem && m_parentMenu && m_parentMenu->platformMenu()) m_parentMenu->platformMenu()->removeMenuItem(m_platformItem); m_parentMenu = parentMenu; } QQuickMenuItemContainer *QQuickMenuBase::container() const { return m_container; } void QQuickMenuBase::setContainer(QQuickMenuItemContainer *c) { m_container = c; } void QQuickMenuBase::syncWithPlatformMenu() { QQuickMenu *menu = parentMenu(); if (menu && menu->platformMenu() && platformItem() && menu->contains(this)) // If not, it'll be added later and then sync'ed menu->platformMenu()->syncMenuItem(platformItem()); } QQuickItem *QQuickMenuBase::visualItem() const { return m_visualItem; } void QQuickMenuBase::setVisualItem(QQuickItem *item) { m_visualItem = item; } /*! \qmltype MenuSeparator \instantiates QQuickMenuSeparator \inqmlmodule QtQuick.Controls \ingroup menus \brief MenuSeparator provides a separator for items inside a menu. \image menu.png \code Menu { text: "Edit" MenuItem { text: "Cut" shortcut: "Ctrl+X" onTriggered: ... } MenuItem { text: "Copy" shortcut: "Ctrl+C" onTriggered: ... } MenuItem { text: "Paste" shortcut: "Ctrl+V" onTriggered: ... } MenuSeparator { } MenuItem { text: "Do Nothing" shortcut: "Ctrl+E,Shift+Ctrl+X" enabled: false } } \endcode \sa Menu, MenuItem */ /*! \qmlproperty bool MenuSeparator::visible Whether the menu separator should be visible. */ /*! \qmlproperty enumeration MenuSeparator::type This property is read-only and constant, and its value is \l MenuItemType.Separator. */ QQuickMenuSeparator::QQuickMenuSeparator(QObject *parent) : QQuickMenuBase(parent, QQuickMenuItemType::Separator) { if (platformItem()) platformItem()->setIsSeparator(true); } QQuickMenuText::QQuickMenuText(QObject *parent, QQuickMenuItemType::MenuItemType type) : QQuickMenuBase(parent, type), m_action(new QQuickAction(this)) { connect(m_action, SIGNAL(enabledChanged()), this, SLOT(updateEnabled())); connect(m_action, SIGNAL(textChanged()), this, SLOT(updateText())); connect(m_action, SIGNAL(iconNameChanged()), this, SLOT(updateIcon())); connect(m_action, SIGNAL(iconNameChanged()), this, SIGNAL(iconNameChanged())); connect(m_action, SIGNAL(iconSourceChanged()), this, SLOT(updateIcon())); connect(m_action, SIGNAL(iconSourceChanged()), this, SIGNAL(iconSourceChanged())); } QQuickMenuText::~QQuickMenuText() { delete m_action; } bool QQuickMenuText::enabled() const { return action()->isEnabled(); } void QQuickMenuText::setEnabled(bool e) { action()->setEnabled(e); } QString QQuickMenuText::text() const { return m_action->text(); } void QQuickMenuText::setText(const QString &t) { m_action->setText(t); } QUrl QQuickMenuText::iconSource() const { return m_action->iconSource(); } void QQuickMenuText::setIconSource(const QUrl &iconSource) { m_action->setIconSource(iconSource); } QString QQuickMenuText::iconName() const { return m_action->iconName(); } void QQuickMenuText::setIconName(const QString &iconName) { m_action->setIconName(iconName); } QIcon QQuickMenuText::icon() const { return m_action->icon(); } void QQuickMenuText::updateText() { if (platformItem()) { platformItem()->setText(text()); syncWithPlatformMenu(); } emit __textChanged(); } void QQuickMenuText::updateEnabled() { if (platformItem()) { platformItem()->setEnabled(enabled()); syncWithPlatformMenu(); } emit enabledChanged(); } void QQuickMenuText::updateIcon() { if (platformItem()) { platformItem()->setIcon(icon()); syncWithPlatformMenu(); } emit __iconChanged(); } /*! \qmltype MenuItem \instantiates QQuickMenuItem \ingroup menus \inqmlmodule QtQuick.Controls \brief MenuItem provides an item to add in a menu or a menu bar. \image menu.png \code Menu { text: "Edit" MenuItem { text: "Cut" shortcut: "Ctrl+X" onTriggered: ... } MenuItem { text: "Copy" shortcut: "Ctrl+C" onTriggered: ... } MenuItem { text: "Paste" shortcut: "Ctrl+V" onTriggered: ... } } \endcode \sa MenuBar, Menu, MenuSeparator, Action */ /*! \qmlproperty bool MenuItem::visible Whether the menu item should be visible. Defaults to \c true. */ /*! \qmlproperty enumeration MenuItem::type This property is read-only and constant, and its value is \l MenuItemType.Item. */ /*! \qmlproperty string MenuItem::text Text for the menu item. Overrides the item's bound action \c text property. Mnemonics are supported by prefixing the shortcut letter with \&. For instance, \c "\&Open" will bind the \c Alt-O shortcut to the \c "Open" menu item. Note that not all platforms support mnemonics. Defaults to the empty string. \sa Action::text */ /*! \qmlproperty bool MenuItem::enabled Whether the menu item is enabled, and responsive to user interaction. Defaults to \c true. */ /*! \qmlproperty url MenuItem::iconSource Sets the icon file or resource url for the \l MenuItem icon. Overrides the item's bound action \c iconSource property. Defaults to the empty URL. \sa iconName, Action::iconSource */ /*! \qmlproperty string MenuItem::iconName Sets the icon name for the \l MenuItem icon. This will pick the icon with the given name from the current theme. Overrides the item's bound action \c iconName property. Defaults to the empty string. \sa iconSource, Action::iconName */ /*! \qmlsignal MenuItem::triggered() Emitted when either the menu item or its bound action have been activated. \sa trigger(), Action::triggered, Action::toggled The corresponding handler is \c onTriggered. */ /*! \qmlmethod MenuItem::trigger() Manually trigger a menu item. Will also trigger the item's bound action. \sa triggered, Action::trigger() */ /*! \qmlproperty keysequence MenuItem::shortcut Shortcut bound to the menu item. The keysequence can be a string or a \l {QKeySequence::StandardKey}{standard key}. Defaults to an empty string. \qml MenuItem { id: copyItem text: qsTr("&Copy") shortcut: StandardKey.Copy } \endqml \sa Action::shortcut */ /*! \qmlproperty bool MenuItem::checkable Whether the menu item can be checked, or toggled. Defaults to \c false. \sa checked */ /*! \qmlproperty bool MenuItem::checked If the menu item is checkable, this property reflects its checked state. Defaults to \c false. \sa checkable, Action::toggled */ /*! \qmlproperty ExclusiveGroup MenuItem::exclusiveGroup If a menu item is checkable, an \l ExclusiveGroup can be attached to it. All the menu items sharing the same exclusive group, and by extension, any \l Action sharing it, become mutually exclusive selectable, meaning that only the last checked menu item will actually be checked. Defaults to \c null, meaning no exclusive behavior is to be expected. \sa checked, checkable */ /*! \qmlsignal MenuItem::toggled(checked) Emitted whenever a menu item's \c checked property changes. This usually happens at the same time as \l triggered. \sa checked, triggered, Action::triggered, Action::toggled The corresponding handler is \c onToggled. */ /*! \qmlproperty Action MenuItem::action The action bound to this menu item. It will provide values for all the properties of the menu item. However, it is possible to override the action's \c text, \c iconSource, and \c iconName properties by just assigning these properties, allowing some customization. In addition, the menu item \c triggered() and \c toggled() signals will not be emitted. Instead, the action \c triggered() and \c toggled() signals will be. Defaults to \c null, meaning no action is bound to the menu item. */ QQuickMenuItem::QQuickMenuItem(QObject *parent) : QQuickMenuText(parent, QQuickMenuItemType::Item), m_boundAction(0) { connect(this, SIGNAL(__textChanged()), this, SIGNAL(textChanged())); connect(action(), SIGNAL(shortcutChanged(QVariant)), this, SLOT(updateShortcut())); connect(action(), SIGNAL(triggered()), this, SIGNAL(triggered())); connect(action(), SIGNAL(checkableChanged()), this, SLOT(updateCheckable())); connect(action(), SIGNAL(toggled(bool)), this, SLOT(updateChecked())); if (platformItem()) connect(platformItem(), SIGNAL(activated()), this, SLOT(trigger()), Qt::QueuedConnection); } QQuickMenuItem::~QQuickMenuItem() { unbindFromAction(m_boundAction); if (platformItem()) disconnect(platformItem(), SIGNAL(activated()), this, SLOT(trigger())); } void QQuickMenuItem::setParentMenu(QQuickMenu *parentMenu) { QQuickMenuText::setParentMenu(parentMenu); if (parentMenu) connect(this, SIGNAL(triggered()), parentMenu, SLOT(updateSelectedIndex())); } void QQuickMenuItem::bindToAction(QQuickAction *action) { m_boundAction = action; connect(m_boundAction, SIGNAL(destroyed(QObject*)), this, SLOT(unbindFromAction(QObject*))); connect(m_boundAction, SIGNAL(triggered()), this, SIGNAL(triggered())); connect(m_boundAction, SIGNAL(toggled(bool)), this, SLOT(updateChecked())); connect(m_boundAction, SIGNAL(exclusiveGroupChanged()), this, SIGNAL(exclusiveGroupChanged())); connect(m_boundAction, SIGNAL(enabledChanged()), this, SLOT(updateEnabled())); connect(m_boundAction, SIGNAL(textChanged()), this, SLOT(updateText())); connect(m_boundAction, SIGNAL(shortcutChanged(QVariant)), this, SLOT(updateShortcut())); connect(m_boundAction, SIGNAL(checkableChanged()), this, SLOT(updateCheckable())); connect(m_boundAction, SIGNAL(iconNameChanged()), this, SLOT(updateIcon())); connect(m_boundAction, SIGNAL(iconNameChanged()), this, SIGNAL(iconNameChanged())); connect(m_boundAction, SIGNAL(iconSourceChanged()), this, SLOT(updateIcon())); connect(m_boundAction, SIGNAL(iconSourceChanged()), this, SIGNAL(iconSourceChanged())); if (m_boundAction->parent() != this) { updateText(); updateShortcut(); updateEnabled(); updateIcon(); if (checkable()) updateChecked(); } } void QQuickMenuItem::unbindFromAction(QObject *o) { if (!o) return; if (o == m_boundAction) m_boundAction = 0; QQuickAction *action = qobject_cast(o); if (!action) return; disconnect(action, SIGNAL(destroyed(QObject*)), this, SLOT(unbindFromAction(QObject*))); disconnect(action, SIGNAL(triggered()), this, SIGNAL(triggered())); disconnect(action, SIGNAL(toggled(bool)), this, SLOT(updateChecked())); disconnect(action, SIGNAL(exclusiveGroupChanged()), this, SIGNAL(exclusiveGroupChanged())); disconnect(action, SIGNAL(enabledChanged()), this, SLOT(updateEnabled())); disconnect(action, SIGNAL(textChanged()), this, SLOT(updateText())); disconnect(action, SIGNAL(shortcutChanged(QVariant)), this, SLOT(updateShortcut())); disconnect(action, SIGNAL(checkableChanged()), this, SLOT(updateCheckable())); disconnect(action, SIGNAL(iconNameChanged()), this, SLOT(updateIcon())); disconnect(action, SIGNAL(iconNameChanged()), this, SIGNAL(iconNameChanged())); disconnect(action, SIGNAL(iconSourceChanged()), this, SLOT(updateIcon())); disconnect(action, SIGNAL(iconSourceChanged()), this, SIGNAL(iconSourceChanged())); } QQuickAction *QQuickMenuItem::action() const { if (m_boundAction) return m_boundAction; return QQuickMenuText::action(); } void QQuickMenuItem::setBoundAction(QQuickAction *a) { if (a == m_boundAction) return; unbindFromAction(m_boundAction); bindToAction(a); emit actionChanged(); } QString QQuickMenuItem::text() const { QString ownText = QQuickMenuText::text(); if (!ownText.isNull()) return ownText; return m_boundAction ? m_boundAction->text() : QString(); } QUrl QQuickMenuItem::iconSource() const { QUrl ownIconSource = QQuickMenuText::iconSource(); if (!ownIconSource.isEmpty()) return ownIconSource; return m_boundAction ? m_boundAction->iconSource() : QUrl(); } QString QQuickMenuItem::iconName() const { QString ownIconName = QQuickMenuText::iconName(); if (!ownIconName.isEmpty()) return ownIconName; return m_boundAction ? m_boundAction->iconName() : QString(); } QIcon QQuickMenuItem::icon() const { QIcon ownIcon = QQuickMenuText::icon(); if (!ownIcon.isNull()) return ownIcon; return m_boundAction ? m_boundAction->icon() : QIcon(); } QVariant QQuickMenuItem::shortcut() const { return action()->shortcut(); } void QQuickMenuItem::setShortcut(const QVariant &shortcut) { if (!m_boundAction) action()->setShortcut(shortcut); } void QQuickMenuItem::updateShortcut() { if (platformItem()) { QKeySequence sequence; QVariant var = shortcut(); if (var.type() == QVariant::Int) sequence = QKeySequence(static_cast(var.toInt())); else sequence = QKeySequence::fromString(var.toString(), QKeySequence::NativeText); platformItem()->setShortcut(sequence); syncWithPlatformMenu(); } emit shortcutChanged(); } bool QQuickMenuItem::checkable() const { return action()->isCheckable(); } void QQuickMenuItem::setCheckable(bool checkable) { if (!m_boundAction) action()->setCheckable(checkable); } void QQuickMenuItem::updateCheckable() { if (platformItem()) { platformItem()->setCheckable(checkable()); syncWithPlatformMenu(); } emit checkableChanged(); } bool QQuickMenuItem::checked() const { return action()->isChecked(); } void QQuickMenuItem::setChecked(bool checked) { if (!m_boundAction) action()->setChecked(checked); } void QQuickMenuItem::updateChecked() { bool checked = this->checked(); if (platformItem()) { platformItem()->setChecked(checked); syncWithPlatformMenu(); } emit toggled(checked); } QQuickExclusiveGroup *QQuickMenuItem::exclusiveGroup() const { return action()->exclusiveGroup(); } void QQuickMenuItem::setExclusiveGroup(QQuickExclusiveGroup *eg) { if (!m_boundAction) action()->setExclusiveGroup(eg); } void QQuickMenuItem::setEnabled(bool enabled) { if (!m_boundAction) action()->setEnabled(enabled); } void QQuickMenuItem::trigger() { action()->trigger(this); } QT_END_NAMESPACE