1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
6 ** This file is part of the QtGui module of the Qt Toolkit.
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
52 #include <qpa/qplatformtheme.h>
53 #include "qapplication.h"
54 #include "qdesktopwidget.h"
55 #ifndef QT_NO_ACCESSIBILITY
56 # include "qaccessible.h"
59 # include <private/qeffects_p.h>
61 #ifndef QT_NO_WHATSTHIS
62 # include <qwhatsthis.h>
66 #include "qmenubar_p.h"
67 #include "qwidgetaction.h"
68 #include "qtoolbutton.h"
69 #include "qpushbutton.h"
70 #include <private/qpushbutton_p.h>
71 #include <private/qaction_p.h>
72 #include <private/qsoftkeymanager_p.h>
73 #include <private/qguiapplication_p.h>
74 #include <qpa/qplatformtheme.h>
78 QMenu *QMenuPrivate::mouseDown = 0;
79 int QMenuPrivate::sloppyDelayTimer = 0;
82 // internal class used for the torn off popup
83 class QTornOffMenu : public QMenu
86 class QTornOffMenuPrivate : public QMenuPrivate
88 Q_DECLARE_PUBLIC(QMenu)
90 QTornOffMenuPrivate(QMenu *p) : causedMenu(p) {
92 causedPopup.widget = 0;
93 causedPopup.action = ((QTornOffMenu*)p)->d_func()->causedPopup.action;
94 causedStack = ((QTornOffMenu*)p)->d_func()->calcCausedStack();
96 QList<QPointer<QWidget> > calcCausedStack() const { return causedStack; }
97 QPointer<QMenu> causedMenu;
98 QList<QPointer<QWidget> > causedStack;
101 QTornOffMenu(QMenu *p) : QMenu(*(new QTornOffMenuPrivate(p)))
104 // make the torn-off menu a sibling of p (instead of a child)
105 QWidget *parentWidget = d->causedStack.isEmpty() ? p : d->causedStack.last();
106 if (parentWidget->parentWidget())
107 parentWidget = parentWidget->parentWidget();
108 setParent(parentWidget, Qt::Window | Qt::Tool);
109 setAttribute(Qt::WA_DeleteOnClose, true);
110 setAttribute(Qt::WA_X11NetWmWindowTypeMenu, true);
111 setWindowTitle(p->windowTitle());
112 setEnabled(p->isEnabled());
113 //QObject::connect(this, SIGNAL(triggered(QAction*)), this, SLOT(onTrigger(QAction*)));
114 //QObject::connect(this, SIGNAL(hovered(QAction*)), this, SLOT(onHovered(QAction*)));
115 QList<QAction*> items = p->actions();
116 for(int i = 0; i < items.count(); i++)
117 addAction(items.at(i));
119 void syncWithMenu(QMenu *menu, QActionEvent *act)
122 if(menu != d->causedMenu)
124 if (act->type() == QEvent::ActionAdded) {
125 insertAction(act->before(), act->action());
126 } else if (act->type() == QEvent::ActionRemoved)
127 removeAction(act->action());
129 void actionEvent(QActionEvent *e)
131 QMenu::actionEvent(e);
132 setFixedSize(sizeHint());
135 void onTrigger(QAction *action) { d_func()->activateAction(action, QAction::Trigger, false); }
136 void onHovered(QAction *action) { d_func()->activateAction(action, QAction::Hover, false); }
138 Q_DECLARE_PRIVATE(QTornOffMenu)
139 friend class QMenuPrivate;
142 void QMenuPrivate::init()
145 #ifndef QT_NO_WHATSTHIS
146 q->setAttribute(Qt::WA_CustomWhatsThis);
148 q->setAttribute(Qt::WA_X11NetWmWindowTypePopupMenu);
149 defaultMenuAction = menuAction = new QAction(q);
150 menuAction->d_func()->menu = q;
151 q->setMouseTracking(q->style()->styleHint(QStyle::SH_Menu_MouseTracking, 0, q));
152 if (q->style()->styleHint(QStyle::SH_Menu_Scrollable, 0, q)) {
153 scroll = new QMenuPrivate::QMenuScroller;
154 scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
157 platformMenu = QGuiApplicationPrivate::platformTheme()->createPlatformMenu();
159 QObject::connect(platformMenu, SIGNAL(aboutToShow()), q, SIGNAL(aboutToShow()));
160 QObject::connect(platformMenu, SIGNAL(aboutToHide()), q, SIGNAL(aboutToHide()));
163 #ifdef QT_SOFTKEYS_ENABLED
164 selectAction = QSoftKeyManager::createKeyedAction(QSoftKeyManager::SelectSoftKey, Qt::Key_Select, q);
165 cancelAction = QSoftKeyManager::createKeyedAction(QSoftKeyManager::CancelSoftKey, Qt::Key_Back, q);
166 selectAction->setPriority(QAction::HighPriority);
167 cancelAction->setPriority(QAction::HighPriority);
168 q->addAction(selectAction);
169 q->addAction(cancelAction);
173 int QMenuPrivate::scrollerHeight() const
176 return qMax(QApplication::globalStrut().height(), q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q));
179 //Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't
180 QRect QMenuPrivate::popupGeometry(const QWidget *widget) const
182 if (QGuiApplicationPrivate::platformTheme() &&
183 QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool()) {
184 return QApplication::desktop()->screenGeometry(widget);
186 return QApplication::desktop()->availableGeometry(widget);
190 //Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't
191 QRect QMenuPrivate::popupGeometry(int screen) const
193 if (QGuiApplicationPrivate::platformTheme() &&
194 QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool()) {
195 return QApplication::desktop()->screenGeometry(screen);
197 return QApplication::desktop()->availableGeometry(screen);
201 QList<QPointer<QWidget> > QMenuPrivate::calcCausedStack() const
203 QList<QPointer<QWidget> > ret;
204 for(QWidget *widget = causedPopup.widget; widget; ) {
206 if (QTornOffMenu *qtmenu = qobject_cast<QTornOffMenu*>(widget))
207 ret += qtmenu->d_func()->causedStack;
208 if (QMenu *qmenu = qobject_cast<QMenu*>(widget))
209 widget = qmenu->d_func()->causedPopup.widget;
216 void QMenuPrivate::updateActionRects() const
219 updateActionRects(popupGeometry(q));
222 void QMenuPrivate::updateActionRects(const QRect &screen) const
230 //let's reinitialize the buffer
231 actionRects.resize(actions.count());
232 actionRects.fill(QRect());
234 int lastVisibleAction = getLastVisibleAction();
236 int max_column_width = 0,
237 dh = screen.height(),
239 QStyle *style = q->style();
242 const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q),
243 vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q),
244 icone = style->pixelMetric(QStyle::PM_SmallIconSize, &opt, q);
245 const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q);
246 const int deskFw = style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, &opt, q);
247 const int tearoffHeight = tearoff ? style->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q) : 0;
249 //for compatibility now - will have to refactor this away
252 hasCheckableItems = false;
256 for (int i = 0; i < actions.count(); ++i) {
257 QAction *action = actions.at(i);
258 if (action->isSeparator() || !action->isVisible() || widgetItems.contains(action))
261 hasCheckableItems |= action->isCheckable();
262 QIcon is = action->icon();
264 maxIconWidth = qMax<uint>(maxIconWidth, icone + 4);
269 QFontMetrics qfm = q->fontMetrics();
270 bool previousWasSeparator = true; // this is true to allow removing the leading separators
271 for(int i = 0; i <= lastVisibleAction; i++) {
272 QAction *action = actions.at(i);
274 if (!action->isVisible() ||
275 (collapsibleSeparators && previousWasSeparator && action->isSeparator()))
276 continue; // we continue, this action will get an empty QRect
278 previousWasSeparator = action->isSeparator();
280 //let the style modify the above size..
281 QStyleOptionMenuItem opt;
282 q->initStyleOption(&opt, action);
283 const QFontMetrics &fm = opt.fontMetrics;
286 if (QWidget *w = widgetItems.value(action)) {
287 sz = w->sizeHint().expandedTo(w->minimumSize()).expandedTo(w->minimumSizeHint()).boundedTo(w->maximumSize());
289 //calc what I think the size is..
290 if (action->isSeparator()) {
293 QString s = action->text();
294 int t = s.indexOf(QLatin1Char('\t'));
296 tabWidth = qMax(int(tabWidth), qfm.width(s.mid(t+1)));
298 #ifndef QT_NO_SHORTCUT
300 QKeySequence seq = action->shortcut();
302 tabWidth = qMax(int(tabWidth), qfm.width(seq.toString(QKeySequence::NativeText)));
305 sz.setWidth(fm.boundingRect(QRect(), Qt::TextSingleLine | Qt::TextShowMnemonic, s).width());
306 sz.setHeight(qMax(fm.height(), qfm.height()));
308 QIcon is = action->icon();
310 QSize is_sz = QSize(icone, icone);
311 if (is_sz.height() > sz.height())
312 sz.setHeight(is_sz.height());
315 sz = style->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, q);
320 max_column_width = qMax(max_column_width, sz.width());
323 y+sz.height()+vmargin > dh - (deskFw * 2)) {
329 actionRects[i] = QRect(0, 0, sz.width(), sz.height());
333 max_column_width += tabWidth; //finally add in the tab width
334 const int sfcMargin = style->sizeFromContents(QStyle::CT_Menu, &opt, QApplication::globalStrut(), q).width() - QApplication::globalStrut().width();
335 const int min_column_width = q->minimumWidth() - (sfcMargin + leftmargin + rightmargin + 2 * (fw + hmargin));
336 max_column_width = qMax(min_column_width, max_column_width);
339 const int base_y = vmargin + fw + topmargin +
340 (scroll ? scroll->scrollOffset : 0) +
342 int x = hmargin + fw + leftmargin;
345 for(int i = 0; i < actions.count(); i++) {
346 QRect &rect = actionRects[i];
350 y+rect.height() > dh - deskFw * 2) {
351 x += max_column_width + hmargin;
354 rect.translate(x, y); //move
355 rect.setWidth(max_column_width); //uniform width
357 //we need to update the widgets geometry
358 if (QWidget *widget = widgetItems.value(actions.at(i))) {
359 widget->setGeometry(rect);
360 widget->setVisible(actions.at(i)->isVisible());
368 QSize QMenuPrivate::adjustMenuSizeForScreen(const QRect &screen)
371 QSize ret = screen.size();
373 updateActionRects(screen);
374 const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
375 ret.setWidth(actionRects.at(getLastVisibleAction()).right() + fw);
379 int QMenuPrivate::getLastVisibleAction() const
381 //let's try to get the last visible action
382 int lastVisibleAction = actions.count() - 1;
383 for (;lastVisibleAction >= 0; --lastVisibleAction) {
384 const QAction *action = actions.at(lastVisibleAction);
385 if (action->isVisible()) {
386 //removing trailing separators
387 if (action->isSeparator() && collapsibleSeparators)
392 return lastVisibleAction;
396 QRect QMenuPrivate::actionRect(QAction *act) const
398 int index = actions.indexOf(act);
404 //we found the action
405 return actionRects.at(index);
408 #if defined(Q_OS_MAC)
409 static const qreal MenuFadeTimeInSec = 0.150;
412 void QMenuPrivate::hideUpToMenuBar()
415 bool fadeMenus = q->style()->styleHint(QStyle::SH_Menu_FadeOutOnHide);
417 QWidget *caused = causedPopup.widget;
418 hideMenu(q); //hide after getting causedPopup
420 #ifndef QT_NO_MENUBAR
421 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
422 mb->d_func()->setCurrentAction(0);
423 mb->d_func()->setKeyboardMode(false);
427 if (QMenu *m = qobject_cast<QMenu*>(caused)) {
428 caused = m->d_func()->causedPopup.widget;
429 if (!m->d_func()->tornoff)
430 hideMenu(m, fadeMenus);
431 if (!fadeMenus) // Mac doesn't clear the action until after hidden.
432 m->d_func()->setCurrentAction(0);
436 #if defined(Q_WS_MAC)
438 QEventLoop eventLoop;
439 QTimer::singleShot(int(MenuFadeTimeInSec * 1000), &eventLoop, SLOT(quit()));
440 QMacWindowFader::currentFader()->performFade();
448 void QMenuPrivate::hideMenu(QMenu *menu, bool justRegister)
452 #if !defined(QT_NO_EFFECTS)
453 menu->blockSignals(true);
455 // Flash item which is about to trigger (if any).
456 if (menu->style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)
457 && currentAction && currentAction == actionAboutToTrigger
458 && menu->actions().contains(currentAction)) {
459 QEventLoop eventLoop;
460 QAction *activeAction = currentAction;
462 menu->setActiveAction(0);
463 QTimer::singleShot(60, &eventLoop, SLOT(quit()));
466 // Select and wait 20 ms.
467 menu->setActiveAction(activeAction);
468 QTimer::singleShot(20, &eventLoop, SLOT(quit()));
473 if (menu->style()->styleHint(QStyle::SH_Menu_FadeOutOnHide)) {
475 // Should be something like: q->transitionWindow(Qt::FadeOutTransition, MenuFadeTimeInSec);
476 // Hopefully we'll integrate qt/research/windowtransitions into main before 4.4.
477 // Talk to Richard, Trenton or Bjoern.
478 #if defined(Q_WS_MAC)
480 QMacWindowFader::currentFader()->setFadeDuration(MenuFadeTimeInSec);
481 QMacWindowFader::currentFader()->registerWindowToFade(menu);
483 macWindowFade(qt_mac_window_for(menu), MenuFadeTimeInSec);
489 menu->blockSignals(false);
490 #endif // QT_NO_EFFECTS
495 void QMenuPrivate::popupAction(QAction *action, int delay, bool activateFirst)
498 if (action && action->isEnabled()) {
500 q->internalDelayedPopup();
501 else if (!menuDelayTimer.isActive() && (!action->menu() || !action->menu()->isVisible()))
502 menuDelayTimer.start(delay, q);
503 if (activateFirst && action->menu())
504 action->menu()->d_func()->setFirstActionActive();
505 } else if (QMenu *menu = activeMenu) { //hide the current item
511 void QMenuPrivate::setSyncAction()
514 QAction *current = currentAction;
515 if(current && (!current->isEnabled() || current->menu() || current->isSeparator()))
517 for(QWidget *caused = q; caused;) {
518 if (QMenu *m = qobject_cast<QMenu*>(caused)) {
519 caused = m->d_func()->causedPopup.widget;
520 if (m->d_func()->eventLoop)
521 m->d_func()->syncAction = current; // synchronous operation
529 void QMenuPrivate::setFirstActionActive()
533 for(int i = 0, saccum = 0; i < actions.count(); i++) {
534 const QRect &rect = actionRects.at(i);
537 if (scroll && scroll->scrollFlags & QMenuScroller::ScrollUp) {
538 saccum -= rect.height();
539 if (saccum > scroll->scrollOffset - scrollerHeight())
542 QAction *act = actions.at(i);
543 if (!act->isSeparator() &&
544 (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
545 || act->isEnabled())) {
546 setCurrentAction(act);
552 // popup == -1 means do not popup, 0 means immediately, others mean use a timer
553 void QMenuPrivate::setCurrentAction(QAction *action, int popup, SelectionReason reason, bool activateFirst)
556 tearoffHighlighted = 0;
558 q->update(actionRect(currentAction));
561 if (!sloppyRegion.isEmpty())
562 sloppyRegion = QRegion();
563 QMenu *hideActiveMenu = activeMenu;
564 #ifndef QT_NO_STATUSTIP
565 QAction *previousAction = currentAction;
568 currentAction = action;
570 if (!action->isSeparator()) {
571 activateAction(action, QAction::Hover);
573 hideActiveMenu = 0; //will be done "later"
574 // if the menu is visible then activate the required action,
575 // otherwise we just mark the action as currentAction
576 // and activate it when the menu will be popuped.
578 popupAction(currentAction, popup, activateFirst);
580 q->update(actionRect(action));
582 if (reason == SelectedFromKeyboard) {
583 QWidget *widget = widgetItems.value(action);
585 if (widget->focusPolicy() != Qt::NoFocus)
586 widget->setFocus(Qt::TabFocusReason);
588 //when the action has no QWidget, the QMenu itself should
590 // Since the menu is a pop-up, it uses the popup reason.
591 if (!q->hasFocus()) {
592 q->setFocus(Qt::PopupFocusReason);
596 } else { //action is a separator
598 hideActiveMenu = 0; //will be done "later"
600 #ifndef QT_NO_STATUSTIP
601 } else if (previousAction) {
602 previousAction->d_func()->showStatusText(topCausedWidget(), QString());
605 if (hideActiveMenu) {
607 #ifndef QT_NO_EFFECTS
608 // kill any running effect
612 hideMenu(hideActiveMenu);
616 //return the top causedPopup.widget that is not a QMenu
617 QWidget *QMenuPrivate::topCausedWidget() const
619 QWidget* top = causedPopup.widget;
620 while (QMenu* m = qobject_cast<QMenu *>(top))
621 top = m->d_func()->causedPopup.widget;
625 QAction *QMenuPrivate::actionAt(QPoint p) const
627 if (!q_func()->rect().contains(p)) //sanity check
630 for(int i = 0; i < actionRects.count(); i++) {
631 if (actionRects.at(i).contains(p))
632 return actions.at(i);
637 void QMenuPrivate::setOverrideMenuAction(QAction *a)
640 QObject::disconnect(menuAction, SIGNAL(destroyed()), q, SLOT(_q_overrideMenuActionDestroyed()));
643 QObject::connect(a, SIGNAL(destroyed()), q, SLOT(_q_overrideMenuActionDestroyed()));
644 } else { //we revert back to the default action created by the QMenu itself
645 menuAction = defaultMenuAction;
649 void QMenuPrivate::_q_overrideMenuActionDestroyed()
651 menuAction=defaultMenuAction;
655 void QMenuPrivate::updateLayoutDirection()
658 //we need to mimic the cause of the popup's layout direction
659 //to allow setting it on a mainwindow for example
660 //we call setLayoutDirection_helper to not overwrite a user-defined value
661 if (!q->testAttribute(Qt::WA_SetLayoutDirection)) {
662 if (QWidget *w = causedPopup.widget)
663 setLayoutDirection_helper(w->layoutDirection());
664 else if (QWidget *w = q->parentWidget())
665 setLayoutDirection_helper(w->layoutDirection());
667 setLayoutDirection_helper(QApplication::layoutDirection());
673 Returns the action associated with this menu.
675 QAction *QMenu::menuAction() const
677 return d_func()->menuAction;
681 \property QMenu::title
682 \brief The title of the menu
684 This is equivalent to the QAction::text property of the menuAction().
686 By default, this property contains an empty string.
688 QString QMenu::title() const
690 return d_func()->menuAction->text();
693 void QMenu::setTitle(const QString &text)
695 d_func()->menuAction->setText(text);
699 \property QMenu::icon
701 \brief The icon of the menu
703 This is equivalent to the QAction::icon property of the menuAction().
705 By default, if no icon is explicitly set, this property contains a null icon.
707 QIcon QMenu::icon() const
709 return d_func()->menuAction->icon();
712 void QMenu::setIcon(const QIcon &icon)
714 d_func()->menuAction->setIcon(icon);
718 //actually performs the scrolling
719 void QMenuPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation location, bool active)
722 if (!scroll || !scroll->scrollFlags)
726 const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp) ? scrollerHeight() : 0;
727 const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollerHeight() : 0;
728 const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
729 const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
731 if (location == QMenuScroller::ScrollTop) {
732 for(int i = 0, saccum = 0; i < actions.count(); i++) {
733 if (actions.at(i) == action) {
734 newOffset = topScroll - saccum;
737 saccum += actionRects.at(i).height();
740 for(int i = 0, saccum = 0; i < actions.count(); i++) {
741 saccum += actionRects.at(i).height();
742 if (actions.at(i) == action) {
743 if (location == QMenuScroller::ScrollCenter)
744 newOffset = ((q->height() / 2) - botScroll) - (saccum - topScroll);
746 newOffset = (q->height() - botScroll) - saccum;
754 //figure out which scroll flags
755 uint newScrollFlags = QMenuScroller::ScrollNone;
756 if (newOffset < 0) //easy and cheap one
757 newScrollFlags |= QMenuScroller::ScrollUp;
758 int saccum = newOffset;
759 for(int i = 0; i < actionRects.count(); i++) {
760 saccum += actionRects.at(i).height();
761 if (saccum > q->height()) {
762 newScrollFlags |= QMenuScroller::ScrollDown;
767 if (!(newScrollFlags & QMenuScroller::ScrollDown) && (scroll->scrollFlags & QMenuScroller::ScrollDown)) {
768 newOffset = q->height() - (saccum - newOffset) - fw*2 - vmargin; //last item at bottom
771 if (!(newScrollFlags & QMenuScroller::ScrollUp) && (scroll->scrollFlags & QMenuScroller::ScrollUp)) {
772 newOffset = 0; //first item at top
775 if (newScrollFlags & QMenuScroller::ScrollUp)
776 newOffset -= vmargin;
778 QRect screen = popupGeometry(q);
779 const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q);
780 if (q->height() < screen.height()-(desktopFrame*2)-1) {
781 QRect geom = q->geometry();
782 if (newOffset > scroll->scrollOffset && (scroll->scrollFlags & newScrollFlags & QMenuScroller::ScrollUp)) { //scroll up
783 const int newHeight = geom.height()-(newOffset-scroll->scrollOffset);
784 if(newHeight > geom.height())
785 geom.setHeight(newHeight);
786 } else if(scroll->scrollFlags & newScrollFlags & QMenuScroller::ScrollDown) {
787 int newTop = geom.top() + (newOffset-scroll->scrollOffset);
788 if (newTop < desktopFrame+screen.top())
789 newTop = desktopFrame+screen.top();
790 if (newTop < geom.top()) {
793 newScrollFlags &= ~QMenuScroller::ScrollUp;
796 if (geom.bottom() > screen.bottom() - desktopFrame)
797 geom.setBottom(screen.bottom() - desktopFrame);
798 if (geom.top() < desktopFrame+screen.top())
799 geom.setTop(desktopFrame+screen.top());
800 if (geom != q->geometry()) {
802 if (newScrollFlags & QMenuScroller::ScrollDown &&
803 q->geometry().top() - geom.top() >= -newOffset)
804 newScrollFlags &= ~QMenuScroller::ScrollDown;
806 q->setGeometry(geom);
810 //actually update flags
811 const int delta = qMin(0, newOffset) - scroll->scrollOffset; //make sure the new offset is always negative
812 if (!itemsDirty && delta) {
813 //we've scrolled so we need to update the action rects
814 for (int i = 0; i < actionRects.count(); ++i) {
815 QRect ¤t = actionRects[i];
816 current.moveTop(current.top() + delta);
818 //we need to update the widgets geometry
819 if (QWidget *w = widgetItems.value(actions.at(i)))
820 w->setGeometry(current);
823 scroll->scrollOffset += delta;
824 scroll->scrollFlags = newScrollFlags;
826 setCurrentAction(action);
828 q->update(); //issue an update so we see all the new state..
831 void QMenuPrivate::scrollMenu(QMenuScroller::ScrollLocation location, bool active)
835 if(location == QMenuScroller::ScrollBottom) {
836 for(int i = actions.size()-1; i >= 0; --i) {
837 QAction *act = actions.at(i);
838 if (actionRects.at(i).isNull())
840 if (!act->isSeparator() &&
841 (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
842 || act->isEnabled())) {
843 if(scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
844 scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollBottom, active);
846 setCurrentAction(act, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
850 } else if(location == QMenuScroller::ScrollTop) {
851 for(int i = 0; i < actions.size(); ++i) {
852 QAction *act = actions.at(i);
853 if (actionRects.at(i).isNull())
855 if (!act->isSeparator() &&
856 (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
857 || act->isEnabled())) {
858 if(scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
859 scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollTop, active);
861 setCurrentAction(act, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
869 void QMenuPrivate::scrollMenu(QMenuScroller::ScrollDirection direction, bool page, bool active)
872 if (!scroll || !(scroll->scrollFlags & direction)) //not really possible...
875 const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp) ? scrollerHeight() : 0;
876 const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollerHeight() : 0;
877 const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
878 const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
879 const int offset = topScroll ? topScroll-vmargin : 0;
880 if (direction == QMenuScroller::ScrollUp) {
881 for(int i = 0, saccum = 0; i < actions.count(); i++) {
882 saccum -= actionRects.at(i).height();
883 if (saccum <= scroll->scrollOffset-offset) {
884 scrollMenu(actions.at(i), page ? QMenuScroller::ScrollBottom : QMenuScroller::ScrollTop, active);
888 } else if (direction == QMenuScroller::ScrollDown) {
889 bool scrolled = false;
890 for(int i = 0, saccum = 0; i < actions.count(); i++) {
891 const int iHeight = actionRects.at(i).height();
893 if (saccum <= scroll->scrollOffset-offset) {
894 const int scrollerArea = q->height() - botScroll - fw*2;
895 int visible = (scroll->scrollOffset-offset) - saccum;
896 for(i++ ; i < actions.count(); i++) {
897 visible += actionRects.at(i).height();
898 if (visible > scrollerArea - topScroll) {
900 scrollMenu(actions.at(i), page ? QMenuScroller::ScrollTop : QMenuScroller::ScrollBottom, active);
908 scroll->scrollFlags &= ~QMenuScroller::ScrollDown;
914 /* This is poor-mans eventfilters. This avoids the use of
915 eventFilter (which can be nasty for users of QMenuBar's). */
916 bool QMenuPrivate::mouseEventTaken(QMouseEvent *e)
919 QPoint pos = q->mapFromGlobal(e->globalPos());
920 if (scroll && !activeMenu) { //let the scroller "steal" the event
921 bool isScroll = false;
922 if (pos.x() >= 0 && pos.x() < q->width()) {
923 for(int dir = QMenuScroller::ScrollUp; dir <= QMenuScroller::ScrollDown; dir = dir << 1) {
924 if (scroll->scrollFlags & dir) {
925 if (dir == QMenuScroller::ScrollUp)
926 isScroll = (pos.y() <= scrollerHeight());
927 else if (dir == QMenuScroller::ScrollDown)
928 isScroll = (pos.y() >= q->height() - scrollerHeight());
930 scroll->scrollDirection = dir;
937 scroll->scrollTimer.start(50, q);
940 scroll->scrollTimer.stop();
944 if (tearoff) { //let the tear off thingie "steal" the event..
945 QRect tearRect(0, 0, q->width(), q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q));
946 if (scroll && scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
947 tearRect.translate(0, scrollerHeight());
949 if (tearRect.contains(pos) && hasMouseMoved(e->globalPos())) {
951 tearoffHighlighted = 1;
952 if (e->type() == QEvent::MouseButtonRelease) {
954 tornPopup = new QTornOffMenu(q);
955 tornPopup->setGeometry(q->geometry());
961 tearoffHighlighted = 0;
964 if (q->frameGeometry().contains(e->globalPos())) //otherwise if the event is in our rect we want it..
967 for(QWidget *caused = causedPopup.widget; caused;) {
968 bool passOnEvent = false;
969 QWidget *next_widget = 0;
970 QPoint cpos = caused->mapFromGlobal(e->globalPos());
971 #ifndef QT_NO_MENUBAR
972 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
973 passOnEvent = mb->rect().contains(cpos);
976 if (QMenu *m = qobject_cast<QMenu*>(caused)) {
977 passOnEvent = m->rect().contains(cpos);
978 next_widget = m->d_func()->causedPopup.widget;
981 if(e->type() != QEvent::MouseButtonRelease || mouseDown == caused) {
982 QMouseEvent new_e(e->type(), cpos, caused->mapTo(caused->topLevelWidget(), cpos), e->screenPos(),
983 e->button(), e->buttons(), e->modifiers());
984 QApplication::sendEvent(caused, &new_e);
990 caused = next_widget;
995 void QMenuPrivate::activateCausedStack(const QList<QPointer<QWidget> > &causedStack, QAction *action, QAction::ActionEvent action_e, bool self)
997 QBoolBlocker guard(activationRecursionGuard);
999 action->activate(action_e);
1001 for(int i = 0; i < causedStack.size(); ++i) {
1002 QPointer<QWidget> widget = causedStack.at(i);
1006 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
1007 widget = qmenu->d_func()->causedPopup.widget;
1008 if (action_e == QAction::Trigger) {
1009 emit qmenu->triggered(action);
1010 } else if (action_e == QAction::Hover) {
1011 emit qmenu->hovered(action);
1013 #ifndef QT_NO_MENUBAR
1014 } else if (QMenuBar *qmenubar = qobject_cast<QMenuBar*>(widget)) {
1015 if (action_e == QAction::Trigger) {
1016 emit qmenubar->triggered(action);
1017 } else if (action_e == QAction::Hover) {
1018 emit qmenubar->hovered(action);
1020 break; //nothing more..
1026 void QMenuPrivate::activateAction(QAction *action, QAction::ActionEvent action_e, bool self)
1029 #ifndef QT_NO_WHATSTHIS
1030 bool inWhatsThisMode = QWhatsThis::inWhatsThisMode();
1032 if (!action || !q->isEnabled()
1033 || (action_e == QAction::Trigger
1034 #ifndef QT_NO_WHATSTHIS
1037 && (action->isSeparator() ||!action->isEnabled())))
1040 /* I have to save the caused stack here because it will be undone after popup execution (ie in the hide).
1041 Then I iterate over the list to actually send the events. --Sam
1043 const QList<QPointer<QWidget> > causedStack = calcCausedStack();
1044 if (action_e == QAction::Trigger) {
1045 #ifndef QT_NO_WHATSTHIS
1046 if (!inWhatsThisMode)
1047 actionAboutToTrigger = action;
1050 if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
1053 for(QWidget *widget = QApplication::activePopupWidget(); widget; ) {
1054 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
1057 widget = qmenu->d_func()->causedPopup.widget;
1064 #ifndef QT_NO_WHATSTHIS
1065 if (inWhatsThisMode) {
1066 QString s = action->whatsThis();
1069 QWhatsThis::showText(q->mapToGlobal(actionRect(action).center()), s, q);
1076 activateCausedStack(causedStack, action, action_e, self);
1079 if (action_e == QAction::Hover) {
1080 #ifndef QT_NO_ACCESSIBILITY
1081 if (QAccessible::isActive()) {
1082 int actionIndex = indexOf(action);
1083 QAccessibleEvent focusEvent(q, QAccessible::Focus);
1084 focusEvent.setChild(actionIndex);
1085 QAccessible::updateAccessibility(&focusEvent);
1086 QAccessibleEvent selectionEvent(q, QAccessible::Selection);
1087 focusEvent.setChild(actionIndex);
1088 QAccessible::updateAccessibility(&selectionEvent);
1091 action->showStatusText(topCausedWidget());
1093 actionAboutToTrigger = 0;
1097 void QMenuPrivate::_q_actionTriggered()
1100 if (QAction *action = qobject_cast<QAction *>(q->sender())) {
1101 QPointer<QAction> actionGuard = action;
1102 emit q->triggered(action);
1103 if (!activationRecursionGuard && actionGuard) {
1104 //in case the action has not been activated by the mouse
1105 //we check the parent hierarchy
1106 QList< QPointer<QWidget> > list;
1107 for(QWidget *widget = q->parentWidget(); widget; ) {
1108 if (qobject_cast<QMenu*>(widget)
1109 #ifndef QT_NO_MENUBAR
1110 || qobject_cast<QMenuBar*>(widget)
1113 list.append(widget);
1114 widget = widget->parentWidget();
1119 activateCausedStack(list, action, QAction::Trigger, false);
1124 void QMenuPrivate::_q_actionHovered()
1127 if (QAction * action = qobject_cast<QAction *>(q->sender())) {
1128 emit q->hovered(action);
1132 bool QMenuPrivate::hasMouseMoved(const QPoint &globalPos)
1134 //determines if the mouse has moved (ie its initial position has
1135 //changed by more than QApplication::startDragDistance()
1136 //or if there were at least 6 mouse motions)
1137 return motions > 6 ||
1138 QApplication::startDragDistance() < (mousePopupPos - globalPos).manhattanLength();
1143 Initialize \a option with the values from this menu and information from \a action. This method
1144 is useful for subclasses when they need a QStyleOptionMenuItem, but don't want
1145 to fill in all the information themselves.
1147 \sa QStyleOption::initFrom(), QMenuBar::initStyleOption()
1149 void QMenu::initStyleOption(QStyleOptionMenuItem *option, const QAction *action) const
1151 if (!option || !action)
1155 option->initFrom(this);
1156 option->palette = palette();
1157 option->state = QStyle::State_None;
1159 if (window()->isActiveWindow())
1160 option->state |= QStyle::State_Active;
1161 if (isEnabled() && action->isEnabled()
1162 && (!action->menu() || action->menu()->isEnabled()))
1163 option->state |= QStyle::State_Enabled;
1165 option->palette.setCurrentColorGroup(QPalette::Disabled);
1167 option->font = action->font().resolve(font());
1168 option->fontMetrics = QFontMetrics(option->font);
1170 if (d->currentAction && d->currentAction == action && !d->currentAction->isSeparator()) {
1171 option->state |= QStyle::State_Selected
1172 | (d->mouseDown ? QStyle::State_Sunken : QStyle::State_None);
1175 option->menuHasCheckableItems = d->hasCheckableItems;
1176 if (!action->isCheckable()) {
1177 option->checkType = QStyleOptionMenuItem::NotCheckable;
1179 option->checkType = (action->actionGroup() && action->actionGroup()->isExclusive())
1180 ? QStyleOptionMenuItem::Exclusive : QStyleOptionMenuItem::NonExclusive;
1181 option->checked = action->isChecked();
1184 option->menuItemType = QStyleOptionMenuItem::SubMenu;
1185 else if (action->isSeparator())
1186 option->menuItemType = QStyleOptionMenuItem::Separator;
1187 else if (d->defaultAction == action)
1188 option->menuItemType = QStyleOptionMenuItem::DefaultItem;
1190 option->menuItemType = QStyleOptionMenuItem::Normal;
1191 if (action->isIconVisibleInMenu())
1192 option->icon = action->icon();
1193 QString textAndAccel = action->text();
1194 #ifndef QT_NO_SHORTCUT
1195 if (textAndAccel.indexOf(QLatin1Char('\t')) == -1) {
1196 QKeySequence seq = action->shortcut();
1198 textAndAccel += QLatin1Char('\t') + seq.toString(QKeySequence::NativeText);
1201 option->text = textAndAccel;
1202 option->tabWidth = d->tabWidth;
1203 option->maxIconWidth = d->maxIconWidth;
1204 option->menuRect = rect();
1209 \brief The QMenu class provides a menu widget for use in menu
1210 bars, context menus, and other popup menus.
1212 \ingroup mainwindow-classes
1213 \ingroup basicwidgets
1216 A menu widget is a selection menu. It can be either a pull-down
1217 menu in a menu bar or a standalone context menu. Pull-down menus
1218 are shown by the menu bar when the user clicks on the respective
1219 item or presses the specified shortcut key. Use
1220 QMenuBar::addMenu() to insert a menu into a menu bar. Context
1221 menus are usually invoked by some special keyboard key or by
1222 right-clicking. They can be executed either asynchronously with
1223 popup() or synchronously with exec(). Menus can also be invoked in
1224 response to button presses; these are just like context menus
1225 except for how they are invoked.
1229 \li \inlineimage plastique-menu.png
1230 \li \inlineimage windowsxp-menu.png
1231 \li \inlineimage macintosh-menu.png
1233 \caption Fig. A menu shown in \l{Plastique Style Widget Gallery}{Plastique widget style},
1234 \l{Windows XP Style Widget Gallery}{Windows XP widget style},
1235 and \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
1239 A menu consists of a list of action items. Actions are added with
1240 the addAction(), addActions() and insertAction() functions. An action
1241 is represented vertically and rendered by QStyle. In addition, actions
1242 can have a text label, an optional icon drawn on the very left side,
1243 and shortcut key sequence such as "Ctrl+X".
1245 The existing actions held by a menu can be found with actions().
1247 There are four kinds of action items: separators, actions that
1248 show a submenu, widgets, and actions that perform an action.
1249 Separators are inserted with addSeparator(), submenus with addMenu(),
1250 and all other items are considered action items.
1252 When inserting action items you usually specify a receiver and a
1253 slot. The receiver will be notifed whenever the item is
1254 \l{QAction::triggered()}{triggered()}. In addition, QMenu provides
1255 two signals, activated() and highlighted(), which signal the
1256 QAction that was triggered from the menu.
1258 You clear a menu with clear() and remove individual action items
1259 with removeAction().
1261 A QMenu can also provide a tear-off menu. A tear-off menu is a
1262 top-level window that contains a copy of the menu. This makes it
1263 possible for the user to "tear off" frequently used menus and
1264 position them in a convenient place on the screen. If you want
1265 this functionality for a particular menu, insert a tear-off handle
1266 with setTearOffEnabled(). When using tear-off menus, bear in mind
1267 that the concept isn't typically used on Microsoft Windows so
1268 some users may not be familiar with it. Consider using a QToolBar
1271 Widgets can be inserted into menus with the QWidgetAction class.
1272 Instances of this class are used to hold widgets, and are inserted
1273 into menus with the addAction() overload that takes a QAction.
1275 Conversely, actions can be added to widgets with the addAction(),
1276 addActions() and insertAction() functions.
1278 \warning To make QMenu visible on the screen, exec() or popup() should be
1279 used instead of show().
1281 \section1 QMenu on Qt for Windows CE
1283 If a menu is integrated into the native menubar on Windows Mobile we
1284 do not support the signals: aboutToHide (), aboutToShow () and hovered ().
1285 It is not possible to display an icon in a native menu on Windows Mobile.
1287 \section1 QMenu on Mac OS X with Qt build against Cocoa
1289 QMenu can be inserted only once in a menu/menubar. Subsequent insertions will
1290 have no effect or will result in a disabled menu item.
1292 See the \l{mainwindows/menus}{Menus} example for an example of how
1293 to use QMenuBar and QMenu in your application.
1295 \b{Important inherited functions:} addAction(), removeAction(), clear(),
1296 addSeparator(), and addMenu().
1298 \sa QMenuBar, {fowler}{GUI Design Handbook: Menu, Drop-Down and Pop-Up},
1299 {Application Example}, {Menus Example}, {Recent Files Example}
1304 Constructs a menu with parent \a parent.
1306 Although a popup menu is always a top-level widget, if a parent is
1307 passed the popup menu will be deleted when that parent is
1308 destroyed (as with any other QObject).
1310 QMenu::QMenu(QWidget *parent)
1311 : QWidget(*new QMenuPrivate, parent, Qt::Popup)
1318 Constructs a menu with a \a title and a \a parent.
1320 Although a popup menu is always a top-level widget, if a parent is
1321 passed the popup menu will be deleted when that parent is
1322 destroyed (as with any other QObject).
1326 QMenu::QMenu(const QString &title, QWidget *parent)
1327 : QWidget(*new QMenuPrivate, parent, Qt::Popup)
1331 d->menuAction->setText(title);
1336 QMenu::QMenu(QMenuPrivate &dd, QWidget *parent)
1337 : QWidget(dd, parent, Qt::Popup)
1349 if (!d->widgetItems.isEmpty()) { // avoid detach on shared null hash
1350 QHash<QAction *, QWidget *>::iterator it = d->widgetItems.begin();
1351 for (; it != d->widgetItems.end(); ++it) {
1352 if (QWidget *widget = it.value()) {
1353 QWidgetAction *action = static_cast<QWidgetAction *>(it.key());
1354 action->releaseWidget(widget);
1361 d->eventLoop->exit();
1368 This convenience function creates a new action with \a text.
1369 The function adds the newly created action to the menu's
1370 list of actions, and returns it.
1372 \sa QWidget::addAction()
1374 QAction *QMenu::addAction(const QString &text)
1376 QAction *ret = new QAction(text, this);
1384 This convenience function creates a new action with an \a icon
1385 and some \a text. The function adds the newly created action to
1386 the menu's list of actions, and returns it.
1388 \sa QWidget::addAction()
1390 QAction *QMenu::addAction(const QIcon &icon, const QString &text)
1392 QAction *ret = new QAction(icon, text, this);
1400 This convenience function creates a new action with the text \a
1401 text and an optional shortcut \a shortcut. The action's
1402 \l{QAction::triggered()}{triggered()} signal is connected to the
1403 \a receiver's \a member slot. The function adds the newly created
1404 action to the menu's list of actions and returns it.
1406 \sa QWidget::addAction()
1408 QAction *QMenu::addAction(const QString &text, const QObject *receiver, const char* member, const QKeySequence &shortcut)
1410 QAction *action = new QAction(text, this);
1411 #ifdef QT_NO_SHORTCUT
1414 action->setShortcut(shortcut);
1416 QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
1424 This convenience function creates a new action with an \a icon and
1425 some \a text and an optional shortcut \a shortcut. The action's
1426 \l{QAction::triggered()}{triggered()} signal is connected to the
1427 \a member slot of the \a receiver object. The function adds the
1428 newly created action to the menu's list of actions, and returns it.
1430 \sa QWidget::addAction()
1432 QAction *QMenu::addAction(const QIcon &icon, const QString &text, const QObject *receiver,
1433 const char* member, const QKeySequence &shortcut)
1435 QAction *action = new QAction(icon, text, this);
1436 #ifdef QT_NO_SHORTCUT
1439 action->setShortcut(shortcut);
1441 QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
1447 This convenience function adds \a menu as a submenu to this menu.
1448 It returns \a menu's menuAction(). This menu does not take
1449 ownership of \a menu.
1451 \sa QWidget::addAction(), QMenu::menuAction()
1453 QAction *QMenu::addMenu(QMenu *menu)
1455 QAction *action = menu->menuAction();
1461 Appends a new QMenu with \a title to the menu. The menu
1462 takes ownership of the menu. Returns the new menu.
1464 \sa QWidget::addAction(), QMenu::menuAction()
1466 QMenu *QMenu::addMenu(const QString &title)
1468 QMenu *menu = new QMenu(title, this);
1469 addAction(menu->menuAction());
1474 Appends a new QMenu with \a icon and \a title to the menu. The menu
1475 takes ownership of the menu. Returns the new menu.
1477 \sa QWidget::addAction(), QMenu::menuAction()
1479 QMenu *QMenu::addMenu(const QIcon &icon, const QString &title)
1481 QMenu *menu = new QMenu(title, this);
1482 menu->setIcon(icon);
1483 addAction(menu->menuAction());
1488 This convenience function creates a new separator action, i.e. an
1489 action with QAction::isSeparator() returning true, and adds the new
1490 action to this menu's list of actions. It returns the newly
1493 \sa QWidget::addAction()
1495 QAction *QMenu::addSeparator()
1497 QAction *action = new QAction(this);
1498 action->setSeparator(true);
1504 This convenience function inserts \a menu before action \a before
1505 and returns the menus menuAction().
1507 \sa QWidget::insertAction(), addMenu()
1509 QAction *QMenu::insertMenu(QAction *before, QMenu *menu)
1511 QAction *action = menu->menuAction();
1512 insertAction(before, action);
1517 This convenience function creates a new separator action, i.e. an
1518 action with QAction::isSeparator() returning true. The function inserts
1519 the newly created action into this menu's list of actions before
1520 action \a before and returns it.
1522 \sa QWidget::insertAction(), addSeparator()
1524 QAction *QMenu::insertSeparator(QAction *before)
1526 QAction *action = new QAction(this);
1527 action->setSeparator(true);
1528 insertAction(before, action);
1533 This sets the default action to \a act. The default action may have
1534 a visual cue, depending on the current QStyle. A default action
1535 usually indicates what will happen by default when a drop occurs.
1539 void QMenu::setDefaultAction(QAction *act)
1541 d_func()->defaultAction = act;
1545 Returns the current default action.
1547 \sa setDefaultAction()
1549 QAction *QMenu::defaultAction() const
1551 return d_func()->defaultAction;
1555 \property QMenu::tearOffEnabled
1556 \brief whether the menu supports being torn off
1558 When true, the menu contains a special tear-off item (often shown as a dashed
1559 line at the top of the menu) that creates a copy of the menu when it is
1562 This "torn-off" copy lives in a separate window. It contains the same menu
1563 items as the original menu, with the exception of the tear-off handle.
1565 By default, this property is false.
1567 void QMenu::setTearOffEnabled(bool b)
1570 if (d->tearoff == b)
1576 d->itemsDirty = true;
1581 bool QMenu::isTearOffEnabled() const
1583 return d_func()->tearoff;
1587 When a menu is torn off a second menu is shown to display the menu
1588 contents in a new window. When the menu is in this mode and the menu
1589 is visible returns true; otherwise false.
1591 \sa hideTearOffMenu(), isTearOffEnabled()
1593 bool QMenu::isTearOffMenuVisible() const
1595 if (d_func()->tornPopup)
1596 return d_func()->tornPopup->isVisible();
1601 This function will forcibly hide the torn off menu making it
1602 disappear from the users desktop.
1604 \sa isTearOffMenuVisible(), isTearOffEnabled()
1606 void QMenu::hideTearOffMenu()
1608 if (QWidget *w = d_func()->tornPopup)
1614 Sets the currently highlighted action to \a act.
1616 void QMenu::setActiveAction(QAction *act)
1619 d->setCurrentAction(act, 0);
1621 d->scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollCenter);
1626 Returns the currently highlighted action, or 0 if no
1627 action is currently highlighted.
1629 QAction *QMenu::activeAction() const
1631 return d_func()->currentAction;
1637 Returns true if there are no visible actions inserted into the menu, false
1640 \sa QWidget::actions()
1643 bool QMenu::isEmpty() const
1646 for(int i = 0; ret && i < actions().count(); ++i) {
1647 const QAction *action = actions().at(i);
1648 if (!action->isSeparator() && action->isVisible()) {
1656 Removes all the menu's actions. Actions owned by the menu and not
1657 shown in any other widget are deleted.
1663 QList<QAction*> acts = actions();
1665 for(int i = 0; i < acts.size(); i++) {
1666 #ifdef QT_SOFTKEYS_ENABLED
1668 // Lets not touch to our internal softkey actions
1669 if(acts[i] == d->selectAction || acts[i] == d->cancelAction)
1672 removeAction(acts[i]);
1673 if (acts[i]->parent() == this && acts[i]->d_func()->widgets.isEmpty())
1679 If a menu does not fit on the screen it lays itself out so that it
1680 does fit. It is style dependent what layout means (for example, on
1681 Windows it will use multiple columns).
1683 This functions returns the number of columns necessary.
1685 int QMenu::columnCount() const
1687 return d_func()->ncols;
1691 Returns the item at \a pt; returns 0 if there is no item there.
1693 QAction *QMenu::actionAt(const QPoint &pt) const
1695 if (QAction *ret = d_func()->actionAt(pt))
1701 Returns the geometry of action \a act.
1703 QRect QMenu::actionGeometry(QAction *act) const
1705 return d_func()->actionRect(act);
1711 QSize QMenu::sizeHint() const
1714 d->updateActionRects();
1717 for (int i = 0; i < d->actionRects.count(); ++i) {
1718 const QRect &rect = d->actionRects.at(i);
1721 if (rect.bottom() >= s.height())
1722 s.setHeight(rect.y() + rect.height());
1723 if (rect.right() >= s.width())
1724 s.setWidth(rect.x() + rect.width());
1726 // Note that the action rects calculated above already include
1727 // the top and left margins, so we only need to add margins for
1728 // the bottom and right.
1729 QStyleOption opt(0);
1731 const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, this);
1732 s.rwidth() += style()->pixelMetric(QStyle::PM_MenuHMargin, &opt, this) + fw + d->rightmargin;
1733 s.rheight() += style()->pixelMetric(QStyle::PM_MenuVMargin, &opt, this) + fw + d->bottommargin;
1735 return style()->sizeFromContents(QStyle::CT_Menu, &opt,
1736 s.expandedTo(QApplication::globalStrut()), this);
1740 Displays the menu so that the action \a atAction will be at the
1741 specified \e global position \a p. To translate a widget's local
1742 coordinates into global coordinates, use QWidget::mapToGlobal().
1744 When positioning a menu with exec() or popup(), bear in mind that
1745 you cannot rely on the menu's current size(). For performance
1746 reasons, the menu adapts its size only when necessary, so in many
1747 cases, the size before and after the show is different. Instead,
1748 use sizeHint() which calculates the proper size depending on the
1749 menu's current contents.
1751 \sa QWidget::mapToGlobal(), exec()
1753 void QMenu::popup(const QPoint &p, QAction *atAction)
1756 if (d->scroll) { // reset scroll state from last popup
1757 d->scroll->scrollOffset = 0;
1758 d->scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
1760 d->tearoffHighlighted = 0;
1762 d->doChildEffects = true;
1763 d->updateLayoutDirection();
1765 #ifndef QT_NO_MENUBAR
1766 // if this menu is part of a chain attached to a QMenuBar, set the
1767 // _NET_WM_WINDOW_TYPE_DROPDOWN_MENU X11 window type
1768 setAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu, qobject_cast<QMenuBar *>(d->topCausedWidget()) != 0);
1771 ensurePolished(); // Get the right font
1773 const bool actionListChanged = d->itemsDirty;
1774 d->updateActionRects();
1776 QPushButton *causedButton = qobject_cast<QPushButton*>(d->causedPopup.widget);
1777 if (actionListChanged && causedButton)
1778 pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition();
1782 QSize size = sizeHint();
1784 #ifndef QT_NO_GRAPHICSVIEW
1785 bool isEmbedded = !bypassGraphicsProxyWidget(this) && d->nearestGraphicsProxyWidget(this);
1787 screen = d->popupGeometry(this);
1790 screen = d->popupGeometry(QApplication::desktop()->screenNumber(p));
1791 const int desktopFrame = style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, this);
1792 bool adjustToDesktop = !window()->testAttribute(Qt::WA_DontShowOnScreen);
1794 // if the screens have very different geometries and the menu is too big, we have to recalculate
1795 if (size.height() > screen.height() || size.width() > screen.width()) {
1796 size = d->adjustMenuSizeForScreen(screen);
1797 adjustToDesktop = true;
1799 // Layout is not right, we might be able to save horizontal space
1800 if (d->ncols >1 && size.height() < screen.height()) {
1801 size = d->adjustMenuSizeForScreen(screen);
1802 adjustToDesktop = true;
1805 #ifdef QT_KEYPAD_NAVIGATION
1806 if (!atAction && QApplication::keypadNavigationEnabled()) {
1807 // Try to have one item activated
1808 if (d->defaultAction && d->defaultAction->isEnabled()) {
1809 atAction = d->defaultAction;
1810 // TODO: This works for first level menus, not yet sub menus
1812 foreach (QAction *action, d->actions)
1813 if (action->isEnabled()) {
1818 d->currentAction = atAction;
1822 pos.setY(screen.top() + desktopFrame);
1823 } else if (atAction) {
1824 for (int i = 0, above_height = 0; i < d->actions.count(); i++) {
1825 QAction *action = d->actions.at(i);
1826 if (action == atAction) {
1827 int newY = pos.y() - above_height;
1828 if (d->scroll && newY < desktopFrame) {
1829 d->scroll->scrollFlags = d->scroll->scrollFlags
1830 | QMenuPrivate::QMenuScroller::ScrollUp;
1831 d->scroll->scrollOffset = newY;
1832 newY = desktopFrame;
1836 if (d->scroll && d->scroll->scrollFlags != QMenuPrivate::QMenuScroller::ScrollNone
1837 && !style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll, 0, this)) {
1838 int below_height = above_height + d->scroll->scrollOffset;
1839 for (int i2 = i; i2 < d->actionRects.count(); i2++)
1840 below_height += d->actionRects.at(i2).height();
1841 size.setHeight(below_height);
1845 above_height += d->actionRects.at(i).height();
1850 QPoint mouse = QCursor::pos();
1851 d->mousePopupPos = mouse;
1852 const bool snapToMouse = (QRect(p.x() - 3, p.y() - 3, 6, 6).contains(mouse));
1854 if (adjustToDesktop) {
1855 // handle popup falling "off screen"
1856 if (isRightToLeft()) {
1857 if (snapToMouse) // position flowing left from the mouse
1858 pos.setX(mouse.x() - size.width());
1860 #ifndef QT_NO_MENUBAR
1861 // if in a menubar, it should be right-aligned
1862 if (qobject_cast<QMenuBar*>(d->causedPopup.widget))
1863 pos.rx() -= size.width();
1864 #endif //QT_NO_MENUBAR
1866 if (pos.x() < screen.left() + desktopFrame)
1867 pos.setX(qMax(p.x(), screen.left() + desktopFrame));
1868 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
1869 pos.setX(qMax(p.x() - size.width(), screen.right() - desktopFrame - size.width() + 1));
1871 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
1872 pos.setX(screen.right() - desktopFrame - size.width() + 1);
1873 if (pos.x() < screen.left() + desktopFrame)
1874 pos.setX(screen.left() + desktopFrame);
1876 if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
1878 pos.setY(qMin(mouse.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
1880 pos.setY(qMax(p.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
1881 } else if (pos.y() < screen.top() + desktopFrame) {
1882 pos.setY(screen.top() + desktopFrame);
1885 if (pos.y() < screen.top() + desktopFrame)
1886 pos.setY(screen.top() + desktopFrame);
1887 if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
1889 d->scroll->scrollFlags |= uint(QMenuPrivate::QMenuScroller::ScrollDown);
1890 int y = qMax(screen.y(),pos.y());
1891 size.setHeight(screen.bottom() - (desktopFrame * 2) - y);
1893 // Too big for screen, bias to see bottom of menu (for some reason)
1894 pos.setY(screen.bottom() - size.height() + 1);
1898 const int subMenuOffset = style()->pixelMetric(QStyle::PM_SubMenuOverlap, 0, this);
1899 const QSize menuSize(sizeHint());
1900 QMenu *caused = qobject_cast<QMenu*>(d_func()->causedPopup.widget);
1901 if (caused && caused->geometry().width() + menuSize.width() + subMenuOffset < screen.width()) {
1902 QRect parentActionRect(caused->d_func()->actionRect(caused->d_func()->currentAction));
1903 const QPoint actionTopLeft = caused->mapToGlobal(parentActionRect.topLeft());
1904 parentActionRect.moveTopLeft(actionTopLeft);
1905 if (isRightToLeft()) {
1906 if ((pos.x() + menuSize.width() > parentActionRect.left() - subMenuOffset)
1907 && (pos.x() < parentActionRect.right()))
1909 pos.rx() = parentActionRect.left() - menuSize.width();
1910 if (pos.x() < screen.x())
1911 pos.rx() = parentActionRect.right();
1912 if (pos.x() + menuSize.width() > screen.x() + screen.width())
1913 pos.rx() = screen.x();
1916 if ((pos.x() < parentActionRect.right() + subMenuOffset)
1917 && (pos.x() + menuSize.width() > parentActionRect.left()))
1919 pos.rx() = parentActionRect.right();
1920 if (pos.x() + menuSize.width() > screen.x() + screen.width())
1921 pos.rx() = parentActionRect.left() - menuSize.width();
1922 if (pos.x() < screen.x())
1923 pos.rx() = screen.x() + screen.width() - menuSize.width();
1927 setGeometry(QRect(pos, size));
1928 #ifndef QT_NO_EFFECTS
1929 int hGuess = isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll;
1930 int vGuess = QEffects::DownScroll;
1931 if (isRightToLeft()) {
1932 if ((snapToMouse && (pos.x() + size.width() / 2 > mouse.x())) ||
1933 (qobject_cast<QMenu*>(d->causedPopup.widget) && pos.x() + size.width() / 2 > d->causedPopup.widget->x()))
1934 hGuess = QEffects::RightScroll;
1936 if ((snapToMouse && (pos.x() + size.width() / 2 < mouse.x())) ||
1937 (qobject_cast<QMenu*>(d->causedPopup.widget) && pos.x() + size.width() / 2 < d->causedPopup.widget->x()))
1938 hGuess = QEffects::LeftScroll;
1941 #ifndef QT_NO_MENUBAR
1942 if ((snapToMouse && (pos.y() + size.height() / 2 < mouse.y())) ||
1943 (qobject_cast<QMenuBar*>(d->causedPopup.widget) &&
1944 pos.y() + size.width() / 2 < d->causedPopup.widget->mapToGlobal(d->causedPopup.widget->pos()).y()))
1945 vGuess = QEffects::UpScroll;
1947 if (QApplication::isEffectEnabled(Qt::UI_AnimateMenu)) {
1948 bool doChildEffects = true;
1949 #ifndef QT_NO_MENUBAR
1950 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->causedPopup.widget)) {
1951 doChildEffects = mb->d_func()->doChildEffects;
1952 mb->d_func()->doChildEffects = false;
1955 if (QMenu *m = qobject_cast<QMenu*>(d->causedPopup.widget)) {
1956 doChildEffects = m->d_func()->doChildEffects;
1957 m->d_func()->doChildEffects = false;
1960 if (doChildEffects) {
1961 if (QApplication::isEffectEnabled(Qt::UI_FadeMenu))
1963 else if (d->causedPopup.widget)
1964 qScrollEffect(this, qobject_cast<QMenu*>(d->causedPopup.widget) ? hGuess : vGuess);
1966 qScrollEffect(this, hGuess | vGuess);
1968 // kill any running effect
1980 #ifndef QT_NO_ACCESSIBILITY
1981 QAccessibleEvent event(this, QAccessible::PopupMenuStart);
1982 QAccessible::updateAccessibility(&event);
1987 Executes this menu synchronously.
1989 This is equivalent to \c{exec(pos())}.
1991 This returns the triggered QAction in either the popup menu or one
1992 of its submenus, or 0 if no item was triggered (normally because
1993 the user pressed Esc).
1995 In most situations you'll want to specify the position yourself,
1996 for example, the current mouse position:
1997 \snippet code/src_gui_widgets_qmenu.cpp 0
1998 or aligned to a widget:
1999 \snippet code/src_gui_widgets_qmenu.cpp 1
2000 or in reaction to a QMouseEvent *e:
2001 \snippet code/src_gui_widgets_qmenu.cpp 2
2003 QAction *QMenu::exec()
2012 Executes this menu synchronously.
2014 Pops up the menu so that the action \a action will be at the
2015 specified \e global position \a p. To translate a widget's local
2016 coordinates into global coordinates, use QWidget::mapToGlobal().
2018 This returns the triggered QAction in either the popup menu or one
2019 of its submenus, or 0 if no item was triggered (normally because
2020 the user pressed Esc).
2022 Note that all signals are emitted as usual. If you connect a
2023 QAction to a slot and call the menu's exec(), you get the result
2024 both via the signal-slot connection and in the return value of
2027 Common usage is to position the menu at the current mouse
2029 \snippet code/src_gui_widgets_qmenu.cpp 3
2030 or aligned to a widget:
2031 \snippet code/src_gui_widgets_qmenu.cpp 4
2032 or in reaction to a QMouseEvent *e:
2033 \snippet code/src_gui_widgets_qmenu.cpp 5
2035 When positioning a menu with exec() or popup(), bear in mind that
2036 you cannot rely on the menu's current size(). For performance
2037 reasons, the menu adapts its size only when necessary. So in many
2038 cases, the size before and after the show is different. Instead,
2039 use sizeHint() which calculates the proper size depending on the
2040 menu's current contents.
2042 \sa popup(), QWidget::mapToGlobal()
2044 QAction *QMenu::exec(const QPoint &p, QAction *action)
2048 QEventLoop eventLoop;
2049 d->eventLoop = &eventLoop;
2052 QPointer<QObject> guard = this;
2053 (void) eventLoop.exec();
2057 action = d->syncAction;
2066 Executes a menu synchronously.
2068 The menu's actions are specified by the list of \a actions. The menu will
2069 pop up so that the specified action, \a at, appears at global position \a
2070 pos. If \a at is not specified then the menu appears at position \a
2071 pos. \a parent is the menu's parent widget; specifying the parent will
2072 provide context when \a pos alone is not enough to decide where the menu
2073 should go (e.g., with multiple desktops or when the parent is embedded in
2076 The function returns the triggered QAction in either the popup
2077 menu or one of its submenus, or 0 if no item was triggered
2078 (normally because the user pressed Esc).
2080 This is equivalent to:
2081 \snippet code/src_gui_widgets_qmenu.cpp 6
2083 \sa popup(), QWidget::mapToGlobal()
2085 QAction *QMenu::exec(QList<QAction*> actions, const QPoint &pos, QAction *at, QWidget *parent)
2088 menu.addActions(actions);
2089 return menu.exec(pos, at);
2095 void QMenu::hideEvent(QHideEvent *)
2100 d->eventLoop->exit();
2101 d->setCurrentAction(0);
2102 #ifndef QT_NO_ACCESSIBILITY
2103 QAccessibleEvent event(this, QAccessible::PopupMenuEnd);
2104 QAccessible::updateAccessibility(&event);
2106 #ifndef QT_NO_MENUBAR
2107 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->causedPopup.widget))
2108 mb->d_func()->setCurrentAction(0);
2111 d->hasHadMouse = false;
2112 d->causedPopup.widget = 0;
2113 d->causedPopup.action = 0;
2115 d->scroll->scrollTimer.stop(); //make sure the timer stops
2121 void QMenu::paintEvent(QPaintEvent *e)
2124 d->updateActionRects();
2126 QRegion emptyArea = QRegion(rect());
2128 QStyleOptionMenuItem menuOpt;
2129 menuOpt.initFrom(this);
2130 menuOpt.state = QStyle::State_None;
2131 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2132 menuOpt.maxIconWidth = 0;
2133 menuOpt.tabWidth = 0;
2134 style()->drawPrimitive(QStyle::PE_PanelMenu, &menuOpt, &p, this);
2136 //draw the items that need updating..
2137 for (int i = 0; i < d->actions.count(); ++i) {
2138 QAction *action = d->actions.at(i);
2139 QRect adjustedActionRect = d->actionRects.at(i);
2140 if (!e->rect().intersects(adjustedActionRect)
2141 || d->widgetItems.value(action))
2143 //set the clip region to be extra safe (and adjust for the scrollers)
2144 QRegion adjustedActionReg(adjustedActionRect);
2145 emptyArea -= adjustedActionReg;
2146 p.setClipRegion(adjustedActionReg);
2148 QStyleOptionMenuItem opt;
2149 initStyleOption(&opt, action);
2150 opt.rect = adjustedActionRect;
2151 style()->drawControl(QStyle::CE_MenuItem, &opt, &p, this);
2154 const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, this);
2155 //draw the scroller regions..
2157 menuOpt.menuItemType = QStyleOptionMenuItem::Scroller;
2158 menuOpt.state |= QStyle::State_Enabled;
2159 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) {
2160 menuOpt.rect.setRect(fw, fw, width() - (fw * 2), d->scrollerHeight());
2161 emptyArea -= QRegion(menuOpt.rect);
2162 p.setClipRect(menuOpt.rect);
2163 style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this);
2165 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown) {
2166 menuOpt.rect.setRect(fw, height() - d->scrollerHeight() - fw, width() - (fw * 2),
2167 d->scrollerHeight());
2168 emptyArea -= QRegion(menuOpt.rect);
2169 menuOpt.state |= QStyle::State_DownArrow;
2170 p.setClipRect(menuOpt.rect);
2171 style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this);
2174 //paint the tear off..
2176 menuOpt.menuItemType = QStyleOptionMenuItem::TearOff;
2177 menuOpt.rect.setRect(fw, fw, width() - (fw * 2),
2178 style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this));
2179 if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2180 menuOpt.rect.translate(0, d->scrollerHeight());
2181 emptyArea -= QRegion(menuOpt.rect);
2182 p.setClipRect(menuOpt.rect);
2183 menuOpt.state = QStyle::State_None;
2184 if (d->tearoffHighlighted)
2185 menuOpt.state |= QStyle::State_Selected;
2186 style()->drawControl(QStyle::CE_MenuTearoff, &menuOpt, &p, this);
2191 borderReg += QRect(0, 0, fw, height()); //left
2192 borderReg += QRect(width()-fw, 0, fw, height()); //right
2193 borderReg += QRect(0, 0, width(), fw); //top
2194 borderReg += QRect(0, height()-fw, width(), fw); //bottom
2195 p.setClipRegion(borderReg);
2196 emptyArea -= borderReg;
2197 QStyleOptionFrame frame;
2198 frame.rect = rect();
2199 frame.palette = palette();
2200 frame.state = QStyle::State_None;
2201 frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth);
2202 frame.midLineWidth = 0;
2203 style()->drawPrimitive(QStyle::PE_FrameMenu, &frame, &p, this);
2206 //finally the rest of the space
2207 p.setClipRegion(emptyArea);
2208 menuOpt.state = QStyle::State_None;
2209 menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea;
2210 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2211 menuOpt.rect = rect();
2212 menuOpt.menuRect = rect();
2213 style()->drawControl(QStyle::CE_MenuEmptyArea, &menuOpt, &p, this);
2216 #ifndef QT_NO_WHEELEVENT
2220 void QMenu::wheelEvent(QWheelEvent *e)
2223 if (d->scroll && rect().contains(e->pos()))
2224 d->scrollMenu(e->delta() > 0 ?
2225 QMenuPrivate::QMenuScroller::ScrollUp : QMenuPrivate::QMenuScroller::ScrollDown);
2232 void QMenu::mousePressEvent(QMouseEvent *e)
2235 if (d->aboutToHide || d->mouseEventTaken(e))
2237 if (!rect().contains(e->pos())) {
2239 && QRect(d->noReplayFor->mapToGlobal(QPoint()), d->noReplayFor->size()).contains(e->globalPos()))
2240 setAttribute(Qt::WA_NoMouseReplay);
2241 if (d->eventLoop) // synchronous operation
2243 d->hideUpToMenuBar();
2246 d->mouseDown = this;
2248 QAction *action = d->actionAt(e->pos());
2249 d->setCurrentAction(action, 20);
2256 void QMenu::mouseReleaseEvent(QMouseEvent *e)
2259 if (d->aboutToHide || d->mouseEventTaken(e))
2261 if(d->mouseDown != this) {
2268 QAction *action = d->actionAt(e->pos());
2270 if (action && action == d->currentAction) {
2271 if (!action->menu()){
2272 #if defined(Q_OS_WIN)
2273 //On Windows only context menus can be activated with the right button
2274 if (e->button() == Qt::LeftButton || d->topCausedWidget() == 0)
2276 d->activateAction(action, QAction::Trigger);
2278 } else if (d->hasMouseMoved(e->globalPos())) {
2279 d->hideUpToMenuBar();
2286 void QMenu::changeEvent(QEvent *e)
2289 if (e->type() == QEvent::StyleChange || e->type() == QEvent::FontChange ||
2290 e->type() == QEvent::LayoutDirectionChange) {
2292 setMouseTracking(style()->styleHint(QStyle::SH_Menu_MouseTracking, 0, this));
2295 if (!style()->styleHint(QStyle::SH_Menu_Scrollable, 0, this)) {
2298 } else if (!d->scroll) {
2299 d->scroll = new QMenuPrivate::QMenuScroller;
2300 d->scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
2302 } else if (e->type() == QEvent::EnabledChange) {
2303 if (d->tornPopup) // torn-off menu
2304 d->tornPopup->setEnabled(isEnabled());
2305 d->menuAction->setEnabled(isEnabled());
2306 if (d->platformMenu)
2307 d->platformMenu->setEnabled(isEnabled());
2309 QWidget::changeEvent(e);
2317 QMenu::event(QEvent *e)
2320 switch (e->type()) {
2321 case QEvent::Polish:
2322 d->updateLayoutDirection();
2324 case QEvent::ShortcutOverride: {
2325 QKeyEvent *kev = static_cast<QKeyEvent*>(e);
2326 if (kev->key() == Qt::Key_Up || kev->key() == Qt::Key_Down
2327 || kev->key() == Qt::Key_Left || kev->key() == Qt::Key_Right
2328 || kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return
2329 || kev->key() == Qt::Key_Escape) {
2335 case QEvent::KeyPress: {
2336 QKeyEvent *ke = (QKeyEvent*)e;
2337 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
2342 case QEvent::ContextMenu:
2343 if(d->menuDelayTimer.isActive()) {
2344 d->menuDelayTimer.stop();
2345 internalDelayedPopup();
2348 case QEvent::Resize: {
2349 QStyleHintReturnMask menuMask;
2350 QStyleOption option;
2351 option.initFrom(this);
2352 if (style()->styleHint(QStyle::SH_Menu_Mask, &option, this, &menuMask)) {
2353 setMask(menuMask.region);
2356 d->updateActionRects();
2360 d->updateActionRects();
2361 if (d->currentAction)
2362 d->popupAction(d->currentAction, 0, false);
2364 #ifndef QT_NO_WHATSTHIS
2365 case QEvent::QueryWhatsThis:
2366 e->setAccepted(d->whatsThis.size());
2367 if (QAction *action = d->actionAt(static_cast<QHelpEvent*>(e)->pos())) {
2368 if (action->whatsThis().size() || action->menu())
2373 #ifdef QT_SOFTKEYS_ENABLED
2374 case QEvent::LanguageChange: {
2375 d->selectAction->setText(QSoftKeyManager::standardSoftKeyText(QSoftKeyManager::SelectSoftKey));
2376 d->cancelAction->setText(QSoftKeyManager::standardSoftKeyText(QSoftKeyManager::CancelSoftKey));
2383 return QWidget::event(e);
2389 bool QMenu::focusNextPrevChild(bool next)
2392 QKeyEvent ev(QEvent::KeyPress, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier);
2400 void QMenu::keyPressEvent(QKeyEvent *e)
2403 d->updateActionRects();
2405 if (isRightToLeft()) { // in reverse mode open/close key for submenues are reversed
2406 if (key == Qt::Key_Left)
2407 key = Qt::Key_Right;
2408 else if (key == Qt::Key_Right)
2412 if (key == Qt::Key_Tab) //means down
2414 if (key == Qt::Key_Backtab) //means up
2418 bool key_consumed = false;
2421 key_consumed = true;
2423 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop, true);
2426 key_consumed = true;
2428 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom, true);
2430 case Qt::Key_PageUp:
2431 key_consumed = true;
2432 if (d->currentAction && d->scroll) {
2433 if(d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2434 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollUp, true, true);
2436 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop, true);
2439 case Qt::Key_PageDown:
2440 key_consumed = true;
2441 if (d->currentAction && d->scroll) {
2442 if(d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
2443 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollDown, true, true);
2445 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom, true);
2449 case Qt::Key_Down: {
2450 key_consumed = true;
2451 QAction *nextAction = 0;
2452 QMenuPrivate::QMenuScroller::ScrollLocation scroll_loc = QMenuPrivate::QMenuScroller::ScrollStay;
2453 if (!d->currentAction) {
2454 if(key == Qt::Key_Down) {
2455 for(int i = 0; i < d->actions.count(); ++i) {
2456 QAction *act = d->actions.at(i);
2457 if (d->actionRects.at(i).isNull())
2459 if (!act->isSeparator() &&
2460 (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
2461 || act->isEnabled())) {
2467 for(int i = d->actions.count()-1; i >= 0; --i) {
2468 QAction *act = d->actions.at(i);
2469 if (d->actionRects.at(i).isNull())
2471 if (!act->isSeparator() &&
2472 (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
2473 || act->isEnabled())) {
2480 for(int i = 0, y = 0; !nextAction && i < d->actions.count(); i++) {
2481 QAction *act = d->actions.at(i);
2482 if (act == d->currentAction) {
2483 if (key == Qt::Key_Up) {
2484 for(int next_i = i-1; true; next_i--) {
2486 if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this))
2489 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
2490 next_i = d->actionRects.count()-1;
2492 QAction *next = d->actions.at(next_i);
2493 if (next == d->currentAction)
2495 if (d->actionRects.at(next_i).isNull())
2497 if (next->isSeparator() ||
2498 (!next->isEnabled() &&
2499 !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)))
2502 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)) {
2503 int topVisible = d->scrollerHeight();
2505 topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);
2506 if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.at(next_i).height())
2507 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
2511 if (!nextAction && d->tearoff)
2512 d->tearoffHighlighted = 1;
2514 y += d->actionRects.at(i).height();
2515 for(int next_i = i+1; true; next_i++) {
2516 if (next_i == d->actionRects.count()) {
2517 if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this))
2520 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
2523 QAction *next = d->actions.at(next_i);
2524 if (next == d->currentAction)
2526 if (d->actionRects.at(next_i).isNull())
2528 if (next->isSeparator() ||
2529 (!next->isEnabled() &&
2530 !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)))
2533 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)) {
2534 int bottomVisible = height() - d->scrollerHeight();
2535 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2536 bottomVisible -= d->scrollerHeight();
2538 bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);
2539 if ((y + d->scroll->scrollOffset + d->actionRects.at(next_i).height()) > bottomVisible)
2540 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
2547 y += d->actionRects.at(i).height();
2551 if (d->scroll && scroll_loc != QMenuPrivate::QMenuScroller::ScrollStay) {
2552 d->scroll->scrollTimer.stop();
2553 d->scrollMenu(nextAction, scroll_loc);
2555 d->setCurrentAction(nextAction, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
2560 if (d->currentAction && d->currentAction->isEnabled() && d->currentAction->menu()) {
2561 d->popupAction(d->currentAction, 0, true);
2562 key_consumed = true;
2566 case Qt::Key_Left: {
2567 if (d->currentAction && !d->scroll) {
2568 QAction *nextAction = 0;
2569 if (key == Qt::Key_Left) {
2570 QRect actionR = d->actionRect(d->currentAction);
2571 for(int x = actionR.left()-1; !nextAction && x >= 0; x--)
2572 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
2574 QRect actionR = d->actionRect(d->currentAction);
2575 for(int x = actionR.right()+1; !nextAction && x < width(); x++)
2576 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
2579 d->setCurrentAction(nextAction, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
2580 key_consumed = true;
2583 if (!key_consumed && key == Qt::Key_Left && qobject_cast<QMenu*>(d->causedPopup.widget)) {
2584 QPointer<QWidget> caused = d->causedPopup.widget;
2588 key_consumed = true;
2596 key_consumed = true;
2597 if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, 0, this))
2600 #ifndef QT_NO_MENUBAR
2601 if (QMenuBar *mb = qobject_cast<QMenuBar*>(QApplication::focusWidget())) {
2602 mb->d_func()->setKeyboardMode(false);
2608 case Qt::Key_Escape:
2609 #ifdef QT_KEYPAD_NAVIGATION
2612 key_consumed = true;
2618 QPointer<QWidget> caused = d->causedPopup.widget;
2619 d->hideMenu(this); // hide after getting causedPopup
2620 #ifndef QT_NO_MENUBAR
2621 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
2622 mb->d_func()->setCurrentAction(d->menuAction);
2623 mb->d_func()->setKeyboardMode(true);
2630 if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem, 0, this))
2632 // for motif, fall through
2633 #ifdef QT_KEYPAD_NAVIGATION
2634 case Qt::Key_Select:
2636 case Qt::Key_Return:
2637 case Qt::Key_Enter: {
2638 if (!d->currentAction) {
2639 d->setFirstActionActive();
2640 key_consumed = true;
2646 if (d->currentAction->menu())
2647 d->popupAction(d->currentAction, 0, true);
2649 d->activateAction(d->currentAction, QAction::Trigger);
2650 key_consumed = true;
2653 #ifndef QT_NO_WHATSTHIS
2655 if (!d->currentAction || d->currentAction->whatsThis().isNull())
2657 QWhatsThis::enterWhatsThisMode();
2658 d->activateAction(d->currentAction, QAction::Trigger);
2662 key_consumed = false;
2665 if (!key_consumed) { // send to menu bar
2666 if ((!e->modifiers() || e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ShiftModifier) &&
2667 e->text().length()==1) {
2668 bool activateAction = false;
2669 QAction *nextAction = 0;
2670 if (style()->styleHint(QStyle::SH_Menu_KeyboardSearch, 0, this) && !e->modifiers()) {
2671 int best_match_count = 0;
2672 d->searchBufferTimer.start(2000, this);
2673 d->searchBuffer += e->text();
2674 for(int i = 0; i < d->actions.size(); ++i) {
2675 int match_count = 0;
2676 if (d->actionRects.at(i).isNull())
2678 QAction *act = d->actions.at(i);
2679 const QString act_text = act->text();
2680 for(int c = 0; c < d->searchBuffer.size(); ++c) {
2681 if(act_text.indexOf(d->searchBuffer.at(c), 0, Qt::CaseInsensitive) != -1)
2684 if(match_count > best_match_count) {
2685 best_match_count = match_count;
2690 #ifndef QT_NO_SHORTCUT
2693 QAction *first = 0, *currentSelected = 0, *firstAfterCurrent = 0;
2694 QChar c = e->text().at(0).toUpper();
2695 for(int i = 0; i < d->actions.size(); ++i) {
2696 if (d->actionRects.at(i).isNull())
2698 QAction *act = d->actions.at(i);
2699 QKeySequence sequence = QKeySequence::mnemonic(act->text());
2700 int key = sequence[0] & 0xffff;
2701 if (key == c.unicode()) {
2705 if (act == d->currentAction)
2706 currentSelected = act;
2707 else if (!firstAfterCurrent && currentSelected)
2708 firstAfterCurrent = act;
2711 if (clashCount == 1)
2712 activateAction = true;
2713 if (clashCount >= 1) {
2714 if (clashCount == 1 || !currentSelected || !firstAfterCurrent)
2717 nextAction = firstAfterCurrent;
2722 key_consumed = true;
2724 d->scrollMenu(nextAction, QMenuPrivate::QMenuScroller::ScrollCenter, false);
2725 d->setCurrentAction(nextAction, 20, QMenuPrivate::SelectedFromElsewhere, true);
2726 if (!nextAction->menu() && activateAction) {
2728 d->activateAction(nextAction, QAction::Trigger);
2732 if (!key_consumed) {
2733 #ifndef QT_NO_MENUBAR
2734 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->topCausedWidget())) {
2735 QAction *oldAct = mb->d_func()->currentAction;
2736 QApplication::sendEvent(mb, e);
2737 if (mb->d_func()->currentAction != oldAct)
2738 key_consumed = true;
2744 if (key_consumed && (e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta))
2745 QApplication::beep();
2746 #endif // Q_OS_WIN32
2757 void QMenu::mouseMoveEvent(QMouseEvent *e)
2760 if (!isVisible() || d->aboutToHide || d->mouseEventTaken(e))
2763 if (d->motions == 0) // ignore first mouse move event (see enterEvent())
2765 d->hasHadMouse = d->hasHadMouse || rect().contains(e->pos());
2767 QAction *action = d->actionAt(e->pos());
2770 && (!d->currentAction
2771 || !(d->currentAction->menu() && d->currentAction->menu()->isVisible())))
2772 d->setCurrentAction(0);
2774 } else if(e->buttons()) {
2775 d->mouseDown = this;
2777 if (d->sloppyRegion.contains(e->pos())) {
2778 d->sloppyAction = action;
2779 QMenuPrivate::sloppyDelayTimer = startTimer(style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this)*6);
2780 } else if (action != d->currentAction) {
2781 d->setCurrentAction(action, style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this));
2788 void QMenu::enterEvent(QEvent *)
2790 d_func()->motions = -1; // force us to ignore the generate mouse move in mouseMoveEvent()
2796 void QMenu::leaveEvent(QEvent *)
2799 d->sloppyAction = 0;
2800 if (!d->sloppyRegion.isEmpty())
2801 d->sloppyRegion = QRegion();
2802 if (!d->activeMenu && d->currentAction)
2810 QMenu::timerEvent(QTimerEvent *e)
2813 if (d->scroll && d->scroll->scrollTimer.timerId() == e->timerId()) {
2814 d->scrollMenu((QMenuPrivate::QMenuScroller::ScrollDirection)d->scroll->scrollDirection);
2815 if (d->scroll->scrollFlags == QMenuPrivate::QMenuScroller::ScrollNone)
2816 d->scroll->scrollTimer.stop();
2817 } else if(d->menuDelayTimer.timerId() == e->timerId()) {
2818 d->menuDelayTimer.stop();
2819 internalDelayedPopup();
2820 } else if(QMenuPrivate::sloppyDelayTimer == e->timerId()) {
2821 killTimer(QMenuPrivate::sloppyDelayTimer);
2822 QMenuPrivate::sloppyDelayTimer = 0;
2823 internalSetSloppyAction();
2824 } else if(d->searchBufferTimer.timerId() == e->timerId()) {
2825 d->searchBuffer.clear();
2829 void copyActionToPlatformItem(const QAction *action, QPlatformMenuItem* item)
2831 item->setText(action->text());
2832 item->setIsSeparator(action->isSeparator());
2833 if (action->isIconVisibleInMenu())
2834 item->setIcon(action->icon());
2835 item->setVisible(action->isVisible());
2836 item->setShortcut(action->shortcut());
2837 item->setChecked(action->isChecked());
2838 item->setFont(action->font());
2839 item->setRole((QPlatformMenuItem::MenuRole) action->menuRole());
2840 item->setEnabled(action->isEnabled());
2842 if (action->menu()) {
2843 item->setMenu(action->menu()->platformMenu());
2852 void QMenu::actionEvent(QActionEvent *e)
2856 setAttribute(Qt::WA_Resized, false);
2858 d->tornPopup->syncWithMenu(this, e);
2859 if (e->type() == QEvent::ActionAdded) {
2861 connect(e->action(), SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
2862 connect(e->action(), SIGNAL(hovered()), this, SLOT(_q_actionHovered()));
2864 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
2865 QWidget *widget = wa->requestWidget(this);
2867 d->widgetItems.insert(wa, widget);
2869 } else if (e->type() == QEvent::ActionRemoved) {
2870 e->action()->disconnect(this);
2871 if (e->action() == d->currentAction)
2872 d->currentAction = 0;
2873 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
2874 if (QWidget *widget = d->widgetItems.value(wa))
2875 wa->releaseWidget(widget);
2877 d->widgetItems.remove(e->action());
2880 if (d->platformMenu) {
2881 if (e->type() == QEvent::ActionAdded) {
2882 QPlatformMenuItem *menuItem =
2883 QGuiApplicationPrivate::platformTheme()->createPlatformMenuItem();
2884 menuItem->setTag(reinterpret_cast<quintptr>(e->action()));
2885 QObject::connect(menuItem, SIGNAL(activated()), e->action(), SLOT(trigger()));
2886 copyActionToPlatformItem(e->action(), menuItem);
2887 QPlatformMenuItem* beforeItem = d->platformMenu->menuItemForTag(reinterpret_cast<quintptr>(e->before()));
2888 d->platformMenu->insertMenuItem(menuItem, beforeItem);
2889 } else if (e->type() == QEvent::ActionRemoved) {
2890 QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(reinterpret_cast<quintptr>(e->action()));
2891 d->platformMenu->removeMenuItem(menuItem);
2892 } else if (e->type() == QEvent::ActionChanged) {
2893 QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(reinterpret_cast<quintptr>(e->action()));
2894 copyActionToPlatformItem(e->action(), menuItem);
2895 d->platformMenu->syncMenuItem(menuItem);
2898 d->platformMenu->syncSeparatorsCollapsible(d->collapsibleSeparators);
2901 #if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR)
2903 d->wce_menu = new QMenuPrivate::QWceMenuPrivate;
2904 if (e->type() == QEvent::ActionAdded)
2905 d->wce_menu->addAction(e->action(), d->wce_menu->findAction(e->before()));
2906 else if (e->type() == QEvent::ActionRemoved)
2907 d->wce_menu->removeAction(e->action());
2908 else if (e->type() == QEvent::ActionChanged)
2909 d->wce_menu->syncAction(e->action());
2913 d->updateActionRects();
2922 void QMenu::internalSetSloppyAction()
2924 if (d_func()->sloppyAction)
2925 d_func()->setCurrentAction(d_func()->sloppyAction, 0);
2931 void QMenu::internalDelayedPopup()
2935 //hide the current item
2936 if (QMenu *menu = d->activeMenu) {
2941 if (!d->currentAction || !d->currentAction->isEnabled() || !d->currentAction->menu() ||
2942 !d->currentAction->menu()->isEnabled() || d->currentAction->menu()->isVisible())
2946 d->activeMenu = d->currentAction->menu();
2947 d->activeMenu->d_func()->causedPopup.widget = this;
2948 d->activeMenu->d_func()->causedPopup.action = d->currentAction;
2950 int subMenuOffset = style()->pixelMetric(QStyle::PM_SubMenuOverlap, 0, this);
2951 const QRect actionRect(d->actionRect(d->currentAction));
2952 const QSize menuSize(d->activeMenu->sizeHint());
2953 const QPoint rightPos(mapToGlobal(QPoint(actionRect.right() + subMenuOffset + 1, actionRect.top())));
2955 QPoint pos(rightPos);
2957 //calc sloppy focus buffer
2958 if (style()->styleHint(QStyle::SH_Menu_SloppySubMenus, 0, this)) {
2959 QPoint cur = QCursor::pos();
2960 if (actionRect.contains(mapFromGlobal(cur))) {
2962 pts[0] = QPoint(cur.x(), cur.y() - 2);
2963 pts[3] = QPoint(cur.x(), cur.y() + 2);
2964 if (pos.x() >= cur.x()) {
2965 pts[1] = QPoint(geometry().right(), pos.y());
2966 pts[2] = QPoint(geometry().right(), pos.y() + menuSize.height());
2968 pts[1] = QPoint(pos.x() + menuSize.width(), pos.y());
2969 pts[2] = QPoint(pos.x() + menuSize.width(), pos.y() + menuSize.height());
2972 for(int i = 0; i < 4; i++)
2973 points.setPoint(i, mapFromGlobal(pts[i]));
2974 d->sloppyRegion = QRegion(points);
2979 d->activeMenu->popup(pos);
2983 \fn void QMenu::addAction(QAction *action)
2986 Appends the action \a action to the menu's list of actions.
2988 \sa QMenuBar::addAction(), QWidget::addAction()
2992 \fn void QMenu::aboutToHide()
2995 This signal is emitted just before the menu is hidden from the user.
2997 \sa aboutToShow(), hide()
3001 \fn void QMenu::aboutToShow()
3003 This signal is emitted just before the menu is shown to the user.
3005 \sa aboutToHide(), show()
3009 \fn void QMenu::triggered(QAction *action)
3011 This signal is emitted when an action in this menu is triggered.
3013 \a action is the action that caused the signal to be emitted.
3015 Normally, you connect each menu action's \l{QAction::}{triggered()} signal
3016 to its own custom slot, but sometimes you will want to connect several
3017 actions to a single slot, for example, when you have a group of closely
3018 related actions, such as "left justify", "center", "right justify".
3020 \note This signal is emitted for the main parent menu in a hierarchy.
3021 Hence, only the parent menu needs to be connected to a slot; sub-menus need
3024 \sa hovered(), QAction::triggered()
3028 \fn void QMenu::hovered(QAction *action)
3030 This signal is emitted when a menu action is highlighted; \a action
3031 is the action that caused the signal to be emitted.
3033 Often this is used to update status information.
3035 \sa triggered(), QAction::hovered()
3041 void QMenu::setNoReplayFor(QWidget *noReplayFor)
3044 d_func()->noReplayFor = noReplayFor;
3046 Q_UNUSED(noReplayFor);
3052 QPlatformMenu *QMenu::platformMenu()
3055 return d_func()->platformMenu;
3059 \property QMenu::separatorsCollapsible
3062 \brief whether consecutive separators should be collapsed
3064 This property specifies whether consecutive separators in the menu
3065 should be visually collapsed to a single one. Separators at the
3066 beginning or the end of the menu are also hidden.
3068 By default, this property is true.
3070 bool QMenu::separatorsCollapsible() const
3073 return d->collapsibleSeparators;
3076 void QMenu::setSeparatorsCollapsible(bool collapse)
3079 if (d->collapsibleSeparators == collapse)
3082 d->collapsibleSeparators = collapse;
3085 d->updateActionRects();
3088 if (d->platformMenu)
3089 d->platformMenu->syncSeparatorsCollapsible(collapse);
3094 // for private slots
3095 #include "moc_qmenu.cpp"
3096 #include "qmenu.moc"
3098 #endif // QT_NO_MENU