1 /****************************************************************************
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** All rights reserved.
5 ** Contact: Nokia Corporation (qt-info@nokia.com)
7 ** This file is part of the QtGui module of the Qt Toolkit.
9 ** $QT_BEGIN_LICENSE:LGPL$
10 ** GNU Lesser General Public License Usage
11 ** This file may be used under the terms of the GNU Lesser General Public
12 ** License version 2.1 as published by the Free Software Foundation and
13 ** appearing in the file LICENSE.LGPL included in the packaging of this
14 ** file. Please review the following information to ensure the GNU Lesser
15 ** General Public License version 2.1 requirements will be met:
16 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 ** In addition, as a special exception, Nokia gives you certain additional
19 ** rights. These rights are described in the Nokia Qt LGPL Exception
20 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 ** GNU General Public License Usage
23 ** Alternatively, this file may be used under the terms of the GNU General
24 ** Public License version 3.0 as published by the Free Software Foundation
25 ** and appearing in the file LICENSE.GPL included in the packaging of this
26 ** file. Please review the following information to ensure the GNU General
27 ** Public License version 3.0 requirements will be met:
28 ** http://www.gnu.org/copyleft/gpl.html.
31 ** Alternatively, this file may be used in accordance with the terms and
32 ** conditions contained in a signed written agreement between you and Nokia.
40 ****************************************************************************/
52 #include "qapplication.h"
53 #include "qdesktopwidget.h"
54 #ifndef QT_NO_ACCESSIBILITY
55 # include "qaccessible.h"
58 # include <private/qeffects_p.h>
60 #ifndef QT_NO_WHATSTHIS
61 # include <qwhatsthis.h>
65 #include "qmenubar_p.h"
66 #include "qwidgetaction.h"
67 #include "qtoolbutton.h"
68 #include "qpushbutton.h"
69 #include <private/qpushbutton_p.h>
70 #include <private/qaction_p.h>
71 #include <private/qsoftkeymanager_p.h>
74 # include <private/qt_x11_p.h>
77 #if defined(Q_OS_MAC) && !defined(QT_NO_EFFECTS)
78 # include <private/qcore_mac_p.h>
79 # include <private/qt_cocoa_helpers_mac_p.h>
85 QMenu *QMenuPrivate::mouseDown = 0;
86 int QMenuPrivate::sloppyDelayTimer = 0;
89 // internal class used for the torn off popup
90 class QTornOffMenu : public QMenu
93 class QTornOffMenuPrivate : public QMenuPrivate
95 Q_DECLARE_PUBLIC(QMenu)
97 QTornOffMenuPrivate(QMenu *p) : causedMenu(p) {
99 causedPopup.widget = 0;
100 causedPopup.action = ((QTornOffMenu*)p)->d_func()->causedPopup.action;
101 causedStack = ((QTornOffMenu*)p)->d_func()->calcCausedStack();
103 QList<QPointer<QWidget> > calcCausedStack() const { return causedStack; }
104 QPointer<QMenu> causedMenu;
105 QList<QPointer<QWidget> > causedStack;
108 QTornOffMenu(QMenu *p) : QMenu(*(new QTornOffMenuPrivate(p)))
111 // make the torn-off menu a sibling of p (instead of a child)
112 QWidget *parentWidget = d->causedStack.isEmpty() ? p : d->causedStack.last();
113 if (parentWidget->parentWidget())
114 parentWidget = parentWidget->parentWidget();
115 setParent(parentWidget, Qt::Window | Qt::Tool);
116 setAttribute(Qt::WA_DeleteOnClose, true);
117 setAttribute(Qt::WA_X11NetWmWindowTypeMenu, true);
118 setWindowTitle(p->windowTitle());
119 setEnabled(p->isEnabled());
120 //QObject::connect(this, SIGNAL(triggered(QAction*)), this, SLOT(onTrigger(QAction*)));
121 //QObject::connect(this, SIGNAL(hovered(QAction*)), this, SLOT(onHovered(QAction*)));
122 QList<QAction*> items = p->actions();
123 for(int i = 0; i < items.count(); i++)
124 addAction(items.at(i));
126 void syncWithMenu(QMenu *menu, QActionEvent *act)
129 if(menu != d->causedMenu)
131 if (act->type() == QEvent::ActionAdded) {
132 insertAction(act->before(), act->action());
133 } else if (act->type() == QEvent::ActionRemoved)
134 removeAction(act->action());
136 void actionEvent(QActionEvent *e)
138 QMenu::actionEvent(e);
139 setFixedSize(sizeHint());
142 void onTrigger(QAction *action) { d_func()->activateAction(action, QAction::Trigger, false); }
143 void onHovered(QAction *action) { d_func()->activateAction(action, QAction::Hover, false); }
145 Q_DECLARE_PRIVATE(QTornOffMenu)
146 friend class QMenuPrivate;
149 void QMenuPrivate::init()
152 #ifndef QT_NO_WHATSTHIS
153 q->setAttribute(Qt::WA_CustomWhatsThis);
155 q->setAttribute(Qt::WA_X11NetWmWindowTypePopupMenu);
156 defaultMenuAction = menuAction = new QAction(q);
157 menuAction->d_func()->menu = q;
158 q->setMouseTracking(q->style()->styleHint(QStyle::SH_Menu_MouseTracking, 0, q));
159 if (q->style()->styleHint(QStyle::SH_Menu_Scrollable, 0, q)) {
160 scroll = new QMenuPrivate::QMenuScroller;
161 scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
164 platformMenu = QGuiApplicationPrivate::platformTheme()->createPlatformMenu(q);
166 #ifdef QT_SOFTKEYS_ENABLED
167 selectAction = QSoftKeyManager::createKeyedAction(QSoftKeyManager::SelectSoftKey, Qt::Key_Select, q);
168 cancelAction = QSoftKeyManager::createKeyedAction(QSoftKeyManager::CancelSoftKey, Qt::Key_Back, q);
169 selectAction->setPriority(QAction::HighPriority);
170 cancelAction->setPriority(QAction::HighPriority);
171 q->addAction(selectAction);
172 q->addAction(cancelAction);
176 int QMenuPrivate::scrollerHeight() const
179 return qMax(QApplication::globalStrut().height(), q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q));
182 //Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't
183 QRect QMenuPrivate::popupGeometry(const QWidget *widget) const
186 return QApplication::desktop()->screenGeometry(widget);
187 #elif defined Q_WS_X11
188 if (X11->desktopEnvironment == DE_KDE)
189 return QApplication::desktop()->screenGeometry(widget);
191 return QApplication::desktop()->availableGeometry(widget);
193 return QApplication::desktop()->availableGeometry(widget);
197 //Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't
198 QRect QMenuPrivate::popupGeometry(int screen) const
201 return QApplication::desktop()->screenGeometry(screen);
202 #elif defined Q_WS_X11
203 if (X11->desktopEnvironment == DE_KDE)
204 return QApplication::desktop()->screenGeometry(screen);
206 return QApplication::desktop()->availableGeometry(screen);
208 return QApplication::desktop()->availableGeometry(screen);
212 QList<QPointer<QWidget> > QMenuPrivate::calcCausedStack() const
214 QList<QPointer<QWidget> > ret;
215 for(QWidget *widget = causedPopup.widget; widget; ) {
217 if (QTornOffMenu *qtmenu = qobject_cast<QTornOffMenu*>(widget))
218 ret += qtmenu->d_func()->causedStack;
219 if (QMenu *qmenu = qobject_cast<QMenu*>(widget))
220 widget = qmenu->d_func()->causedPopup.widget;
227 void QMenuPrivate::updateActionRects() const
230 updateActionRects(popupGeometry(q));
233 void QMenuPrivate::updateActionRects(const QRect &screen) const
241 //let's reinitialize the buffer
242 actionRects.resize(actions.count());
243 actionRects.fill(QRect());
245 int lastVisibleAction = getLastVisibleAction();
247 int max_column_width = 0,
248 dh = screen.height(),
250 QStyle *style = q->style();
253 const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q),
254 vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q),
255 icone = style->pixelMetric(QStyle::PM_SmallIconSize, &opt, q);
256 const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q);
257 const int deskFw = style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, &opt, q);
258 const int tearoffHeight = tearoff ? style->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q) : 0;
260 //for compatibility now - will have to refactor this away
263 hasCheckableItems = false;
267 for (int i = 0; i < actions.count(); ++i) {
268 QAction *action = actions.at(i);
269 if (action->isSeparator() || !action->isVisible() || widgetItems.contains(action))
272 hasCheckableItems |= action->isCheckable();
273 QIcon is = action->icon();
275 maxIconWidth = qMax<uint>(maxIconWidth, icone + 4);
280 QFontMetrics qfm = q->fontMetrics();
281 bool previousWasSeparator = true; // this is true to allow removing the leading separators
282 for(int i = 0; i <= lastVisibleAction; i++) {
283 QAction *action = actions.at(i);
285 if (!action->isVisible() ||
286 (collapsibleSeparators && previousWasSeparator && action->isSeparator()))
287 continue; // we continue, this action will get an empty QRect
289 previousWasSeparator = action->isSeparator();
291 //let the style modify the above size..
292 QStyleOptionMenuItem opt;
293 q->initStyleOption(&opt, action);
294 const QFontMetrics &fm = opt.fontMetrics;
297 if (QWidget *w = widgetItems.value(action)) {
298 sz = w->sizeHint().expandedTo(w->minimumSize()).expandedTo(w->minimumSizeHint()).boundedTo(w->maximumSize());
300 //calc what I think the size is..
301 if (action->isSeparator()) {
304 QString s = action->text();
305 int t = s.indexOf(QLatin1Char('\t'));
307 tabWidth = qMax(int(tabWidth), qfm.width(s.mid(t+1)));
309 #ifndef QT_NO_SHORTCUT
311 QKeySequence seq = action->shortcut();
313 tabWidth = qMax(int(tabWidth), qfm.width(seq));
316 sz.setWidth(fm.boundingRect(QRect(), Qt::TextSingleLine | Qt::TextShowMnemonic, s).width());
317 sz.setHeight(qMax(fm.height(), qfm.height()));
319 QIcon is = action->icon();
321 QSize is_sz = QSize(icone, icone);
322 if (is_sz.height() > sz.height())
323 sz.setHeight(is_sz.height());
326 sz = style->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, q);
331 max_column_width = qMax(max_column_width, sz.width());
334 y+sz.height()+vmargin > dh - (deskFw * 2)) {
340 actionRects[i] = QRect(0, 0, sz.width(), sz.height());
344 max_column_width += tabWidth; //finally add in the tab width
345 const int sfcMargin = style->sizeFromContents(QStyle::CT_Menu, &opt, QApplication::globalStrut(), q).width() - QApplication::globalStrut().width();
346 const int min_column_width = q->minimumWidth() - (sfcMargin + leftmargin + rightmargin + 2 * (fw + hmargin));
347 max_column_width = qMax(min_column_width, max_column_width);
350 const int base_y = vmargin + fw + topmargin +
351 (scroll ? scroll->scrollOffset : 0) +
353 int x = hmargin + fw + leftmargin;
356 for(int i = 0; i < actions.count(); i++) {
357 QRect &rect = actionRects[i];
361 y+rect.height() > dh - deskFw * 2) {
362 x += max_column_width + hmargin;
365 rect.translate(x, y); //move
366 rect.setWidth(max_column_width); //uniform width
368 //we need to update the widgets geometry
369 if (QWidget *widget = widgetItems.value(actions.at(i))) {
370 widget->setGeometry(rect);
371 widget->setVisible(actions.at(i)->isVisible());
379 QSize QMenuPrivate::adjustMenuSizeForScreen(const QRect &screen)
382 QSize ret = screen.size();
384 updateActionRects(screen);
385 const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
386 ret.setWidth(actionRects.at(getLastVisibleAction()).right() + fw);
390 int QMenuPrivate::getLastVisibleAction() const
392 //let's try to get the last visible action
393 int lastVisibleAction = actions.count() - 1;
394 for (;lastVisibleAction >= 0; --lastVisibleAction) {
395 const QAction *action = actions.at(lastVisibleAction);
396 if (action->isVisible()) {
397 //removing trailing separators
398 if (action->isSeparator() && collapsibleSeparators)
403 return lastVisibleAction;
407 QRect QMenuPrivate::actionRect(QAction *act) const
409 int index = actions.indexOf(act);
415 //we found the action
416 return actionRects.at(index);
419 #if defined(Q_OS_MAC)
420 static const qreal MenuFadeTimeInSec = 0.150;
423 void QMenuPrivate::hideUpToMenuBar()
426 bool fadeMenus = q->style()->styleHint(QStyle::SH_Menu_FadeOutOnHide);
428 QWidget *caused = causedPopup.widget;
429 hideMenu(q); //hide after getting causedPopup
431 #ifndef QT_NO_MENUBAR
432 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
433 mb->d_func()->setCurrentAction(0);
434 mb->d_func()->setKeyboardMode(false);
438 if (QMenu *m = qobject_cast<QMenu*>(caused)) {
439 caused = m->d_func()->causedPopup.widget;
440 if (!m->d_func()->tornoff)
441 hideMenu(m, fadeMenus);
442 if (!fadeMenus) // Mac doesn't clear the action until after hidden.
443 m->d_func()->setCurrentAction(0);
447 #if defined(Q_WS_MAC)
449 QEventLoop eventLoop;
450 QTimer::singleShot(int(MenuFadeTimeInSec * 1000), &eventLoop, SLOT(quit()));
451 QMacWindowFader::currentFader()->performFade();
459 void QMenuPrivate::hideMenu(QMenu *menu, bool justRegister)
463 #if !defined(QT_NO_EFFECTS)
464 menu->blockSignals(true);
466 // Flash item which is about to trigger (if any).
467 if (menu->style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)
468 && currentAction && currentAction == actionAboutToTrigger
469 && menu->actions().contains(currentAction)) {
470 QEventLoop eventLoop;
471 QAction *activeAction = currentAction;
473 menu->setActiveAction(0);
474 QTimer::singleShot(60, &eventLoop, SLOT(quit()));
477 // Select and wait 20 ms.
478 menu->setActiveAction(activeAction);
479 QTimer::singleShot(20, &eventLoop, SLOT(quit()));
484 if (menu->style()->styleHint(QStyle::SH_Menu_FadeOutOnHide)) {
486 // Should be something like: q->transitionWindow(Qt::FadeOutTransition, MenuFadeTimeInSec);
487 // Hopefully we'll integrate qt/research/windowtransitions into main before 4.4.
488 // Talk to Richard, Trenton or Bjoern.
489 #if defined(Q_WS_MAC)
491 QMacWindowFader::currentFader()->setFadeDuration(MenuFadeTimeInSec);
492 QMacWindowFader::currentFader()->registerWindowToFade(menu);
494 macWindowFade(qt_mac_window_for(menu), MenuFadeTimeInSec);
500 menu->blockSignals(false);
501 #endif // QT_NO_EFFECTS
506 void QMenuPrivate::popupAction(QAction *action, int delay, bool activateFirst)
509 if (action && action->isEnabled()) {
511 q->internalDelayedPopup();
512 else if (!menuDelayTimer.isActive() && (!action->menu() || !action->menu()->isVisible()))
513 menuDelayTimer.start(delay, q);
514 if (activateFirst && action->menu())
515 action->menu()->d_func()->setFirstActionActive();
516 } else if (QMenu *menu = activeMenu) { //hide the current item
522 void QMenuPrivate::setSyncAction()
525 QAction *current = currentAction;
526 if(current && (!current->isEnabled() || current->menu() || current->isSeparator()))
528 for(QWidget *caused = q; caused;) {
529 if (QMenu *m = qobject_cast<QMenu*>(caused)) {
530 caused = m->d_func()->causedPopup.widget;
531 if (m->d_func()->eventLoop)
532 m->d_func()->syncAction = current; // synchronous operation
540 void QMenuPrivate::setFirstActionActive()
544 for(int i = 0, saccum = 0; i < actions.count(); i++) {
545 const QRect &rect = actionRects.at(i);
548 if (scroll && scroll->scrollFlags & QMenuScroller::ScrollUp) {
549 saccum -= rect.height();
550 if (saccum > scroll->scrollOffset - scrollerHeight())
553 QAction *act = actions.at(i);
554 if (!act->isSeparator() &&
555 (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
556 || act->isEnabled())) {
557 setCurrentAction(act);
563 // popup == -1 means do not popup, 0 means immediately, others mean use a timer
564 void QMenuPrivate::setCurrentAction(QAction *action, int popup, SelectionReason reason, bool activateFirst)
567 tearoffHighlighted = 0;
569 q->update(actionRect(currentAction));
572 if (!sloppyRegion.isEmpty())
573 sloppyRegion = QRegion();
574 QMenu *hideActiveMenu = activeMenu;
575 #ifndef QT_NO_STATUSTIP
576 QAction *previousAction = currentAction;
579 currentAction = action;
581 if (!action->isSeparator()) {
582 activateAction(action, QAction::Hover);
584 hideActiveMenu = 0; //will be done "later"
585 // if the menu is visible then activate the required action,
586 // otherwise we just mark the action as currentAction
587 // and activate it when the menu will be popuped.
589 popupAction(currentAction, popup, activateFirst);
591 q->update(actionRect(action));
593 if (reason == SelectedFromKeyboard) {
594 QWidget *widget = widgetItems.value(action);
596 if (widget->focusPolicy() != Qt::NoFocus)
597 widget->setFocus(Qt::TabFocusReason);
599 //when the action has no QWidget, the QMenu itself should
601 // Since the menu is a pop-up, it uses the popup reason.
602 if (!q->hasFocus()) {
603 q->setFocus(Qt::PopupFocusReason);
607 } else { //action is a separator
609 hideActiveMenu = 0; //will be done "later"
611 #ifndef QT_NO_STATUSTIP
612 } else if (previousAction) {
613 previousAction->d_func()->showStatusText(topCausedWidget(), QString());
616 if (hideActiveMenu) {
618 #ifndef QT_NO_EFFECTS
619 // kill any running effect
623 hideMenu(hideActiveMenu);
627 //return the top causedPopup.widget that is not a QMenu
628 QWidget *QMenuPrivate::topCausedWidget() const
630 QWidget* top = causedPopup.widget;
631 while (QMenu* m = qobject_cast<QMenu *>(top))
632 top = m->d_func()->causedPopup.widget;
636 QAction *QMenuPrivate::actionAt(QPoint p) const
638 if (!q_func()->rect().contains(p)) //sanity check
641 for(int i = 0; i < actionRects.count(); i++) {
642 if (actionRects.at(i).contains(p))
643 return actions.at(i);
648 void QMenuPrivate::setOverrideMenuAction(QAction *a)
651 QObject::disconnect(menuAction, SIGNAL(destroyed()), q, SLOT(_q_overrideMenuActionDestroyed()));
654 QObject::connect(a, SIGNAL(destroyed()), q, SLOT(_q_overrideMenuActionDestroyed()));
655 } else { //we revert back to the default action created by the QMenu itself
656 menuAction = defaultMenuAction;
660 void QMenuPrivate::_q_overrideMenuActionDestroyed()
662 menuAction=defaultMenuAction;
666 void QMenuPrivate::updateLayoutDirection()
669 //we need to mimic the cause of the popup's layout direction
670 //to allow setting it on a mainwindow for example
671 //we call setLayoutDirection_helper to not overwrite a user-defined value
672 if (!q->testAttribute(Qt::WA_SetLayoutDirection)) {
673 if (QWidget *w = causedPopup.widget)
674 setLayoutDirection_helper(w->layoutDirection());
675 else if (QWidget *w = q->parentWidget())
676 setLayoutDirection_helper(w->layoutDirection());
678 setLayoutDirection_helper(QApplication::layoutDirection());
684 Returns the action associated with this menu.
686 QAction *QMenu::menuAction() const
688 return d_func()->menuAction;
692 \property QMenu::title
693 \brief The title of the menu
695 This is equivalent to the QAction::text property of the menuAction().
697 By default, this property contains an empty string.
699 QString QMenu::title() const
701 return d_func()->menuAction->text();
704 void QMenu::setTitle(const QString &text)
706 d_func()->menuAction->setText(text);
710 \property QMenu::icon
712 \brief The icon of the menu
714 This is equivalent to the QAction::icon property of the menuAction().
716 By default, if no icon is explicitly set, this property contains a null icon.
718 QIcon QMenu::icon() const
720 return d_func()->menuAction->icon();
723 void QMenu::setIcon(const QIcon &icon)
725 d_func()->menuAction->setIcon(icon);
729 //actually performs the scrolling
730 void QMenuPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation location, bool active)
733 if (!scroll || !scroll->scrollFlags)
737 const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp) ? scrollerHeight() : 0;
738 const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollerHeight() : 0;
739 const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
740 const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
742 if (location == QMenuScroller::ScrollTop) {
743 for(int i = 0, saccum = 0; i < actions.count(); i++) {
744 if (actions.at(i) == action) {
745 newOffset = topScroll - saccum;
748 saccum += actionRects.at(i).height();
751 for(int i = 0, saccum = 0; i < actions.count(); i++) {
752 saccum += actionRects.at(i).height();
753 if (actions.at(i) == action) {
754 if (location == QMenuScroller::ScrollCenter)
755 newOffset = ((q->height() / 2) - botScroll) - (saccum - topScroll);
757 newOffset = (q->height() - botScroll) - saccum;
765 //figure out which scroll flags
766 uint newScrollFlags = QMenuScroller::ScrollNone;
767 if (newOffset < 0) //easy and cheap one
768 newScrollFlags |= QMenuScroller::ScrollUp;
769 int saccum = newOffset;
770 for(int i = 0; i < actionRects.count(); i++) {
771 saccum += actionRects.at(i).height();
772 if (saccum > q->height()) {
773 newScrollFlags |= QMenuScroller::ScrollDown;
778 if (!(newScrollFlags & QMenuScroller::ScrollDown) && (scroll->scrollFlags & QMenuScroller::ScrollDown)) {
779 newOffset = q->height() - (saccum - newOffset) - fw*2 - vmargin; //last item at bottom
782 if (!(newScrollFlags & QMenuScroller::ScrollUp) && (scroll->scrollFlags & QMenuScroller::ScrollUp)) {
783 newOffset = 0; //first item at top
786 if (newScrollFlags & QMenuScroller::ScrollUp)
787 newOffset -= vmargin;
789 QRect screen = popupGeometry(q);
790 const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q);
791 if (q->height() < screen.height()-(desktopFrame*2)-1) {
792 QRect geom = q->geometry();
793 if (newOffset > scroll->scrollOffset && (scroll->scrollFlags & newScrollFlags & QMenuScroller::ScrollUp)) { //scroll up
794 const int newHeight = geom.height()-(newOffset-scroll->scrollOffset);
795 if(newHeight > geom.height())
796 geom.setHeight(newHeight);
797 } else if(scroll->scrollFlags & newScrollFlags & QMenuScroller::ScrollDown) {
798 int newTop = geom.top() + (newOffset-scroll->scrollOffset);
799 if (newTop < desktopFrame+screen.top())
800 newTop = desktopFrame+screen.top();
801 if (newTop < geom.top()) {
804 newScrollFlags &= ~QMenuScroller::ScrollUp;
807 if (geom.bottom() > screen.bottom() - desktopFrame)
808 geom.setBottom(screen.bottom() - desktopFrame);
809 if (geom.top() < desktopFrame+screen.top())
810 geom.setTop(desktopFrame+screen.top());
811 if (geom != q->geometry()) {
813 if (newScrollFlags & QMenuScroller::ScrollDown &&
814 q->geometry().top() - geom.top() >= -newOffset)
815 newScrollFlags &= ~QMenuScroller::ScrollDown;
817 q->setGeometry(geom);
821 //actually update flags
822 const int delta = qMin(0, newOffset) - scroll->scrollOffset; //make sure the new offset is always negative
823 if (!itemsDirty && delta) {
824 //we've scrolled so we need to update the action rects
825 for (int i = 0; i < actionRects.count(); ++i) {
826 QRect ¤t = actionRects[i];
827 current.moveTop(current.top() + delta);
829 //we need to update the widgets geometry
830 if (QWidget *w = widgetItems.value(actions.at(i)))
831 w->setGeometry(current);
834 scroll->scrollOffset += delta;
835 scroll->scrollFlags = newScrollFlags;
837 setCurrentAction(action);
839 q->update(); //issue an update so we see all the new state..
842 void QMenuPrivate::scrollMenu(QMenuScroller::ScrollLocation location, bool active)
846 if(location == QMenuScroller::ScrollBottom) {
847 for(int i = actions.size()-1; i >= 0; --i) {
848 QAction *act = actions.at(i);
849 if (actionRects.at(i).isNull())
851 if (!act->isSeparator() &&
852 (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
853 || act->isEnabled())) {
854 if(scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
855 scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollBottom, active);
857 setCurrentAction(act, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
861 } else if(location == QMenuScroller::ScrollTop) {
862 for(int i = 0; i < actions.size(); ++i) {
863 QAction *act = actions.at(i);
864 if (actionRects.at(i).isNull())
866 if (!act->isSeparator() &&
867 (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
868 || act->isEnabled())) {
869 if(scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
870 scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollTop, active);
872 setCurrentAction(act, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
880 void QMenuPrivate::scrollMenu(QMenuScroller::ScrollDirection direction, bool page, bool active)
883 if (!scroll || !(scroll->scrollFlags & direction)) //not really possible...
886 const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp) ? scrollerHeight() : 0;
887 const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollerHeight() : 0;
888 const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
889 const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
890 const int offset = topScroll ? topScroll-vmargin : 0;
891 if (direction == QMenuScroller::ScrollUp) {
892 for(int i = 0, saccum = 0; i < actions.count(); i++) {
893 saccum -= actionRects.at(i).height();
894 if (saccum <= scroll->scrollOffset-offset) {
895 scrollMenu(actions.at(i), page ? QMenuScroller::ScrollBottom : QMenuScroller::ScrollTop, active);
899 } else if (direction == QMenuScroller::ScrollDown) {
900 bool scrolled = false;
901 for(int i = 0, saccum = 0; i < actions.count(); i++) {
902 const int iHeight = actionRects.at(i).height();
904 if (saccum <= scroll->scrollOffset-offset) {
905 const int scrollerArea = q->height() - botScroll - fw*2;
906 int visible = (scroll->scrollOffset-offset) - saccum;
907 for(i++ ; i < actions.count(); i++) {
908 visible += actionRects.at(i).height();
909 if (visible > scrollerArea - topScroll) {
911 scrollMenu(actions.at(i), page ? QMenuScroller::ScrollTop : QMenuScroller::ScrollBottom, active);
919 scroll->scrollFlags &= ~QMenuScroller::ScrollDown;
925 /* This is poor-mans eventfilters. This avoids the use of
926 eventFilter (which can be nasty for users of QMenuBar's). */
927 bool QMenuPrivate::mouseEventTaken(QMouseEvent *e)
930 QPoint pos = q->mapFromGlobal(e->globalPos());
931 if (scroll && !activeMenu) { //let the scroller "steal" the event
932 bool isScroll = false;
933 if (pos.x() >= 0 && pos.x() < q->width()) {
934 for(int dir = QMenuScroller::ScrollUp; dir <= QMenuScroller::ScrollDown; dir = dir << 1) {
935 if (scroll->scrollFlags & dir) {
936 if (dir == QMenuScroller::ScrollUp)
937 isScroll = (pos.y() <= scrollerHeight());
938 else if (dir == QMenuScroller::ScrollDown)
939 isScroll = (pos.y() >= q->height() - scrollerHeight());
941 scroll->scrollDirection = dir;
948 scroll->scrollTimer.start(50, q);
951 scroll->scrollTimer.stop();
955 if (tearoff) { //let the tear off thingie "steal" the event..
956 QRect tearRect(0, 0, q->width(), q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q));
957 if (scroll && scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
958 tearRect.translate(0, scrollerHeight());
960 if (tearRect.contains(pos) && hasMouseMoved(e->globalPos())) {
962 tearoffHighlighted = 1;
963 if (e->type() == QEvent::MouseButtonRelease) {
965 tornPopup = new QTornOffMenu(q);
966 tornPopup->setGeometry(q->geometry());
972 tearoffHighlighted = 0;
975 if (q->frameGeometry().contains(e->globalPos())) //otherwise if the event is in our rect we want it..
978 for(QWidget *caused = causedPopup.widget; caused;) {
979 bool passOnEvent = false;
980 QWidget *next_widget = 0;
981 QPoint cpos = caused->mapFromGlobal(e->globalPos());
982 #ifndef QT_NO_MENUBAR
983 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
984 passOnEvent = mb->rect().contains(cpos);
987 if (QMenu *m = qobject_cast<QMenu*>(caused)) {
988 passOnEvent = m->rect().contains(cpos);
989 next_widget = m->d_func()->causedPopup.widget;
992 if(e->type() != QEvent::MouseButtonRelease || mouseDown == caused) {
993 QMouseEvent new_e(e->type(), cpos, caused->mapTo(caused->topLevelWidget(), cpos), e->screenPos(),
994 e->button(), e->buttons(), e->modifiers());
995 QApplication::sendEvent(caused, &new_e);
1001 caused = next_widget;
1006 void QMenuPrivate::activateCausedStack(const QList<QPointer<QWidget> > &causedStack, QAction *action, QAction::ActionEvent action_e, bool self)
1008 QBoolBlocker guard(activationRecursionGuard);
1010 action->activate(action_e);
1012 for(int i = 0; i < causedStack.size(); ++i) {
1013 QPointer<QWidget> widget = causedStack.at(i);
1017 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
1018 widget = qmenu->d_func()->causedPopup.widget;
1019 if (action_e == QAction::Trigger) {
1020 emit qmenu->triggered(action);
1021 } else if (action_e == QAction::Hover) {
1022 emit qmenu->hovered(action);
1024 #ifndef QT_NO_MENUBAR
1025 } else if (QMenuBar *qmenubar = qobject_cast<QMenuBar*>(widget)) {
1026 if (action_e == QAction::Trigger) {
1027 emit qmenubar->triggered(action);
1028 } else if (action_e == QAction::Hover) {
1029 emit qmenubar->hovered(action);
1031 break; //nothing more..
1037 void QMenuPrivate::activateAction(QAction *action, QAction::ActionEvent action_e, bool self)
1040 #ifndef QT_NO_WHATSTHIS
1041 bool inWhatsThisMode = QWhatsThis::inWhatsThisMode();
1043 if (!action || !q->isEnabled()
1044 || (action_e == QAction::Trigger
1045 #ifndef QT_NO_WHATSTHIS
1048 && (action->isSeparator() ||!action->isEnabled())))
1051 /* I have to save the caused stack here because it will be undone after popup execution (ie in the hide).
1052 Then I iterate over the list to actually send the events. --Sam
1054 const QList<QPointer<QWidget> > causedStack = calcCausedStack();
1055 if (action_e == QAction::Trigger) {
1056 #ifndef QT_NO_WHATSTHIS
1057 if (!inWhatsThisMode)
1058 actionAboutToTrigger = action;
1061 if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
1064 for(QWidget *widget = QApplication::activePopupWidget(); widget; ) {
1065 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
1068 widget = qmenu->d_func()->causedPopup.widget;
1075 #ifndef QT_NO_WHATSTHIS
1076 if (inWhatsThisMode) {
1077 QString s = action->whatsThis();
1080 QWhatsThis::showText(q->mapToGlobal(actionRect(action).center()), s, q);
1087 activateCausedStack(causedStack, action, action_e, self);
1090 if (action_e == QAction::Hover) {
1091 #ifndef QT_NO_ACCESSIBILITY
1092 if (QAccessible::isActive()) {
1093 int actionIndex = indexOf(action) + 1;
1094 QAccessible::updateAccessibility(q, actionIndex, QAccessible::Focus);
1095 QAccessible::updateAccessibility(q, actionIndex, QAccessible::Selection);
1098 action->showStatusText(topCausedWidget());
1100 actionAboutToTrigger = 0;
1104 void QMenuPrivate::_q_actionTriggered()
1107 if (QAction *action = qobject_cast<QAction *>(q->sender())) {
1108 QWeakPointer<QAction> actionGuard = action;
1109 emit q->triggered(action);
1110 if (!activationRecursionGuard && actionGuard) {
1111 //in case the action has not been activated by the mouse
1112 //we check the parent hierarchy
1113 QList< QPointer<QWidget> > list;
1114 for(QWidget *widget = q->parentWidget(); widget; ) {
1115 if (qobject_cast<QMenu*>(widget)
1116 #ifndef QT_NO_MENUBAR
1117 || qobject_cast<QMenuBar*>(widget)
1120 list.append(widget);
1121 widget = widget->parentWidget();
1126 activateCausedStack(list, action, QAction::Trigger, false);
1131 void QMenuPrivate::_q_actionHovered()
1134 if (QAction * action = qobject_cast<QAction *>(q->sender())) {
1135 emit q->hovered(action);
1139 bool QMenuPrivate::hasMouseMoved(const QPoint &globalPos)
1141 //determines if the mouse has moved (ie its initial position has
1142 //changed by more than QApplication::startDragDistance()
1143 //or if there were at least 6 mouse motions)
1144 return motions > 6 ||
1145 QApplication::startDragDistance() < (mousePopupPos - globalPos).manhattanLength();
1150 Initialize \a option with the values from this menu and information from \a action. This method
1151 is useful for subclasses when they need a QStyleOptionMenuItem, but don't want
1152 to fill in all the information themselves.
1154 \sa QStyleOption::initFrom() QMenuBar::initStyleOption()
1156 void QMenu::initStyleOption(QStyleOptionMenuItem *option, const QAction *action) const
1158 if (!option || !action)
1162 option->initFrom(this);
1163 option->palette = palette();
1164 option->state = QStyle::State_None;
1166 if (window()->isActiveWindow())
1167 option->state |= QStyle::State_Active;
1168 if (isEnabled() && action->isEnabled()
1169 && (!action->menu() || action->menu()->isEnabled()))
1170 option->state |= QStyle::State_Enabled;
1172 option->palette.setCurrentColorGroup(QPalette::Disabled);
1174 option->font = action->font().resolve(font());
1175 option->fontMetrics = QFontMetrics(option->font);
1177 if (d->currentAction && d->currentAction == action && !d->currentAction->isSeparator()) {
1178 option->state |= QStyle::State_Selected
1179 | (d->mouseDown ? QStyle::State_Sunken : QStyle::State_None);
1182 option->menuHasCheckableItems = d->hasCheckableItems;
1183 if (!action->isCheckable()) {
1184 option->checkType = QStyleOptionMenuItem::NotCheckable;
1186 option->checkType = (action->actionGroup() && action->actionGroup()->isExclusive())
1187 ? QStyleOptionMenuItem::Exclusive : QStyleOptionMenuItem::NonExclusive;
1188 option->checked = action->isChecked();
1191 option->menuItemType = QStyleOptionMenuItem::SubMenu;
1192 else if (action->isSeparator())
1193 option->menuItemType = QStyleOptionMenuItem::Separator;
1194 else if (d->defaultAction == action)
1195 option->menuItemType = QStyleOptionMenuItem::DefaultItem;
1197 option->menuItemType = QStyleOptionMenuItem::Normal;
1198 if (action->isIconVisibleInMenu())
1199 option->icon = action->icon();
1200 QString textAndAccel = action->text();
1201 #ifndef QT_NO_SHORTCUT
1202 if (textAndAccel.indexOf(QLatin1Char('\t')) == -1) {
1203 QKeySequence seq = action->shortcut();
1205 textAndAccel += QLatin1Char('\t') + QString(seq);
1208 option->text = textAndAccel;
1209 option->tabWidth = d->tabWidth;
1210 option->maxIconWidth = d->maxIconWidth;
1211 option->menuRect = rect();
1216 \brief The QMenu class provides a menu widget for use in menu
1217 bars, context menus, and other popup menus.
1219 \ingroup mainwindow-classes
1220 \ingroup basicwidgets
1223 A menu widget is a selection menu. It can be either a pull-down
1224 menu in a menu bar or a standalone context menu. Pull-down menus
1225 are shown by the menu bar when the user clicks on the respective
1226 item or presses the specified shortcut key. Use
1227 QMenuBar::addMenu() to insert a menu into a menu bar. Context
1228 menus are usually invoked by some special keyboard key or by
1229 right-clicking. They can be executed either asynchronously with
1230 popup() or synchronously with exec(). Menus can also be invoked in
1231 response to button presses; these are just like context menus
1232 except for how they are invoked.
1236 \o \inlineimage plastique-menu.png
1237 \o \inlineimage windowsxp-menu.png
1238 \o \inlineimage macintosh-menu.png
1240 \caption Fig. A menu shown in \l{Plastique Style Widget Gallery}{Plastique widget style},
1241 \l{Windows XP Style Widget Gallery}{Windows XP widget style},
1242 and \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
1246 A menu consists of a list of action items. Actions are added with
1247 the addAction(), addActions() and insertAction() functions. An action
1248 is represented vertically and rendered by QStyle. In addition, actions
1249 can have a text label, an optional icon drawn on the very left side,
1250 and shortcut key sequence such as "Ctrl+X".
1252 The existing actions held by a menu can be found with actions().
1254 There are four kinds of action items: separators, actions that
1255 show a submenu, widgets, and actions that perform an action.
1256 Separators are inserted with addSeparator(), submenus with addMenu(),
1257 and all other items are considered action items.
1259 When inserting action items you usually specify a receiver and a
1260 slot. The receiver will be notifed whenever the item is
1261 \l{QAction::triggered()}{triggered()}. In addition, QMenu provides
1262 two signals, activated() and highlighted(), which signal the
1263 QAction that was triggered from the menu.
1265 You clear a menu with clear() and remove individual action items
1266 with removeAction().
1268 A QMenu can also provide a tear-off menu. A tear-off menu is a
1269 top-level window that contains a copy of the menu. This makes it
1270 possible for the user to "tear off" frequently used menus and
1271 position them in a convenient place on the screen. If you want
1272 this functionality for a particular menu, insert a tear-off handle
1273 with setTearOffEnabled(). When using tear-off menus, bear in mind
1274 that the concept isn't typically used on Microsoft Windows so
1275 some users may not be familiar with it. Consider using a QToolBar
1278 Widgets can be inserted into menus with the QWidgetAction class.
1279 Instances of this class are used to hold widgets, and are inserted
1280 into menus with the addAction() overload that takes a QAction.
1282 Conversely, actions can be added to widgets with the addAction(),
1283 addActions() and insertAction() functions.
1285 \warning To make QMenu visible on the screen, exec() or popup() should be
1286 used instead of show().
1288 \section1 QMenu on Qt for Windows CE
1290 If a menu is integrated into the native menubar on Windows Mobile we
1291 do not support the signals: aboutToHide (), aboutToShow () and hovered ().
1292 It is not possible to display an icon in a native menu on Windows Mobile.
1294 \section1 QMenu on Mac OS X with Qt build against Cocoa
1296 QMenu can be inserted only once in a menu/menubar. Subsequent insertions will
1297 have no effect or will result in a disabled menu item.
1299 See the \l{mainwindows/menus}{Menus} example for an example of how
1300 to use QMenuBar and QMenu in your application.
1302 \bold{Important inherited functions:} addAction(), removeAction(), clear(),
1303 addSeparator(), and addMenu().
1305 \sa QMenuBar, {fowler}{GUI Design Handbook: Menu, Drop-Down and Pop-Up},
1306 {Application Example}, {Menus Example}, {Recent Files Example}
1311 Constructs a menu with parent \a parent.
1313 Although a popup menu is always a top-level widget, if a parent is
1314 passed the popup menu will be deleted when that parent is
1315 destroyed (as with any other QObject).
1317 QMenu::QMenu(QWidget *parent)
1318 : QWidget(*new QMenuPrivate, parent, Qt::Popup)
1325 Constructs a menu with a \a title and a \a parent.
1327 Although a popup menu is always a top-level widget, if a parent is
1328 passed the popup menu will be deleted when that parent is
1329 destroyed (as with any other QObject).
1333 QMenu::QMenu(const QString &title, QWidget *parent)
1334 : QWidget(*new QMenuPrivate, parent, Qt::Popup)
1338 d->menuAction->setText(title);
1343 QMenu::QMenu(QMenuPrivate &dd, QWidget *parent)
1344 : QWidget(dd, parent, Qt::Popup)
1356 if (!d->widgetItems.isEmpty()) { // avoid detach on shared null hash
1357 QHash<QAction *, QWidget *>::iterator it = d->widgetItems.begin();
1358 for (; it != d->widgetItems.end(); ++it) {
1359 if (QWidget *widget = it.value()) {
1360 QWidgetAction *action = static_cast<QWidgetAction *>(it.key());
1361 action->releaseWidget(widget);
1368 d->eventLoop->exit();
1375 This convenience function creates a new action with \a text.
1376 The function adds the newly created action to the menu's
1377 list of actions, and returns it.
1379 \sa QWidget::addAction()
1381 QAction *QMenu::addAction(const QString &text)
1383 QAction *ret = new QAction(text, this);
1391 This convenience function creates a new action with an \a icon
1392 and some \a text. The function adds the newly created action to
1393 the menu's list of actions, and returns it.
1395 \sa QWidget::addAction()
1397 QAction *QMenu::addAction(const QIcon &icon, const QString &text)
1399 QAction *ret = new QAction(icon, text, this);
1407 This convenience function creates a new action with the text \a
1408 text and an optional shortcut \a shortcut. The action's
1409 \l{QAction::triggered()}{triggered()} signal is connected to the
1410 \a receiver's \a member slot. The function adds the newly created
1411 action to the menu's list of actions and returns it.
1413 \sa QWidget::addAction()
1415 QAction *QMenu::addAction(const QString &text, const QObject *receiver, const char* member, const QKeySequence &shortcut)
1417 QAction *action = new QAction(text, this);
1418 #ifdef QT_NO_SHORTCUT
1421 action->setShortcut(shortcut);
1423 QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
1431 This convenience function creates a new action with an \a icon and
1432 some \a text and an optional shortcut \a shortcut. The action's
1433 \l{QAction::triggered()}{triggered()} signal is connected to the
1434 \a member slot of the \a receiver object. The function adds the
1435 newly created action to the menu's list of actions, and returns it.
1437 \sa QWidget::addAction()
1439 QAction *QMenu::addAction(const QIcon &icon, const QString &text, const QObject *receiver,
1440 const char* member, const QKeySequence &shortcut)
1442 QAction *action = new QAction(icon, text, this);
1443 #ifdef QT_NO_SHORTCUT
1446 action->setShortcut(shortcut);
1448 QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
1454 This convenience function adds \a menu as a submenu to this menu.
1455 It returns \a menu's menuAction(). This menu does not take
1456 ownership of \a menu.
1458 \sa QWidget::addAction() QMenu::menuAction()
1460 QAction *QMenu::addMenu(QMenu *menu)
1462 QAction *action = menu->menuAction();
1468 Appends a new QMenu with \a title to the menu. The menu
1469 takes ownership of the menu. Returns the new menu.
1471 \sa QWidget::addAction() QMenu::menuAction()
1473 QMenu *QMenu::addMenu(const QString &title)
1475 QMenu *menu = new QMenu(title, this);
1476 addAction(menu->menuAction());
1481 Appends a new QMenu with \a icon and \a title to the menu. The menu
1482 takes ownership of the menu. Returns the new menu.
1484 \sa QWidget::addAction() QMenu::menuAction()
1486 QMenu *QMenu::addMenu(const QIcon &icon, const QString &title)
1488 QMenu *menu = new QMenu(title, this);
1489 menu->setIcon(icon);
1490 addAction(menu->menuAction());
1495 This convenience function creates a new separator action, i.e. an
1496 action with QAction::isSeparator() returning true, and adds the new
1497 action to this menu's list of actions. It returns the newly
1500 \sa QWidget::addAction()
1502 QAction *QMenu::addSeparator()
1504 QAction *action = new QAction(this);
1505 action->setSeparator(true);
1511 This convenience function inserts \a menu before action \a before
1512 and returns the menus menuAction().
1514 \sa QWidget::insertAction(), addMenu()
1516 QAction *QMenu::insertMenu(QAction *before, QMenu *menu)
1518 QAction *action = menu->menuAction();
1519 insertAction(before, action);
1524 This convenience function creates a new separator action, i.e. an
1525 action with QAction::isSeparator() returning true. The function inserts
1526 the newly created action into this menu's list of actions before
1527 action \a before and returns it.
1529 \sa QWidget::insertAction(), addSeparator()
1531 QAction *QMenu::insertSeparator(QAction *before)
1533 QAction *action = new QAction(this);
1534 action->setSeparator(true);
1535 insertAction(before, action);
1540 This sets the default action to \a act. The default action may have
1541 a visual cue, depending on the current QStyle. A default action
1542 usually indicates what will happen by default when a drop occurs.
1546 void QMenu::setDefaultAction(QAction *act)
1548 d_func()->defaultAction = act;
1552 Returns the current default action.
1554 \sa setDefaultAction()
1556 QAction *QMenu::defaultAction() const
1558 return d_func()->defaultAction;
1562 \property QMenu::tearOffEnabled
1563 \brief whether the menu supports being torn off
1565 When true, the menu contains a special tear-off item (often shown as a dashed
1566 line at the top of the menu) that creates a copy of the menu when it is
1569 This "torn-off" copy lives in a separate window. It contains the same menu
1570 items as the original menu, with the exception of the tear-off handle.
1572 By default, this property is false.
1574 void QMenu::setTearOffEnabled(bool b)
1577 if (d->tearoff == b)
1583 d->itemsDirty = true;
1588 bool QMenu::isTearOffEnabled() const
1590 return d_func()->tearoff;
1594 When a menu is torn off a second menu is shown to display the menu
1595 contents in a new window. When the menu is in this mode and the menu
1596 is visible returns true; otherwise false.
1598 \sa hideTearOffMenu() isTearOffEnabled()
1600 bool QMenu::isTearOffMenuVisible() const
1602 if (d_func()->tornPopup)
1603 return d_func()->tornPopup->isVisible();
1608 This function will forcibly hide the torn off menu making it
1609 disappear from the users desktop.
1611 \sa isTearOffMenuVisible() isTearOffEnabled()
1613 void QMenu::hideTearOffMenu()
1615 if (QWidget *w = d_func()->tornPopup)
1621 Sets the currently highlighted action to \a act.
1623 void QMenu::setActiveAction(QAction *act)
1626 d->setCurrentAction(act, 0);
1628 d->scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollCenter);
1633 Returns the currently highlighted action, or 0 if no
1634 action is currently highlighted.
1636 QAction *QMenu::activeAction() const
1638 return d_func()->currentAction;
1644 Returns true if there are no visible actions inserted into the menu, false
1647 \sa QWidget::actions()
1650 bool QMenu::isEmpty() const
1653 for(int i = 0; ret && i < actions().count(); ++i) {
1654 const QAction *action = actions().at(i);
1655 if (!action->isSeparator() && action->isVisible()) {
1663 Removes all the menu's actions. Actions owned by the menu and not
1664 shown in any other widget are deleted.
1670 QList<QAction*> acts = actions();
1672 for(int i = 0; i < acts.size(); i++) {
1673 #ifdef QT_SOFTKEYS_ENABLED
1675 // Lets not touch to our internal softkey actions
1676 if(acts[i] == d->selectAction || acts[i] == d->cancelAction)
1679 removeAction(acts[i]);
1680 if (acts[i]->parent() == this && acts[i]->d_func()->widgets.isEmpty())
1686 If a menu does not fit on the screen it lays itself out so that it
1687 does fit. It is style dependent what layout means (for example, on
1688 Windows it will use multiple columns).
1690 This functions returns the number of columns necessary.
1692 int QMenu::columnCount() const
1694 return d_func()->ncols;
1698 Returns the item at \a pt; returns 0 if there is no item there.
1700 QAction *QMenu::actionAt(const QPoint &pt) const
1702 if (QAction *ret = d_func()->actionAt(pt))
1708 Returns the geometry of action \a act.
1710 QRect QMenu::actionGeometry(QAction *act) const
1712 return d_func()->actionRect(act);
1718 QSize QMenu::sizeHint() const
1721 d->updateActionRects();
1724 for (int i = 0; i < d->actionRects.count(); ++i) {
1725 const QRect &rect = d->actionRects.at(i);
1728 if (rect.bottom() >= s.height())
1729 s.setHeight(rect.y() + rect.height());
1730 if (rect.right() >= s.width())
1731 s.setWidth(rect.x() + rect.width());
1733 // Note that the action rects calculated above already include
1734 // the top and left margins, so we only need to add margins for
1735 // the bottom and right.
1736 QStyleOption opt(0);
1738 const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, this);
1739 s.rwidth() += style()->pixelMetric(QStyle::PM_MenuHMargin, &opt, this) + fw + d->rightmargin;
1740 s.rheight() += style()->pixelMetric(QStyle::PM_MenuVMargin, &opt, this) + fw + d->bottommargin;
1742 return style()->sizeFromContents(QStyle::CT_Menu, &opt,
1743 s.expandedTo(QApplication::globalStrut()), this);
1747 Displays the menu so that the action \a atAction will be at the
1748 specified \e global position \a p. To translate a widget's local
1749 coordinates into global coordinates, use QWidget::mapToGlobal().
1751 When positioning a menu with exec() or popup(), bear in mind that
1752 you cannot rely on the menu's current size(). For performance
1753 reasons, the menu adapts its size only when necessary, so in many
1754 cases, the size before and after the show is different. Instead,
1755 use sizeHint() which calculates the proper size depending on the
1756 menu's current contents.
1758 \sa QWidget::mapToGlobal(), exec()
1760 void QMenu::popup(const QPoint &p, QAction *atAction)
1763 if (d->scroll) { // reset scroll state from last popup
1764 d->scroll->scrollOffset = 0;
1765 d->scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
1767 d->tearoffHighlighted = 0;
1769 d->doChildEffects = true;
1770 d->updateLayoutDirection();
1772 #ifndef QT_NO_MENUBAR
1773 // if this menu is part of a chain attached to a QMenuBar, set the
1774 // _NET_WM_WINDOW_TYPE_DROPDOWN_MENU X11 window type
1775 setAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu, qobject_cast<QMenuBar *>(d->topCausedWidget()) != 0);
1778 ensurePolished(); // Get the right font
1780 const bool actionListChanged = d->itemsDirty;
1781 d->updateActionRects();
1783 QPushButton *causedButton = qobject_cast<QPushButton*>(d->causedPopup.widget);
1784 if (actionListChanged && causedButton)
1785 pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition();
1789 QSize size = sizeHint();
1791 #ifndef QT_NO_GRAPHICSVIEW
1792 bool isEmbedded = !bypassGraphicsProxyWidget(this) && d->nearestGraphicsProxyWidget(this);
1794 screen = d->popupGeometry(this);
1797 screen = d->popupGeometry(QApplication::desktop()->screenNumber(p));
1798 const int desktopFrame = style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, this);
1799 bool adjustToDesktop = !window()->testAttribute(Qt::WA_DontShowOnScreen);
1801 // if the screens have very different geometries and the menu is too big, we have to recalculate
1802 if (size.height() > screen.height() || size.width() > screen.width()) {
1803 size = d->adjustMenuSizeForScreen(screen);
1804 adjustToDesktop = true;
1806 // Layout is not right, we might be able to save horizontal space
1807 if (d->ncols >1 && size.height() < screen.height()) {
1808 size = d->adjustMenuSizeForScreen(screen);
1809 adjustToDesktop = true;
1812 #ifdef QT_KEYPAD_NAVIGATION
1813 if (!atAction && QApplication::keypadNavigationEnabled()) {
1814 // Try to have one item activated
1815 if (d->defaultAction && d->defaultAction->isEnabled()) {
1816 atAction = d->defaultAction;
1817 // TODO: This works for first level menus, not yet sub menus
1819 foreach (QAction *action, d->actions)
1820 if (action->isEnabled()) {
1825 d->currentAction = atAction;
1829 pos.setY(screen.top() + desktopFrame);
1830 } else if (atAction) {
1831 for (int i = 0, above_height = 0; i < d->actions.count(); i++) {
1832 QAction *action = d->actions.at(i);
1833 if (action == atAction) {
1834 int newY = pos.y() - above_height;
1835 if (d->scroll && newY < desktopFrame) {
1836 d->scroll->scrollFlags = d->scroll->scrollFlags
1837 | QMenuPrivate::QMenuScroller::ScrollUp;
1838 d->scroll->scrollOffset = newY;
1839 newY = desktopFrame;
1843 if (d->scroll && d->scroll->scrollFlags != QMenuPrivate::QMenuScroller::ScrollNone
1844 && !style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll, 0, this)) {
1845 int below_height = above_height + d->scroll->scrollOffset;
1846 for (int i2 = i; i2 < d->actionRects.count(); i2++)
1847 below_height += d->actionRects.at(i2).height();
1848 size.setHeight(below_height);
1852 above_height += d->actionRects.at(i).height();
1857 QPoint mouse = QCursor::pos();
1858 d->mousePopupPos = mouse;
1859 const bool snapToMouse = (QRect(p.x() - 3, p.y() - 3, 6, 6).contains(mouse));
1861 if (adjustToDesktop) {
1862 // handle popup falling "off screen"
1863 if (isRightToLeft()) {
1864 if (snapToMouse) // position flowing left from the mouse
1865 pos.setX(mouse.x() - size.width());
1867 #ifndef QT_NO_MENUBAR
1868 // if in a menubar, it should be right-aligned
1869 if (qobject_cast<QMenuBar*>(d->causedPopup.widget))
1870 pos.rx() -= size.width();
1871 #endif //QT_NO_MENUBAR
1873 if (pos.x() < screen.left() + desktopFrame)
1874 pos.setX(qMax(p.x(), screen.left() + desktopFrame));
1875 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
1876 pos.setX(qMax(p.x() - size.width(), screen.right() - desktopFrame - size.width() + 1));
1878 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
1879 pos.setX(screen.right() - desktopFrame - size.width() + 1);
1880 if (pos.x() < screen.left() + desktopFrame)
1881 pos.setX(screen.left() + desktopFrame);
1883 if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
1885 pos.setY(qMin(mouse.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
1887 pos.setY(qMax(p.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
1888 } else if (pos.y() < screen.top() + desktopFrame) {
1889 pos.setY(screen.top() + desktopFrame);
1892 if (pos.y() < screen.top() + desktopFrame)
1893 pos.setY(screen.top() + desktopFrame);
1894 if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
1896 d->scroll->scrollFlags |= uint(QMenuPrivate::QMenuScroller::ScrollDown);
1897 int y = qMax(screen.y(),pos.y());
1898 size.setHeight(screen.bottom() - (desktopFrame * 2) - y);
1900 // Too big for screen, bias to see bottom of menu (for some reason)
1901 pos.setY(screen.bottom() - size.height() + 1);
1905 const int subMenuOffset = style()->pixelMetric(QStyle::PM_SubMenuOverlap, 0, this);
1906 const QSize menuSize(sizeHint());
1907 QMenu *caused = qobject_cast<QMenu*>(d_func()->causedPopup.widget);
1908 if (caused && caused->geometry().width() + menuSize.width() + subMenuOffset < screen.width()) {
1909 QRect parentActionRect(caused->d_func()->actionRect(caused->d_func()->currentAction));
1910 const QPoint actionTopLeft = caused->mapToGlobal(parentActionRect.topLeft());
1911 parentActionRect.moveTopLeft(actionTopLeft);
1912 if (isRightToLeft()) {
1913 if ((pos.x() + menuSize.width() > parentActionRect.left() - subMenuOffset)
1914 && (pos.x() < parentActionRect.right()))
1916 pos.rx() = parentActionRect.right();
1919 if ((pos.x() < parentActionRect.right() + subMenuOffset)
1920 && (pos.x() + menuSize.width() > parentActionRect.left()))
1922 pos.rx() = parentActionRect.left() - menuSize.width();
1926 setGeometry(QRect(pos, size));
1927 #ifndef QT_NO_EFFECTS
1928 int hGuess = isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll;
1929 int vGuess = QEffects::DownScroll;
1930 if (isRightToLeft()) {
1931 if ((snapToMouse && (pos.x() + size.width() / 2 > mouse.x())) ||
1932 (qobject_cast<QMenu*>(d->causedPopup.widget) && pos.x() + size.width() / 2 > d->causedPopup.widget->x()))
1933 hGuess = QEffects::RightScroll;
1935 if ((snapToMouse && (pos.x() + size.width() / 2 < mouse.x())) ||
1936 (qobject_cast<QMenu*>(d->causedPopup.widget) && pos.x() + size.width() / 2 < d->causedPopup.widget->x()))
1937 hGuess = QEffects::LeftScroll;
1940 #ifndef QT_NO_MENUBAR
1941 if ((snapToMouse && (pos.y() + size.height() / 2 < mouse.y())) ||
1942 (qobject_cast<QMenuBar*>(d->causedPopup.widget) &&
1943 pos.y() + size.width() / 2 < d->causedPopup.widget->mapToGlobal(d->causedPopup.widget->pos()).y()))
1944 vGuess = QEffects::UpScroll;
1946 if (QApplication::isEffectEnabled(Qt::UI_AnimateMenu)) {
1947 bool doChildEffects = true;
1948 #ifndef QT_NO_MENUBAR
1949 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->causedPopup.widget)) {
1950 doChildEffects = mb->d_func()->doChildEffects;
1951 mb->d_func()->doChildEffects = false;
1954 if (QMenu *m = qobject_cast<QMenu*>(d->causedPopup.widget)) {
1955 doChildEffects = m->d_func()->doChildEffects;
1956 m->d_func()->doChildEffects = false;
1959 if (doChildEffects) {
1960 if (QApplication::isEffectEnabled(Qt::UI_FadeMenu))
1962 else if (d->causedPopup.widget)
1963 qScrollEffect(this, qobject_cast<QMenu*>(d->causedPopup.widget) ? hGuess : vGuess);
1965 qScrollEffect(this, hGuess | vGuess);
1967 // kill any running effect
1979 #ifndef QT_NO_ACCESSIBILITY
1980 QAccessible::updateAccessibility(this, 0, QAccessible::PopupMenuStart);
1985 Executes this menu synchronously.
1987 This is equivalent to \c{exec(pos())}.
1989 This returns the triggered QAction in either the popup menu or one
1990 of its submenus, or 0 if no item was triggered (normally because
1991 the user pressed Esc).
1993 In most situations you'll want to specify the position yourself,
1994 for example, the current mouse position:
1995 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 0
1996 or aligned to a widget:
1997 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 1
1998 or in reaction to a QMouseEvent *e:
1999 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 2
2001 QAction *QMenu::exec()
2010 Executes this menu synchronously.
2012 Pops up the menu so that the action \a action will be at the
2013 specified \e global position \a p. To translate a widget's local
2014 coordinates into global coordinates, use QWidget::mapToGlobal().
2016 This returns the triggered QAction in either the popup menu or one
2017 of its submenus, or 0 if no item was triggered (normally because
2018 the user pressed Esc).
2020 Note that all signals are emitted as usual. If you connect a
2021 QAction to a slot and call the menu's exec(), you get the result
2022 both via the signal-slot connection and in the return value of
2025 Common usage is to position the menu at the current mouse
2027 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 3
2028 or aligned to a widget:
2029 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 4
2030 or in reaction to a QMouseEvent *e:
2031 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 5
2033 When positioning a menu with exec() or popup(), bear in mind that
2034 you cannot rely on the menu's current size(). For performance
2035 reasons, the menu adapts its size only when necessary. So in many
2036 cases, the size before and after the show is different. Instead,
2037 use sizeHint() which calculates the proper size depending on the
2038 menu's current contents.
2040 \sa popup(), QWidget::mapToGlobal()
2042 QAction *QMenu::exec(const QPoint &p, QAction *action)
2046 QEventLoop eventLoop;
2047 d->eventLoop = &eventLoop;
2050 QPointer<QObject> guard = this;
2051 (void) eventLoop.exec();
2055 action = d->syncAction;
2064 Executes a menu synchronously.
2066 The menu's actions are specified by the list of \a actions. The menu will
2067 pop up so that the specified action, \a at, appears at global position \a
2068 pos. If \a at is not specified then the menu appears at position \a
2069 pos. \a parent is the menu's parent widget; specifying the parent will
2070 provide context when \a pos alone is not enough to decide where the menu
2071 should go (e.g., with multiple desktops or when the parent is embedded in
2074 The function returns the triggered QAction in either the popup
2075 menu or one of its submenus, or 0 if no item was triggered
2076 (normally because the user pressed Esc).
2078 This is equivalent to:
2079 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 6
2081 \sa popup(), QWidget::mapToGlobal()
2083 QAction *QMenu::exec(QList<QAction*> actions, const QPoint &pos, QAction *at, QWidget *parent)
2086 menu.addActions(actions);
2087 return menu.exec(pos, at);
2093 Executes a menu synchronously.
2095 The menu's actions are specified by the list of \a actions. The menu
2096 will pop up so that the specified action, \a at, appears at global
2097 position \a pos. If \a at is not specified then the menu appears
2100 The function returns the triggered QAction in either the popup
2101 menu or one of its submenus, or 0 if no item was triggered
2102 (normally because the user pressed Esc).
2104 This is equivalent to:
2105 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 6
2107 \sa popup(), QWidget::mapToGlobal()
2109 QAction *QMenu::exec(QList<QAction*> actions, const QPoint &pos, QAction *at)
2112 return exec(actions, pos, at, 0);
2118 void QMenu::hideEvent(QHideEvent *)
2123 d->eventLoop->exit();
2124 d->setCurrentAction(0);
2125 #ifndef QT_NO_ACCESSIBILITY
2126 QAccessible::updateAccessibility(this, 0, QAccessible::PopupMenuEnd);
2128 #ifndef QT_NO_MENUBAR
2129 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->causedPopup.widget))
2130 mb->d_func()->setCurrentAction(0);
2133 d->hasHadMouse = false;
2134 d->causedPopup.widget = 0;
2135 d->causedPopup.action = 0;
2137 d->scroll->scrollTimer.stop(); //make sure the timer stops
2143 void QMenu::paintEvent(QPaintEvent *e)
2146 d->updateActionRects();
2148 QRegion emptyArea = QRegion(rect());
2150 QStyleOptionMenuItem menuOpt;
2151 menuOpt.initFrom(this);
2152 menuOpt.state = QStyle::State_None;
2153 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2154 menuOpt.maxIconWidth = 0;
2155 menuOpt.tabWidth = 0;
2156 style()->drawPrimitive(QStyle::PE_PanelMenu, &menuOpt, &p, this);
2158 //draw the items that need updating..
2159 for (int i = 0; i < d->actions.count(); ++i) {
2160 QAction *action = d->actions.at(i);
2161 QRect adjustedActionRect = d->actionRects.at(i);
2162 if (!e->rect().intersects(adjustedActionRect)
2163 || d->widgetItems.value(action))
2165 //set the clip region to be extra safe (and adjust for the scrollers)
2166 QRegion adjustedActionReg(adjustedActionRect);
2167 emptyArea -= adjustedActionReg;
2168 p.setClipRegion(adjustedActionReg);
2170 QStyleOptionMenuItem opt;
2171 initStyleOption(&opt, action);
2172 opt.rect = adjustedActionRect;
2173 style()->drawControl(QStyle::CE_MenuItem, &opt, &p, this);
2176 const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, this);
2177 //draw the scroller regions..
2179 menuOpt.menuItemType = QStyleOptionMenuItem::Scroller;
2180 menuOpt.state |= QStyle::State_Enabled;
2181 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) {
2182 menuOpt.rect.setRect(fw, fw, width() - (fw * 2), d->scrollerHeight());
2183 emptyArea -= QRegion(menuOpt.rect);
2184 p.setClipRect(menuOpt.rect);
2185 style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this);
2187 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown) {
2188 menuOpt.rect.setRect(fw, height() - d->scrollerHeight() - fw, width() - (fw * 2),
2189 d->scrollerHeight());
2190 emptyArea -= QRegion(menuOpt.rect);
2191 menuOpt.state |= QStyle::State_DownArrow;
2192 p.setClipRect(menuOpt.rect);
2193 style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this);
2196 //paint the tear off..
2198 menuOpt.menuItemType = QStyleOptionMenuItem::TearOff;
2199 menuOpt.rect.setRect(fw, fw, width() - (fw * 2),
2200 style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this));
2201 if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2202 menuOpt.rect.translate(0, d->scrollerHeight());
2203 emptyArea -= QRegion(menuOpt.rect);
2204 p.setClipRect(menuOpt.rect);
2205 menuOpt.state = QStyle::State_None;
2206 if (d->tearoffHighlighted)
2207 menuOpt.state |= QStyle::State_Selected;
2208 style()->drawControl(QStyle::CE_MenuTearoff, &menuOpt, &p, this);
2213 borderReg += QRect(0, 0, fw, height()); //left
2214 borderReg += QRect(width()-fw, 0, fw, height()); //right
2215 borderReg += QRect(0, 0, width(), fw); //top
2216 borderReg += QRect(0, height()-fw, width(), fw); //bottom
2217 p.setClipRegion(borderReg);
2218 emptyArea -= borderReg;
2219 QStyleOptionFrame frame;
2220 frame.rect = rect();
2221 frame.palette = palette();
2222 frame.state = QStyle::State_None;
2223 frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth);
2224 frame.midLineWidth = 0;
2225 style()->drawPrimitive(QStyle::PE_FrameMenu, &frame, &p, this);
2228 //finally the rest of the space
2229 p.setClipRegion(emptyArea);
2230 menuOpt.state = QStyle::State_None;
2231 menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea;
2232 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2233 menuOpt.rect = rect();
2234 menuOpt.menuRect = rect();
2235 style()->drawControl(QStyle::CE_MenuEmptyArea, &menuOpt, &p, this);
2238 #ifndef QT_NO_WHEELEVENT
2242 void QMenu::wheelEvent(QWheelEvent *e)
2245 if (d->scroll && rect().contains(e->pos()))
2246 d->scrollMenu(e->delta() > 0 ?
2247 QMenuPrivate::QMenuScroller::ScrollUp : QMenuPrivate::QMenuScroller::ScrollDown);
2254 void QMenu::mousePressEvent(QMouseEvent *e)
2257 if (d->aboutToHide || d->mouseEventTaken(e))
2259 if (!rect().contains(e->pos())) {
2261 && QRect(d->noReplayFor->mapToGlobal(QPoint()), d->noReplayFor->size()).contains(e->globalPos()))
2262 setAttribute(Qt::WA_NoMouseReplay);
2263 if (d->eventLoop) // synchronous operation
2265 d->hideUpToMenuBar();
2268 d->mouseDown = this;
2270 QAction *action = d->actionAt(e->pos());
2271 d->setCurrentAction(action, 20);
2278 void QMenu::mouseReleaseEvent(QMouseEvent *e)
2281 if (d->aboutToHide || d->mouseEventTaken(e))
2283 if(d->mouseDown != this) {
2290 QAction *action = d->actionAt(e->pos());
2292 if (action && action == d->currentAction) {
2293 if (!action->menu()){
2294 #if defined(Q_WS_WIN)
2295 //On Windows only context menus can be activated with the right button
2296 if (e->button() == Qt::LeftButton || d->topCausedWidget() == 0)
2298 d->activateAction(action, QAction::Trigger);
2300 } else if (d->hasMouseMoved(e->globalPos())) {
2301 d->hideUpToMenuBar();
2308 void QMenu::changeEvent(QEvent *e)
2311 if (e->type() == QEvent::StyleChange || e->type() == QEvent::FontChange ||
2312 e->type() == QEvent::LayoutDirectionChange) {
2314 setMouseTracking(style()->styleHint(QStyle::SH_Menu_MouseTracking, 0, this));
2317 if (!style()->styleHint(QStyle::SH_Menu_Scrollable, 0, this)) {
2320 } else if (!d->scroll) {
2321 d->scroll = new QMenuPrivate::QMenuScroller;
2322 d->scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
2324 } else if (e->type() == QEvent::EnabledChange) {
2325 if (d->tornPopup) // torn-off menu
2326 d->tornPopup->setEnabled(isEnabled());
2327 d->menuAction->setEnabled(isEnabled());
2328 if (d->platformMenu)
2329 d->platformMenu->setMenuEnabled(isEnabled());
2331 QWidget::changeEvent(e);
2339 QMenu::event(QEvent *e)
2342 switch (e->type()) {
2343 case QEvent::Polish:
2344 d->updateLayoutDirection();
2346 case QEvent::ShortcutOverride: {
2347 QKeyEvent *kev = static_cast<QKeyEvent*>(e);
2348 if (kev->key() == Qt::Key_Up || kev->key() == Qt::Key_Down
2349 || kev->key() == Qt::Key_Left || kev->key() == Qt::Key_Right
2350 || kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return
2351 || kev->key() == Qt::Key_Escape) {
2357 case QEvent::KeyPress: {
2358 QKeyEvent *ke = (QKeyEvent*)e;
2359 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
2364 case QEvent::ContextMenu:
2365 if(d->menuDelayTimer.isActive()) {
2366 d->menuDelayTimer.stop();
2367 internalDelayedPopup();
2370 case QEvent::Resize: {
2371 QStyleHintReturnMask menuMask;
2372 QStyleOption option;
2373 option.initFrom(this);
2374 if (style()->styleHint(QStyle::SH_Menu_Mask, &option, this, &menuMask)) {
2375 setMask(menuMask.region);
2378 d->updateActionRects();
2382 d->updateActionRects();
2383 if (d->currentAction)
2384 d->popupAction(d->currentAction, 0, false);
2386 #ifndef QT_NO_WHATSTHIS
2387 case QEvent::QueryWhatsThis:
2388 e->setAccepted(d->whatsThis.size());
2389 if (QAction *action = d->actionAt(static_cast<QHelpEvent*>(e)->pos())) {
2390 if (action->whatsThis().size() || action->menu())
2395 #ifdef QT_SOFTKEYS_ENABLED
2396 case QEvent::LanguageChange: {
2397 d->selectAction->setText(QSoftKeyManager::standardSoftKeyText(QSoftKeyManager::SelectSoftKey));
2398 d->cancelAction->setText(QSoftKeyManager::standardSoftKeyText(QSoftKeyManager::CancelSoftKey));
2405 return QWidget::event(e);
2411 bool QMenu::focusNextPrevChild(bool next)
2414 QKeyEvent ev(QEvent::KeyPress, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier);
2422 void QMenu::keyPressEvent(QKeyEvent *e)
2425 d->updateActionRects();
2427 if (isRightToLeft()) { // in reverse mode open/close key for submenues are reversed
2428 if (key == Qt::Key_Left)
2429 key = Qt::Key_Right;
2430 else if (key == Qt::Key_Right)
2434 if (key == Qt::Key_Tab) //means down
2436 if (key == Qt::Key_Backtab) //means up
2440 bool key_consumed = false;
2443 key_consumed = true;
2445 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop, true);
2448 key_consumed = true;
2450 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom, true);
2452 case Qt::Key_PageUp:
2453 key_consumed = true;
2454 if (d->currentAction && d->scroll) {
2455 if(d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2456 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollUp, true, true);
2458 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop, true);
2461 case Qt::Key_PageDown:
2462 key_consumed = true;
2463 if (d->currentAction && d->scroll) {
2464 if(d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
2465 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollDown, true, true);
2467 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom, true);
2471 case Qt::Key_Down: {
2472 key_consumed = true;
2473 QAction *nextAction = 0;
2474 QMenuPrivate::QMenuScroller::ScrollLocation scroll_loc = QMenuPrivate::QMenuScroller::ScrollStay;
2475 if (!d->currentAction) {
2476 if(key == Qt::Key_Down) {
2477 for(int i = 0; i < d->actions.count(); ++i) {
2478 QAction *act = d->actions.at(i);
2479 if (d->actionRects.at(i).isNull())
2481 if (!act->isSeparator() &&
2482 (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
2483 || act->isEnabled())) {
2489 for(int i = d->actions.count()-1; i >= 0; --i) {
2490 QAction *act = d->actions.at(i);
2491 if (d->actionRects.at(i).isNull())
2493 if (!act->isSeparator() &&
2494 (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
2495 || act->isEnabled())) {
2502 for(int i = 0, y = 0; !nextAction && i < d->actions.count(); i++) {
2503 QAction *act = d->actions.at(i);
2504 if (act == d->currentAction) {
2505 if (key == Qt::Key_Up) {
2506 for(int next_i = i-1; true; next_i--) {
2508 if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this))
2511 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
2512 next_i = d->actionRects.count()-1;
2514 QAction *next = d->actions.at(next_i);
2515 if (next == d->currentAction)
2517 if (d->actionRects.at(next_i).isNull())
2519 if (next->isSeparator() ||
2520 (!next->isEnabled() &&
2521 !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)))
2524 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)) {
2525 int topVisible = d->scrollerHeight();
2527 topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);
2528 if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.at(next_i).height())
2529 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
2533 if (!nextAction && d->tearoff)
2534 d->tearoffHighlighted = 1;
2536 y += d->actionRects.at(i).height();
2537 for(int next_i = i+1; true; next_i++) {
2538 if (next_i == d->actionRects.count()) {
2539 if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this))
2542 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
2545 QAction *next = d->actions.at(next_i);
2546 if (next == d->currentAction)
2548 if (d->actionRects.at(next_i).isNull())
2550 if (next->isSeparator() ||
2551 (!next->isEnabled() &&
2552 !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)))
2555 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)) {
2556 int bottomVisible = height() - d->scrollerHeight();
2557 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2558 bottomVisible -= d->scrollerHeight();
2560 bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);
2561 if ((y + d->scroll->scrollOffset + d->actionRects.at(next_i).height()) > bottomVisible)
2562 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
2569 y += d->actionRects.at(i).height();
2573 if (d->scroll && scroll_loc != QMenuPrivate::QMenuScroller::ScrollStay) {
2574 d->scroll->scrollTimer.stop();
2575 d->scrollMenu(nextAction, scroll_loc);
2577 d->setCurrentAction(nextAction, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
2582 if (d->currentAction && d->currentAction->isEnabled() && d->currentAction->menu()) {
2583 d->popupAction(d->currentAction, 0, true);
2584 key_consumed = true;
2588 case Qt::Key_Left: {
2589 if (d->currentAction && !d->scroll) {
2590 QAction *nextAction = 0;
2591 if (key == Qt::Key_Left) {
2592 QRect actionR = d->actionRect(d->currentAction);
2593 for(int x = actionR.left()-1; !nextAction && x >= 0; x--)
2594 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
2596 QRect actionR = d->actionRect(d->currentAction);
2597 for(int x = actionR.right()+1; !nextAction && x < width(); x++)
2598 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
2601 d->setCurrentAction(nextAction, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
2602 key_consumed = true;
2605 if (!key_consumed && key == Qt::Key_Left && qobject_cast<QMenu*>(d->causedPopup.widget)) {
2606 QPointer<QWidget> caused = d->causedPopup.widget;
2610 key_consumed = true;
2618 key_consumed = true;
2619 if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, 0, this))
2622 #ifndef QT_NO_MENUBAR
2623 if (QMenuBar *mb = qobject_cast<QMenuBar*>(QApplication::focusWidget())) {
2624 mb->d_func()->setKeyboardMode(false);
2630 case Qt::Key_Escape:
2631 #ifdef QT_KEYPAD_NAVIGATION
2634 key_consumed = true;
2640 QPointer<QWidget> caused = d->causedPopup.widget;
2641 d->hideMenu(this); // hide after getting causedPopup
2642 #ifndef QT_NO_MENUBAR
2643 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
2644 mb->d_func()->setCurrentAction(d->menuAction);
2645 mb->d_func()->setKeyboardMode(true);
2652 if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem, 0, this))
2654 // for motif, fall through
2655 #ifdef QT_KEYPAD_NAVIGATION
2656 case Qt::Key_Select:
2658 case Qt::Key_Return:
2659 case Qt::Key_Enter: {
2660 if (!d->currentAction) {
2661 d->setFirstActionActive();
2662 key_consumed = true;
2668 if (d->currentAction->menu())
2669 d->popupAction(d->currentAction, 0, true);
2671 d->activateAction(d->currentAction, QAction::Trigger);
2672 key_consumed = true;
2675 #ifndef QT_NO_WHATSTHIS
2677 if (!d->currentAction || d->currentAction->whatsThis().isNull())
2679 QWhatsThis::enterWhatsThisMode();
2680 d->activateAction(d->currentAction, QAction::Trigger);
2684 key_consumed = false;
2687 if (!key_consumed) { // send to menu bar
2688 if ((!e->modifiers() || e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ShiftModifier) &&
2689 e->text().length()==1) {
2690 bool activateAction = false;
2691 QAction *nextAction = 0;
2692 if (style()->styleHint(QStyle::SH_Menu_KeyboardSearch, 0, this) && !e->modifiers()) {
2693 int best_match_count = 0;
2694 d->searchBufferTimer.start(2000, this);
2695 d->searchBuffer += e->text();
2696 for(int i = 0; i < d->actions.size(); ++i) {
2697 int match_count = 0;
2698 if (d->actionRects.at(i).isNull())
2700 QAction *act = d->actions.at(i);
2701 const QString act_text = act->text();
2702 for(int c = 0; c < d->searchBuffer.size(); ++c) {
2703 if(act_text.indexOf(d->searchBuffer.at(c), 0, Qt::CaseInsensitive) != -1)
2706 if(match_count > best_match_count) {
2707 best_match_count = match_count;
2712 #ifndef QT_NO_SHORTCUT
2715 QAction *first = 0, *currentSelected = 0, *firstAfterCurrent = 0;
2716 QChar c = e->text().at(0).toUpper();
2717 for(int i = 0; i < d->actions.size(); ++i) {
2718 if (d->actionRects.at(i).isNull())
2720 QAction *act = d->actions.at(i);
2721 QKeySequence sequence = QKeySequence::mnemonic(act->text());
2722 int key = sequence[0] & 0xffff;
2723 if (key == c.unicode()) {
2727 if (act == d->currentAction)
2728 currentSelected = act;
2729 else if (!firstAfterCurrent && currentSelected)
2730 firstAfterCurrent = act;
2733 if (clashCount == 1)
2734 activateAction = true;
2735 if (clashCount >= 1) {
2736 if (clashCount == 1 || !currentSelected || !firstAfterCurrent)
2739 nextAction = firstAfterCurrent;
2744 key_consumed = true;
2746 d->scrollMenu(nextAction, QMenuPrivate::QMenuScroller::ScrollCenter, false);
2747 d->setCurrentAction(nextAction, 20, QMenuPrivate::SelectedFromElsewhere, true);
2748 if (!nextAction->menu() && activateAction) {
2750 d->activateAction(nextAction, QAction::Trigger);
2754 if (!key_consumed) {
2755 #ifndef QT_NO_MENUBAR
2756 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->topCausedWidget())) {
2757 QAction *oldAct = mb->d_func()->currentAction;
2758 QApplication::sendEvent(mb, e);
2759 if (mb->d_func()->currentAction != oldAct)
2760 key_consumed = true;
2766 if (key_consumed && (e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta))
2767 QApplication::beep();
2768 #endif // Q_OS_WIN32
2779 void QMenu::mouseMoveEvent(QMouseEvent *e)
2782 if (!isVisible() || d->aboutToHide || d->mouseEventTaken(e))
2785 if (d->motions == 0) // ignore first mouse move event (see enterEvent())
2787 d->hasHadMouse = d->hasHadMouse || rect().contains(e->pos());
2789 QAction *action = d->actionAt(e->pos());
2792 && (!d->currentAction
2793 || !(d->currentAction->menu() && d->currentAction->menu()->isVisible())))
2794 d->setCurrentAction(0);
2796 } else if(e->buttons()) {
2797 d->mouseDown = this;
2799 if (d->sloppyRegion.contains(e->pos())) {
2800 d->sloppyAction = action;
2801 QMenuPrivate::sloppyDelayTimer = startTimer(style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this)*6);
2802 } else if (action != d->currentAction) {
2803 d->setCurrentAction(action, style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this));
2810 void QMenu::enterEvent(QEvent *)
2812 d_func()->motions = -1; // force us to ignore the generate mouse move in mouseMoveEvent()
2818 void QMenu::leaveEvent(QEvent *)
2821 d->sloppyAction = 0;
2822 if (!d->sloppyRegion.isEmpty())
2823 d->sloppyRegion = QRegion();
2824 if (!d->activeMenu && d->currentAction)
2832 QMenu::timerEvent(QTimerEvent *e)
2835 if (d->scroll && d->scroll->scrollTimer.timerId() == e->timerId()) {
2836 d->scrollMenu((QMenuPrivate::QMenuScroller::ScrollDirection)d->scroll->scrollDirection);
2837 if (d->scroll->scrollFlags == QMenuPrivate::QMenuScroller::ScrollNone)
2838 d->scroll->scrollTimer.stop();
2839 } else if(d->menuDelayTimer.timerId() == e->timerId()) {
2840 d->menuDelayTimer.stop();
2841 internalDelayedPopup();
2842 } else if(QMenuPrivate::sloppyDelayTimer == e->timerId()) {
2843 killTimer(QMenuPrivate::sloppyDelayTimer);
2844 QMenuPrivate::sloppyDelayTimer = 0;
2845 internalSetSloppyAction();
2846 } else if(d->searchBufferTimer.timerId() == e->timerId()) {
2847 d->searchBuffer.clear();
2854 void QMenu::actionEvent(QActionEvent *e)
2858 setAttribute(Qt::WA_Resized, false);
2860 d->tornPopup->syncWithMenu(this, e);
2861 if (e->type() == QEvent::ActionAdded) {
2863 connect(e->action(), SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
2864 connect(e->action(), SIGNAL(hovered()), this, SLOT(_q_actionHovered()));
2866 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
2867 QWidget *widget = wa->requestWidget(this);
2869 d->widgetItems.insert(wa, widget);
2871 } else if (e->type() == QEvent::ActionRemoved) {
2872 e->action()->disconnect(this);
2873 if (e->action() == d->currentAction)
2874 d->currentAction = 0;
2875 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
2876 if (QWidget *widget = d->widgetItems.value(wa))
2877 wa->releaseWidget(widget);
2879 d->widgetItems.remove(e->action());
2882 if (d->platformMenu) {
2883 if (e->type() == QEvent::ActionAdded)
2884 d->platformMenu->addAction(e->action(), e->before());
2885 else if (e->type() == QEvent::ActionRemoved)
2886 d->platformMenu->removeAction(e->action());
2887 else if (e->type() == QEvent::ActionChanged)
2888 d->platformMenu->syncAction(e->action());
2891 #if defined(Q_WS_WINCE) && !defined(QT_NO_MENUBAR)
2893 d->wce_menu = new QMenuPrivate::QWceMenuPrivate;
2894 if (e->type() == QEvent::ActionAdded)
2895 d->wce_menu->addAction(e->action(), d->wce_menu->findAction(e->before()));
2896 else if (e->type() == QEvent::ActionRemoved)
2897 d->wce_menu->removeAction(e->action());
2898 else if (e->type() == QEvent::ActionChanged)
2899 d->wce_menu->syncAction(e->action());
2903 if (!d->symbian_menu)
2904 d->symbian_menu = new QMenuPrivate::QSymbianMenuPrivate;
2905 if (e->type() == QEvent::ActionAdded)
2906 d->symbian_menu->addAction(e->action(), d->symbian_menu->findAction(e->before()));
2907 else if (e->type() == QEvent::ActionRemoved)
2908 d->symbian_menu->removeAction(e->action());
2909 else if (e->type() == QEvent::ActionChanged)
2910 d->symbian_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