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 "qplatformtheme_qpa.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 <qplatformtheme_qpa.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(q);
159 #ifdef QT_SOFTKEYS_ENABLED
160 selectAction = QSoftKeyManager::createKeyedAction(QSoftKeyManager::SelectSoftKey, Qt::Key_Select, q);
161 cancelAction = QSoftKeyManager::createKeyedAction(QSoftKeyManager::CancelSoftKey, Qt::Key_Back, q);
162 selectAction->setPriority(QAction::HighPriority);
163 cancelAction->setPriority(QAction::HighPriority);
164 q->addAction(selectAction);
165 q->addAction(cancelAction);
169 int QMenuPrivate::scrollerHeight() const
172 return qMax(QApplication::globalStrut().height(), q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q));
175 //Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't
176 QRect QMenuPrivate::popupGeometry(const QWidget *widget) const
178 if (QGuiApplicationPrivate::platformTheme() &&
179 QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool()) {
180 return QApplication::desktop()->screenGeometry(widget);
182 return QApplication::desktop()->availableGeometry(widget);
186 //Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't
187 QRect QMenuPrivate::popupGeometry(int screen) const
189 if (QGuiApplicationPrivate::platformTheme() &&
190 QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool()) {
191 return QApplication::desktop()->screenGeometry(screen);
193 return QApplication::desktop()->availableGeometry(screen);
197 QList<QPointer<QWidget> > QMenuPrivate::calcCausedStack() const
199 QList<QPointer<QWidget> > ret;
200 for(QWidget *widget = causedPopup.widget; widget; ) {
202 if (QTornOffMenu *qtmenu = qobject_cast<QTornOffMenu*>(widget))
203 ret += qtmenu->d_func()->causedStack;
204 if (QMenu *qmenu = qobject_cast<QMenu*>(widget))
205 widget = qmenu->d_func()->causedPopup.widget;
212 void QMenuPrivate::updateActionRects() const
215 updateActionRects(popupGeometry(q));
218 void QMenuPrivate::updateActionRects(const QRect &screen) const
226 //let's reinitialize the buffer
227 actionRects.resize(actions.count());
228 actionRects.fill(QRect());
230 int lastVisibleAction = getLastVisibleAction();
232 int max_column_width = 0,
233 dh = screen.height(),
235 QStyle *style = q->style();
238 const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q),
239 vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q),
240 icone = style->pixelMetric(QStyle::PM_SmallIconSize, &opt, q);
241 const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q);
242 const int deskFw = style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, &opt, q);
243 const int tearoffHeight = tearoff ? style->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q) : 0;
245 //for compatibility now - will have to refactor this away
248 hasCheckableItems = false;
252 for (int i = 0; i < actions.count(); ++i) {
253 QAction *action = actions.at(i);
254 if (action->isSeparator() || !action->isVisible() || widgetItems.contains(action))
257 hasCheckableItems |= action->isCheckable();
258 QIcon is = action->icon();
260 maxIconWidth = qMax<uint>(maxIconWidth, icone + 4);
265 QFontMetrics qfm = q->fontMetrics();
266 bool previousWasSeparator = true; // this is true to allow removing the leading separators
267 for(int i = 0; i <= lastVisibleAction; i++) {
268 QAction *action = actions.at(i);
270 if (!action->isVisible() ||
271 (collapsibleSeparators && previousWasSeparator && action->isSeparator()))
272 continue; // we continue, this action will get an empty QRect
274 previousWasSeparator = action->isSeparator();
276 //let the style modify the above size..
277 QStyleOptionMenuItem opt;
278 q->initStyleOption(&opt, action);
279 const QFontMetrics &fm = opt.fontMetrics;
282 if (QWidget *w = widgetItems.value(action)) {
283 sz = w->sizeHint().expandedTo(w->minimumSize()).expandedTo(w->minimumSizeHint()).boundedTo(w->maximumSize());
285 //calc what I think the size is..
286 if (action->isSeparator()) {
289 QString s = action->text();
290 int t = s.indexOf(QLatin1Char('\t'));
292 tabWidth = qMax(int(tabWidth), qfm.width(s.mid(t+1)));
294 #ifndef QT_NO_SHORTCUT
296 QKeySequence seq = action->shortcut();
298 tabWidth = qMax(int(tabWidth), qfm.width(seq));
301 sz.setWidth(fm.boundingRect(QRect(), Qt::TextSingleLine | Qt::TextShowMnemonic, s).width());
302 sz.setHeight(qMax(fm.height(), qfm.height()));
304 QIcon is = action->icon();
306 QSize is_sz = QSize(icone, icone);
307 if (is_sz.height() > sz.height())
308 sz.setHeight(is_sz.height());
311 sz = style->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, q);
316 max_column_width = qMax(max_column_width, sz.width());
319 y+sz.height()+vmargin > dh - (deskFw * 2)) {
325 actionRects[i] = QRect(0, 0, sz.width(), sz.height());
329 max_column_width += tabWidth; //finally add in the tab width
330 const int sfcMargin = style->sizeFromContents(QStyle::CT_Menu, &opt, QApplication::globalStrut(), q).width() - QApplication::globalStrut().width();
331 const int min_column_width = q->minimumWidth() - (sfcMargin + leftmargin + rightmargin + 2 * (fw + hmargin));
332 max_column_width = qMax(min_column_width, max_column_width);
335 const int base_y = vmargin + fw + topmargin +
336 (scroll ? scroll->scrollOffset : 0) +
338 int x = hmargin + fw + leftmargin;
341 for(int i = 0; i < actions.count(); i++) {
342 QRect &rect = actionRects[i];
346 y+rect.height() > dh - deskFw * 2) {
347 x += max_column_width + hmargin;
350 rect.translate(x, y); //move
351 rect.setWidth(max_column_width); //uniform width
353 //we need to update the widgets geometry
354 if (QWidget *widget = widgetItems.value(actions.at(i))) {
355 widget->setGeometry(rect);
356 widget->setVisible(actions.at(i)->isVisible());
364 QSize QMenuPrivate::adjustMenuSizeForScreen(const QRect &screen)
367 QSize ret = screen.size();
369 updateActionRects(screen);
370 const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
371 ret.setWidth(actionRects.at(getLastVisibleAction()).right() + fw);
375 int QMenuPrivate::getLastVisibleAction() const
377 //let's try to get the last visible action
378 int lastVisibleAction = actions.count() - 1;
379 for (;lastVisibleAction >= 0; --lastVisibleAction) {
380 const QAction *action = actions.at(lastVisibleAction);
381 if (action->isVisible()) {
382 //removing trailing separators
383 if (action->isSeparator() && collapsibleSeparators)
388 return lastVisibleAction;
392 QRect QMenuPrivate::actionRect(QAction *act) const
394 int index = actions.indexOf(act);
400 //we found the action
401 return actionRects.at(index);
404 #if defined(Q_OS_MAC)
405 static const qreal MenuFadeTimeInSec = 0.150;
408 void QMenuPrivate::hideUpToMenuBar()
411 bool fadeMenus = q->style()->styleHint(QStyle::SH_Menu_FadeOutOnHide);
413 QWidget *caused = causedPopup.widget;
414 hideMenu(q); //hide after getting causedPopup
416 #ifndef QT_NO_MENUBAR
417 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
418 mb->d_func()->setCurrentAction(0);
419 mb->d_func()->setKeyboardMode(false);
423 if (QMenu *m = qobject_cast<QMenu*>(caused)) {
424 caused = m->d_func()->causedPopup.widget;
425 if (!m->d_func()->tornoff)
426 hideMenu(m, fadeMenus);
427 if (!fadeMenus) // Mac doesn't clear the action until after hidden.
428 m->d_func()->setCurrentAction(0);
432 #if defined(Q_WS_MAC)
434 QEventLoop eventLoop;
435 QTimer::singleShot(int(MenuFadeTimeInSec * 1000), &eventLoop, SLOT(quit()));
436 QMacWindowFader::currentFader()->performFade();
444 void QMenuPrivate::hideMenu(QMenu *menu, bool justRegister)
448 #if !defined(QT_NO_EFFECTS)
449 menu->blockSignals(true);
451 // Flash item which is about to trigger (if any).
452 if (menu->style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)
453 && currentAction && currentAction == actionAboutToTrigger
454 && menu->actions().contains(currentAction)) {
455 QEventLoop eventLoop;
456 QAction *activeAction = currentAction;
458 menu->setActiveAction(0);
459 QTimer::singleShot(60, &eventLoop, SLOT(quit()));
462 // Select and wait 20 ms.
463 menu->setActiveAction(activeAction);
464 QTimer::singleShot(20, &eventLoop, SLOT(quit()));
469 if (menu->style()->styleHint(QStyle::SH_Menu_FadeOutOnHide)) {
471 // Should be something like: q->transitionWindow(Qt::FadeOutTransition, MenuFadeTimeInSec);
472 // Hopefully we'll integrate qt/research/windowtransitions into main before 4.4.
473 // Talk to Richard, Trenton or Bjoern.
474 #if defined(Q_WS_MAC)
476 QMacWindowFader::currentFader()->setFadeDuration(MenuFadeTimeInSec);
477 QMacWindowFader::currentFader()->registerWindowToFade(menu);
479 macWindowFade(qt_mac_window_for(menu), MenuFadeTimeInSec);
485 menu->blockSignals(false);
486 #endif // QT_NO_EFFECTS
491 void QMenuPrivate::popupAction(QAction *action, int delay, bool activateFirst)
494 if (action && action->isEnabled()) {
496 q->internalDelayedPopup();
497 else if (!menuDelayTimer.isActive() && (!action->menu() || !action->menu()->isVisible()))
498 menuDelayTimer.start(delay, q);
499 if (activateFirst && action->menu())
500 action->menu()->d_func()->setFirstActionActive();
501 } else if (QMenu *menu = activeMenu) { //hide the current item
507 void QMenuPrivate::setSyncAction()
510 QAction *current = currentAction;
511 if(current && (!current->isEnabled() || current->menu() || current->isSeparator()))
513 for(QWidget *caused = q; caused;) {
514 if (QMenu *m = qobject_cast<QMenu*>(caused)) {
515 caused = m->d_func()->causedPopup.widget;
516 if (m->d_func()->eventLoop)
517 m->d_func()->syncAction = current; // synchronous operation
525 void QMenuPrivate::setFirstActionActive()
529 for(int i = 0, saccum = 0; i < actions.count(); i++) {
530 const QRect &rect = actionRects.at(i);
533 if (scroll && scroll->scrollFlags & QMenuScroller::ScrollUp) {
534 saccum -= rect.height();
535 if (saccum > scroll->scrollOffset - scrollerHeight())
538 QAction *act = actions.at(i);
539 if (!act->isSeparator() &&
540 (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
541 || act->isEnabled())) {
542 setCurrentAction(act);
548 // popup == -1 means do not popup, 0 means immediately, others mean use a timer
549 void QMenuPrivate::setCurrentAction(QAction *action, int popup, SelectionReason reason, bool activateFirst)
552 tearoffHighlighted = 0;
554 q->update(actionRect(currentAction));
557 if (!sloppyRegion.isEmpty())
558 sloppyRegion = QRegion();
559 QMenu *hideActiveMenu = activeMenu;
560 #ifndef QT_NO_STATUSTIP
561 QAction *previousAction = currentAction;
564 currentAction = action;
566 if (!action->isSeparator()) {
567 activateAction(action, QAction::Hover);
569 hideActiveMenu = 0; //will be done "later"
570 // if the menu is visible then activate the required action,
571 // otherwise we just mark the action as currentAction
572 // and activate it when the menu will be popuped.
574 popupAction(currentAction, popup, activateFirst);
576 q->update(actionRect(action));
578 if (reason == SelectedFromKeyboard) {
579 QWidget *widget = widgetItems.value(action);
581 if (widget->focusPolicy() != Qt::NoFocus)
582 widget->setFocus(Qt::TabFocusReason);
584 //when the action has no QWidget, the QMenu itself should
586 // Since the menu is a pop-up, it uses the popup reason.
587 if (!q->hasFocus()) {
588 q->setFocus(Qt::PopupFocusReason);
592 } else { //action is a separator
594 hideActiveMenu = 0; //will be done "later"
596 #ifndef QT_NO_STATUSTIP
597 } else if (previousAction) {
598 previousAction->d_func()->showStatusText(topCausedWidget(), QString());
601 if (hideActiveMenu) {
603 #ifndef QT_NO_EFFECTS
604 // kill any running effect
608 hideMenu(hideActiveMenu);
612 //return the top causedPopup.widget that is not a QMenu
613 QWidget *QMenuPrivate::topCausedWidget() const
615 QWidget* top = causedPopup.widget;
616 while (QMenu* m = qobject_cast<QMenu *>(top))
617 top = m->d_func()->causedPopup.widget;
621 QAction *QMenuPrivate::actionAt(QPoint p) const
623 if (!q_func()->rect().contains(p)) //sanity check
626 for(int i = 0; i < actionRects.count(); i++) {
627 if (actionRects.at(i).contains(p))
628 return actions.at(i);
633 void QMenuPrivate::setOverrideMenuAction(QAction *a)
636 QObject::disconnect(menuAction, SIGNAL(destroyed()), q, SLOT(_q_overrideMenuActionDestroyed()));
639 QObject::connect(a, SIGNAL(destroyed()), q, SLOT(_q_overrideMenuActionDestroyed()));
640 } else { //we revert back to the default action created by the QMenu itself
641 menuAction = defaultMenuAction;
645 void QMenuPrivate::_q_overrideMenuActionDestroyed()
647 menuAction=defaultMenuAction;
651 void QMenuPrivate::updateLayoutDirection()
654 //we need to mimic the cause of the popup's layout direction
655 //to allow setting it on a mainwindow for example
656 //we call setLayoutDirection_helper to not overwrite a user-defined value
657 if (!q->testAttribute(Qt::WA_SetLayoutDirection)) {
658 if (QWidget *w = causedPopup.widget)
659 setLayoutDirection_helper(w->layoutDirection());
660 else if (QWidget *w = q->parentWidget())
661 setLayoutDirection_helper(w->layoutDirection());
663 setLayoutDirection_helper(QApplication::layoutDirection());
669 Returns the action associated with this menu.
671 QAction *QMenu::menuAction() const
673 return d_func()->menuAction;
677 \property QMenu::title
678 \brief The title of the menu
680 This is equivalent to the QAction::text property of the menuAction().
682 By default, this property contains an empty string.
684 QString QMenu::title() const
686 return d_func()->menuAction->text();
689 void QMenu::setTitle(const QString &text)
691 d_func()->menuAction->setText(text);
695 \property QMenu::icon
697 \brief The icon of the menu
699 This is equivalent to the QAction::icon property of the menuAction().
701 By default, if no icon is explicitly set, this property contains a null icon.
703 QIcon QMenu::icon() const
705 return d_func()->menuAction->icon();
708 void QMenu::setIcon(const QIcon &icon)
710 d_func()->menuAction->setIcon(icon);
714 //actually performs the scrolling
715 void QMenuPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation location, bool active)
718 if (!scroll || !scroll->scrollFlags)
722 const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp) ? scrollerHeight() : 0;
723 const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollerHeight() : 0;
724 const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
725 const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
727 if (location == QMenuScroller::ScrollTop) {
728 for(int i = 0, saccum = 0; i < actions.count(); i++) {
729 if (actions.at(i) == action) {
730 newOffset = topScroll - saccum;
733 saccum += actionRects.at(i).height();
736 for(int i = 0, saccum = 0; i < actions.count(); i++) {
737 saccum += actionRects.at(i).height();
738 if (actions.at(i) == action) {
739 if (location == QMenuScroller::ScrollCenter)
740 newOffset = ((q->height() / 2) - botScroll) - (saccum - topScroll);
742 newOffset = (q->height() - botScroll) - saccum;
750 //figure out which scroll flags
751 uint newScrollFlags = QMenuScroller::ScrollNone;
752 if (newOffset < 0) //easy and cheap one
753 newScrollFlags |= QMenuScroller::ScrollUp;
754 int saccum = newOffset;
755 for(int i = 0; i < actionRects.count(); i++) {
756 saccum += actionRects.at(i).height();
757 if (saccum > q->height()) {
758 newScrollFlags |= QMenuScroller::ScrollDown;
763 if (!(newScrollFlags & QMenuScroller::ScrollDown) && (scroll->scrollFlags & QMenuScroller::ScrollDown)) {
764 newOffset = q->height() - (saccum - newOffset) - fw*2 - vmargin; //last item at bottom
767 if (!(newScrollFlags & QMenuScroller::ScrollUp) && (scroll->scrollFlags & QMenuScroller::ScrollUp)) {
768 newOffset = 0; //first item at top
771 if (newScrollFlags & QMenuScroller::ScrollUp)
772 newOffset -= vmargin;
774 QRect screen = popupGeometry(q);
775 const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q);
776 if (q->height() < screen.height()-(desktopFrame*2)-1) {
777 QRect geom = q->geometry();
778 if (newOffset > scroll->scrollOffset && (scroll->scrollFlags & newScrollFlags & QMenuScroller::ScrollUp)) { //scroll up
779 const int newHeight = geom.height()-(newOffset-scroll->scrollOffset);
780 if(newHeight > geom.height())
781 geom.setHeight(newHeight);
782 } else if(scroll->scrollFlags & newScrollFlags & QMenuScroller::ScrollDown) {
783 int newTop = geom.top() + (newOffset-scroll->scrollOffset);
784 if (newTop < desktopFrame+screen.top())
785 newTop = desktopFrame+screen.top();
786 if (newTop < geom.top()) {
789 newScrollFlags &= ~QMenuScroller::ScrollUp;
792 if (geom.bottom() > screen.bottom() - desktopFrame)
793 geom.setBottom(screen.bottom() - desktopFrame);
794 if (geom.top() < desktopFrame+screen.top())
795 geom.setTop(desktopFrame+screen.top());
796 if (geom != q->geometry()) {
798 if (newScrollFlags & QMenuScroller::ScrollDown &&
799 q->geometry().top() - geom.top() >= -newOffset)
800 newScrollFlags &= ~QMenuScroller::ScrollDown;
802 q->setGeometry(geom);
806 //actually update flags
807 const int delta = qMin(0, newOffset) - scroll->scrollOffset; //make sure the new offset is always negative
808 if (!itemsDirty && delta) {
809 //we've scrolled so we need to update the action rects
810 for (int i = 0; i < actionRects.count(); ++i) {
811 QRect ¤t = actionRects[i];
812 current.moveTop(current.top() + delta);
814 //we need to update the widgets geometry
815 if (QWidget *w = widgetItems.value(actions.at(i)))
816 w->setGeometry(current);
819 scroll->scrollOffset += delta;
820 scroll->scrollFlags = newScrollFlags;
822 setCurrentAction(action);
824 q->update(); //issue an update so we see all the new state..
827 void QMenuPrivate::scrollMenu(QMenuScroller::ScrollLocation location, bool active)
831 if(location == QMenuScroller::ScrollBottom) {
832 for(int i = actions.size()-1; i >= 0; --i) {
833 QAction *act = actions.at(i);
834 if (actionRects.at(i).isNull())
836 if (!act->isSeparator() &&
837 (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
838 || act->isEnabled())) {
839 if(scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
840 scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollBottom, active);
842 setCurrentAction(act, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
846 } else if(location == QMenuScroller::ScrollTop) {
847 for(int i = 0; i < actions.size(); ++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::ScrollUp)
855 scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollTop, active);
857 setCurrentAction(act, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
865 void QMenuPrivate::scrollMenu(QMenuScroller::ScrollDirection direction, bool page, bool active)
868 if (!scroll || !(scroll->scrollFlags & direction)) //not really possible...
871 const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp) ? scrollerHeight() : 0;
872 const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollerHeight() : 0;
873 const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
874 const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
875 const int offset = topScroll ? topScroll-vmargin : 0;
876 if (direction == QMenuScroller::ScrollUp) {
877 for(int i = 0, saccum = 0; i < actions.count(); i++) {
878 saccum -= actionRects.at(i).height();
879 if (saccum <= scroll->scrollOffset-offset) {
880 scrollMenu(actions.at(i), page ? QMenuScroller::ScrollBottom : QMenuScroller::ScrollTop, active);
884 } else if (direction == QMenuScroller::ScrollDown) {
885 bool scrolled = false;
886 for(int i = 0, saccum = 0; i < actions.count(); i++) {
887 const int iHeight = actionRects.at(i).height();
889 if (saccum <= scroll->scrollOffset-offset) {
890 const int scrollerArea = q->height() - botScroll - fw*2;
891 int visible = (scroll->scrollOffset-offset) - saccum;
892 for(i++ ; i < actions.count(); i++) {
893 visible += actionRects.at(i).height();
894 if (visible > scrollerArea - topScroll) {
896 scrollMenu(actions.at(i), page ? QMenuScroller::ScrollTop : QMenuScroller::ScrollBottom, active);
904 scroll->scrollFlags &= ~QMenuScroller::ScrollDown;
910 /* This is poor-mans eventfilters. This avoids the use of
911 eventFilter (which can be nasty for users of QMenuBar's). */
912 bool QMenuPrivate::mouseEventTaken(QMouseEvent *e)
915 QPoint pos = q->mapFromGlobal(e->globalPos());
916 if (scroll && !activeMenu) { //let the scroller "steal" the event
917 bool isScroll = false;
918 if (pos.x() >= 0 && pos.x() < q->width()) {
919 for(int dir = QMenuScroller::ScrollUp; dir <= QMenuScroller::ScrollDown; dir = dir << 1) {
920 if (scroll->scrollFlags & dir) {
921 if (dir == QMenuScroller::ScrollUp)
922 isScroll = (pos.y() <= scrollerHeight());
923 else if (dir == QMenuScroller::ScrollDown)
924 isScroll = (pos.y() >= q->height() - scrollerHeight());
926 scroll->scrollDirection = dir;
933 scroll->scrollTimer.start(50, q);
936 scroll->scrollTimer.stop();
940 if (tearoff) { //let the tear off thingie "steal" the event..
941 QRect tearRect(0, 0, q->width(), q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q));
942 if (scroll && scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
943 tearRect.translate(0, scrollerHeight());
945 if (tearRect.contains(pos) && hasMouseMoved(e->globalPos())) {
947 tearoffHighlighted = 1;
948 if (e->type() == QEvent::MouseButtonRelease) {
950 tornPopup = new QTornOffMenu(q);
951 tornPopup->setGeometry(q->geometry());
957 tearoffHighlighted = 0;
960 if (q->frameGeometry().contains(e->globalPos())) //otherwise if the event is in our rect we want it..
963 for(QWidget *caused = causedPopup.widget; caused;) {
964 bool passOnEvent = false;
965 QWidget *next_widget = 0;
966 QPoint cpos = caused->mapFromGlobal(e->globalPos());
967 #ifndef QT_NO_MENUBAR
968 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
969 passOnEvent = mb->rect().contains(cpos);
972 if (QMenu *m = qobject_cast<QMenu*>(caused)) {
973 passOnEvent = m->rect().contains(cpos);
974 next_widget = m->d_func()->causedPopup.widget;
977 if(e->type() != QEvent::MouseButtonRelease || mouseDown == caused) {
978 QMouseEvent new_e(e->type(), cpos, caused->mapTo(caused->topLevelWidget(), cpos), e->screenPos(),
979 e->button(), e->buttons(), e->modifiers());
980 QApplication::sendEvent(caused, &new_e);
986 caused = next_widget;
991 void QMenuPrivate::activateCausedStack(const QList<QPointer<QWidget> > &causedStack, QAction *action, QAction::ActionEvent action_e, bool self)
993 QBoolBlocker guard(activationRecursionGuard);
995 action->activate(action_e);
997 for(int i = 0; i < causedStack.size(); ++i) {
998 QPointer<QWidget> widget = causedStack.at(i);
1002 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
1003 widget = qmenu->d_func()->causedPopup.widget;
1004 if (action_e == QAction::Trigger) {
1005 emit qmenu->triggered(action);
1006 } else if (action_e == QAction::Hover) {
1007 emit qmenu->hovered(action);
1009 #ifndef QT_NO_MENUBAR
1010 } else if (QMenuBar *qmenubar = qobject_cast<QMenuBar*>(widget)) {
1011 if (action_e == QAction::Trigger) {
1012 emit qmenubar->triggered(action);
1013 } else if (action_e == QAction::Hover) {
1014 emit qmenubar->hovered(action);
1016 break; //nothing more..
1022 void QMenuPrivate::activateAction(QAction *action, QAction::ActionEvent action_e, bool self)
1025 #ifndef QT_NO_WHATSTHIS
1026 bool inWhatsThisMode = QWhatsThis::inWhatsThisMode();
1028 if (!action || !q->isEnabled()
1029 || (action_e == QAction::Trigger
1030 #ifndef QT_NO_WHATSTHIS
1033 && (action->isSeparator() ||!action->isEnabled())))
1036 /* I have to save the caused stack here because it will be undone after popup execution (ie in the hide).
1037 Then I iterate over the list to actually send the events. --Sam
1039 const QList<QPointer<QWidget> > causedStack = calcCausedStack();
1040 if (action_e == QAction::Trigger) {
1041 #ifndef QT_NO_WHATSTHIS
1042 if (!inWhatsThisMode)
1043 actionAboutToTrigger = action;
1046 if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
1049 for(QWidget *widget = QApplication::activePopupWidget(); widget; ) {
1050 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
1053 widget = qmenu->d_func()->causedPopup.widget;
1060 #ifndef QT_NO_WHATSTHIS
1061 if (inWhatsThisMode) {
1062 QString s = action->whatsThis();
1065 QWhatsThis::showText(q->mapToGlobal(actionRect(action).center()), s, q);
1072 activateCausedStack(causedStack, action, action_e, self);
1075 if (action_e == QAction::Hover) {
1076 #ifndef QT_NO_ACCESSIBILITY
1077 if (QAccessible::isActive()) {
1078 int actionIndex = indexOf(action) + 1;
1079 QAccessible::updateAccessibility(QAccessibleEvent(QAccessible::Focus, q, actionIndex));
1080 QAccessible::updateAccessibility(QAccessibleEvent(QAccessible::Selection, q, actionIndex));
1083 action->showStatusText(topCausedWidget());
1085 actionAboutToTrigger = 0;
1089 void QMenuPrivate::_q_actionTriggered()
1092 if (QAction *action = qobject_cast<QAction *>(q->sender())) {
1093 QWeakPointer<QAction> actionGuard = action;
1094 emit q->triggered(action);
1095 if (!activationRecursionGuard && actionGuard) {
1096 //in case the action has not been activated by the mouse
1097 //we check the parent hierarchy
1098 QList< QPointer<QWidget> > list;
1099 for(QWidget *widget = q->parentWidget(); widget; ) {
1100 if (qobject_cast<QMenu*>(widget)
1101 #ifndef QT_NO_MENUBAR
1102 || qobject_cast<QMenuBar*>(widget)
1105 list.append(widget);
1106 widget = widget->parentWidget();
1111 activateCausedStack(list, action, QAction::Trigger, false);
1116 void QMenuPrivate::_q_actionHovered()
1119 if (QAction * action = qobject_cast<QAction *>(q->sender())) {
1120 emit q->hovered(action);
1124 bool QMenuPrivate::hasMouseMoved(const QPoint &globalPos)
1126 //determines if the mouse has moved (ie its initial position has
1127 //changed by more than QApplication::startDragDistance()
1128 //or if there were at least 6 mouse motions)
1129 return motions > 6 ||
1130 QApplication::startDragDistance() < (mousePopupPos - globalPos).manhattanLength();
1135 Initialize \a option with the values from this menu and information from \a action. This method
1136 is useful for subclasses when they need a QStyleOptionMenuItem, but don't want
1137 to fill in all the information themselves.
1139 \sa QStyleOption::initFrom() QMenuBar::initStyleOption()
1141 void QMenu::initStyleOption(QStyleOptionMenuItem *option, const QAction *action) const
1143 if (!option || !action)
1147 option->initFrom(this);
1148 option->palette = palette();
1149 option->state = QStyle::State_None;
1151 if (window()->isActiveWindow())
1152 option->state |= QStyle::State_Active;
1153 if (isEnabled() && action->isEnabled()
1154 && (!action->menu() || action->menu()->isEnabled()))
1155 option->state |= QStyle::State_Enabled;
1157 option->palette.setCurrentColorGroup(QPalette::Disabled);
1159 option->font = action->font().resolve(font());
1160 option->fontMetrics = QFontMetrics(option->font);
1162 if (d->currentAction && d->currentAction == action && !d->currentAction->isSeparator()) {
1163 option->state |= QStyle::State_Selected
1164 | (d->mouseDown ? QStyle::State_Sunken : QStyle::State_None);
1167 option->menuHasCheckableItems = d->hasCheckableItems;
1168 if (!action->isCheckable()) {
1169 option->checkType = QStyleOptionMenuItem::NotCheckable;
1171 option->checkType = (action->actionGroup() && action->actionGroup()->isExclusive())
1172 ? QStyleOptionMenuItem::Exclusive : QStyleOptionMenuItem::NonExclusive;
1173 option->checked = action->isChecked();
1176 option->menuItemType = QStyleOptionMenuItem::SubMenu;
1177 else if (action->isSeparator())
1178 option->menuItemType = QStyleOptionMenuItem::Separator;
1179 else if (d->defaultAction == action)
1180 option->menuItemType = QStyleOptionMenuItem::DefaultItem;
1182 option->menuItemType = QStyleOptionMenuItem::Normal;
1183 if (action->isIconVisibleInMenu())
1184 option->icon = action->icon();
1185 QString textAndAccel = action->text();
1186 #ifndef QT_NO_SHORTCUT
1187 if (textAndAccel.indexOf(QLatin1Char('\t')) == -1) {
1188 QKeySequence seq = action->shortcut();
1190 textAndAccel += QLatin1Char('\t') + QString(seq);
1193 option->text = textAndAccel;
1194 option->tabWidth = d->tabWidth;
1195 option->maxIconWidth = d->maxIconWidth;
1196 option->menuRect = rect();
1201 \brief The QMenu class provides a menu widget for use in menu
1202 bars, context menus, and other popup menus.
1204 \ingroup mainwindow-classes
1205 \ingroup basicwidgets
1208 A menu widget is a selection menu. It can be either a pull-down
1209 menu in a menu bar or a standalone context menu. Pull-down menus
1210 are shown by the menu bar when the user clicks on the respective
1211 item or presses the specified shortcut key. Use
1212 QMenuBar::addMenu() to insert a menu into a menu bar. Context
1213 menus are usually invoked by some special keyboard key or by
1214 right-clicking. They can be executed either asynchronously with
1215 popup() or synchronously with exec(). Menus can also be invoked in
1216 response to button presses; these are just like context menus
1217 except for how they are invoked.
1221 \o \inlineimage plastique-menu.png
1222 \o \inlineimage windowsxp-menu.png
1223 \o \inlineimage macintosh-menu.png
1225 \caption Fig. A menu shown in \l{Plastique Style Widget Gallery}{Plastique widget style},
1226 \l{Windows XP Style Widget Gallery}{Windows XP widget style},
1227 and \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
1231 A menu consists of a list of action items. Actions are added with
1232 the addAction(), addActions() and insertAction() functions. An action
1233 is represented vertically and rendered by QStyle. In addition, actions
1234 can have a text label, an optional icon drawn on the very left side,
1235 and shortcut key sequence such as "Ctrl+X".
1237 The existing actions held by a menu can be found with actions().
1239 There are four kinds of action items: separators, actions that
1240 show a submenu, widgets, and actions that perform an action.
1241 Separators are inserted with addSeparator(), submenus with addMenu(),
1242 and all other items are considered action items.
1244 When inserting action items you usually specify a receiver and a
1245 slot. The receiver will be notifed whenever the item is
1246 \l{QAction::triggered()}{triggered()}. In addition, QMenu provides
1247 two signals, activated() and highlighted(), which signal the
1248 QAction that was triggered from the menu.
1250 You clear a menu with clear() and remove individual action items
1251 with removeAction().
1253 A QMenu can also provide a tear-off menu. A tear-off menu is a
1254 top-level window that contains a copy of the menu. This makes it
1255 possible for the user to "tear off" frequently used menus and
1256 position them in a convenient place on the screen. If you want
1257 this functionality for a particular menu, insert a tear-off handle
1258 with setTearOffEnabled(). When using tear-off menus, bear in mind
1259 that the concept isn't typically used on Microsoft Windows so
1260 some users may not be familiar with it. Consider using a QToolBar
1263 Widgets can be inserted into menus with the QWidgetAction class.
1264 Instances of this class are used to hold widgets, and are inserted
1265 into menus with the addAction() overload that takes a QAction.
1267 Conversely, actions can be added to widgets with the addAction(),
1268 addActions() and insertAction() functions.
1270 \warning To make QMenu visible on the screen, exec() or popup() should be
1271 used instead of show().
1273 \section1 QMenu on Qt for Windows CE
1275 If a menu is integrated into the native menubar on Windows Mobile we
1276 do not support the signals: aboutToHide (), aboutToShow () and hovered ().
1277 It is not possible to display an icon in a native menu on Windows Mobile.
1279 \section1 QMenu on Mac OS X with Qt build against Cocoa
1281 QMenu can be inserted only once in a menu/menubar. Subsequent insertions will
1282 have no effect or will result in a disabled menu item.
1284 See the \l{mainwindows/menus}{Menus} example for an example of how
1285 to use QMenuBar and QMenu in your application.
1287 \bold{Important inherited functions:} addAction(), removeAction(), clear(),
1288 addSeparator(), and addMenu().
1290 \sa QMenuBar, {fowler}{GUI Design Handbook: Menu, Drop-Down and Pop-Up},
1291 {Application Example}, {Menus Example}, {Recent Files Example}
1296 Constructs a menu with parent \a parent.
1298 Although a popup menu is always a top-level widget, if a parent is
1299 passed the popup menu will be deleted when that parent is
1300 destroyed (as with any other QObject).
1302 QMenu::QMenu(QWidget *parent)
1303 : QWidget(*new QMenuPrivate, parent, Qt::Popup)
1310 Constructs a menu with a \a title and a \a parent.
1312 Although a popup menu is always a top-level widget, if a parent is
1313 passed the popup menu will be deleted when that parent is
1314 destroyed (as with any other QObject).
1318 QMenu::QMenu(const QString &title, QWidget *parent)
1319 : QWidget(*new QMenuPrivate, parent, Qt::Popup)
1323 d->menuAction->setText(title);
1328 QMenu::QMenu(QMenuPrivate &dd, QWidget *parent)
1329 : QWidget(dd, parent, Qt::Popup)
1341 if (!d->widgetItems.isEmpty()) { // avoid detach on shared null hash
1342 QHash<QAction *, QWidget *>::iterator it = d->widgetItems.begin();
1343 for (; it != d->widgetItems.end(); ++it) {
1344 if (QWidget *widget = it.value()) {
1345 QWidgetAction *action = static_cast<QWidgetAction *>(it.key());
1346 action->releaseWidget(widget);
1353 d->eventLoop->exit();
1360 This convenience function creates a new action with \a text.
1361 The function adds the newly created action to the menu's
1362 list of actions, and returns it.
1364 \sa QWidget::addAction()
1366 QAction *QMenu::addAction(const QString &text)
1368 QAction *ret = new QAction(text, this);
1376 This convenience function creates a new action with an \a icon
1377 and some \a text. The function adds the newly created action to
1378 the menu's list of actions, and returns it.
1380 \sa QWidget::addAction()
1382 QAction *QMenu::addAction(const QIcon &icon, const QString &text)
1384 QAction *ret = new QAction(icon, text, this);
1392 This convenience function creates a new action with the text \a
1393 text and an optional shortcut \a shortcut. The action's
1394 \l{QAction::triggered()}{triggered()} signal is connected to the
1395 \a receiver's \a member slot. The function adds the newly created
1396 action to the menu's list of actions and returns it.
1398 \sa QWidget::addAction()
1400 QAction *QMenu::addAction(const QString &text, const QObject *receiver, const char* member, const QKeySequence &shortcut)
1402 QAction *action = new QAction(text, this);
1403 #ifdef QT_NO_SHORTCUT
1406 action->setShortcut(shortcut);
1408 QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
1416 This convenience function creates a new action with an \a icon and
1417 some \a text and an optional shortcut \a shortcut. The action's
1418 \l{QAction::triggered()}{triggered()} signal is connected to the
1419 \a member slot of the \a receiver object. The function adds the
1420 newly created action to the menu's list of actions, and returns it.
1422 \sa QWidget::addAction()
1424 QAction *QMenu::addAction(const QIcon &icon, const QString &text, const QObject *receiver,
1425 const char* member, const QKeySequence &shortcut)
1427 QAction *action = new QAction(icon, text, this);
1428 #ifdef QT_NO_SHORTCUT
1431 action->setShortcut(shortcut);
1433 QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
1439 This convenience function adds \a menu as a submenu to this menu.
1440 It returns \a menu's menuAction(). This menu does not take
1441 ownership of \a menu.
1443 \sa QWidget::addAction() QMenu::menuAction()
1445 QAction *QMenu::addMenu(QMenu *menu)
1447 QAction *action = menu->menuAction();
1453 Appends a new QMenu with \a title to the menu. The menu
1454 takes ownership of the menu. Returns the new menu.
1456 \sa QWidget::addAction() QMenu::menuAction()
1458 QMenu *QMenu::addMenu(const QString &title)
1460 QMenu *menu = new QMenu(title, this);
1461 addAction(menu->menuAction());
1466 Appends a new QMenu with \a icon and \a title to the menu. The menu
1467 takes ownership of the menu. Returns the new menu.
1469 \sa QWidget::addAction() QMenu::menuAction()
1471 QMenu *QMenu::addMenu(const QIcon &icon, const QString &title)
1473 QMenu *menu = new QMenu(title, this);
1474 menu->setIcon(icon);
1475 addAction(menu->menuAction());
1480 This convenience function creates a new separator action, i.e. an
1481 action with QAction::isSeparator() returning true, and adds the new
1482 action to this menu's list of actions. It returns the newly
1485 \sa QWidget::addAction()
1487 QAction *QMenu::addSeparator()
1489 QAction *action = new QAction(this);
1490 action->setSeparator(true);
1496 This convenience function inserts \a menu before action \a before
1497 and returns the menus menuAction().
1499 \sa QWidget::insertAction(), addMenu()
1501 QAction *QMenu::insertMenu(QAction *before, QMenu *menu)
1503 QAction *action = menu->menuAction();
1504 insertAction(before, action);
1509 This convenience function creates a new separator action, i.e. an
1510 action with QAction::isSeparator() returning true. The function inserts
1511 the newly created action into this menu's list of actions before
1512 action \a before and returns it.
1514 \sa QWidget::insertAction(), addSeparator()
1516 QAction *QMenu::insertSeparator(QAction *before)
1518 QAction *action = new QAction(this);
1519 action->setSeparator(true);
1520 insertAction(before, action);
1525 This sets the default action to \a act. The default action may have
1526 a visual cue, depending on the current QStyle. A default action
1527 usually indicates what will happen by default when a drop occurs.
1531 void QMenu::setDefaultAction(QAction *act)
1533 d_func()->defaultAction = act;
1537 Returns the current default action.
1539 \sa setDefaultAction()
1541 QAction *QMenu::defaultAction() const
1543 return d_func()->defaultAction;
1547 \property QMenu::tearOffEnabled
1548 \brief whether the menu supports being torn off
1550 When true, the menu contains a special tear-off item (often shown as a dashed
1551 line at the top of the menu) that creates a copy of the menu when it is
1554 This "torn-off" copy lives in a separate window. It contains the same menu
1555 items as the original menu, with the exception of the tear-off handle.
1557 By default, this property is false.
1559 void QMenu::setTearOffEnabled(bool b)
1562 if (d->tearoff == b)
1568 d->itemsDirty = true;
1573 bool QMenu::isTearOffEnabled() const
1575 return d_func()->tearoff;
1579 When a menu is torn off a second menu is shown to display the menu
1580 contents in a new window. When the menu is in this mode and the menu
1581 is visible returns true; otherwise false.
1583 \sa hideTearOffMenu() isTearOffEnabled()
1585 bool QMenu::isTearOffMenuVisible() const
1587 if (d_func()->tornPopup)
1588 return d_func()->tornPopup->isVisible();
1593 This function will forcibly hide the torn off menu making it
1594 disappear from the users desktop.
1596 \sa isTearOffMenuVisible() isTearOffEnabled()
1598 void QMenu::hideTearOffMenu()
1600 if (QWidget *w = d_func()->tornPopup)
1606 Sets the currently highlighted action to \a act.
1608 void QMenu::setActiveAction(QAction *act)
1611 d->setCurrentAction(act, 0);
1613 d->scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollCenter);
1618 Returns the currently highlighted action, or 0 if no
1619 action is currently highlighted.
1621 QAction *QMenu::activeAction() const
1623 return d_func()->currentAction;
1629 Returns true if there are no visible actions inserted into the menu, false
1632 \sa QWidget::actions()
1635 bool QMenu::isEmpty() const
1638 for(int i = 0; ret && i < actions().count(); ++i) {
1639 const QAction *action = actions().at(i);
1640 if (!action->isSeparator() && action->isVisible()) {
1648 Removes all the menu's actions. Actions owned by the menu and not
1649 shown in any other widget are deleted.
1655 QList<QAction*> acts = actions();
1657 for(int i = 0; i < acts.size(); i++) {
1658 #ifdef QT_SOFTKEYS_ENABLED
1660 // Lets not touch to our internal softkey actions
1661 if(acts[i] == d->selectAction || acts[i] == d->cancelAction)
1664 removeAction(acts[i]);
1665 if (acts[i]->parent() == this && acts[i]->d_func()->widgets.isEmpty())
1671 If a menu does not fit on the screen it lays itself out so that it
1672 does fit. It is style dependent what layout means (for example, on
1673 Windows it will use multiple columns).
1675 This functions returns the number of columns necessary.
1677 int QMenu::columnCount() const
1679 return d_func()->ncols;
1683 Returns the item at \a pt; returns 0 if there is no item there.
1685 QAction *QMenu::actionAt(const QPoint &pt) const
1687 if (QAction *ret = d_func()->actionAt(pt))
1693 Returns the geometry of action \a act.
1695 QRect QMenu::actionGeometry(QAction *act) const
1697 return d_func()->actionRect(act);
1703 QSize QMenu::sizeHint() const
1706 d->updateActionRects();
1709 for (int i = 0; i < d->actionRects.count(); ++i) {
1710 const QRect &rect = d->actionRects.at(i);
1713 if (rect.bottom() >= s.height())
1714 s.setHeight(rect.y() + rect.height());
1715 if (rect.right() >= s.width())
1716 s.setWidth(rect.x() + rect.width());
1718 // Note that the action rects calculated above already include
1719 // the top and left margins, so we only need to add margins for
1720 // the bottom and right.
1721 QStyleOption opt(0);
1723 const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, this);
1724 s.rwidth() += style()->pixelMetric(QStyle::PM_MenuHMargin, &opt, this) + fw + d->rightmargin;
1725 s.rheight() += style()->pixelMetric(QStyle::PM_MenuVMargin, &opt, this) + fw + d->bottommargin;
1727 return style()->sizeFromContents(QStyle::CT_Menu, &opt,
1728 s.expandedTo(QApplication::globalStrut()), this);
1732 Displays the menu so that the action \a atAction will be at the
1733 specified \e global position \a p. To translate a widget's local
1734 coordinates into global coordinates, use QWidget::mapToGlobal().
1736 When positioning a menu with exec() or popup(), bear in mind that
1737 you cannot rely on the menu's current size(). For performance
1738 reasons, the menu adapts its size only when necessary, so in many
1739 cases, the size before and after the show is different. Instead,
1740 use sizeHint() which calculates the proper size depending on the
1741 menu's current contents.
1743 \sa QWidget::mapToGlobal(), exec()
1745 void QMenu::popup(const QPoint &p, QAction *atAction)
1748 if (d->scroll) { // reset scroll state from last popup
1749 d->scroll->scrollOffset = 0;
1750 d->scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
1752 d->tearoffHighlighted = 0;
1754 d->doChildEffects = true;
1755 d->updateLayoutDirection();
1757 #ifndef QT_NO_MENUBAR
1758 // if this menu is part of a chain attached to a QMenuBar, set the
1759 // _NET_WM_WINDOW_TYPE_DROPDOWN_MENU X11 window type
1760 setAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu, qobject_cast<QMenuBar *>(d->topCausedWidget()) != 0);
1763 ensurePolished(); // Get the right font
1765 const bool actionListChanged = d->itemsDirty;
1766 d->updateActionRects();
1768 QPushButton *causedButton = qobject_cast<QPushButton*>(d->causedPopup.widget);
1769 if (actionListChanged && causedButton)
1770 pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition();
1774 QSize size = sizeHint();
1776 #ifndef QT_NO_GRAPHICSVIEW
1777 bool isEmbedded = !bypassGraphicsProxyWidget(this) && d->nearestGraphicsProxyWidget(this);
1779 screen = d->popupGeometry(this);
1782 screen = d->popupGeometry(QApplication::desktop()->screenNumber(p));
1783 const int desktopFrame = style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, this);
1784 bool adjustToDesktop = !window()->testAttribute(Qt::WA_DontShowOnScreen);
1786 // if the screens have very different geometries and the menu is too big, we have to recalculate
1787 if (size.height() > screen.height() || size.width() > screen.width()) {
1788 size = d->adjustMenuSizeForScreen(screen);
1789 adjustToDesktop = true;
1791 // Layout is not right, we might be able to save horizontal space
1792 if (d->ncols >1 && size.height() < screen.height()) {
1793 size = d->adjustMenuSizeForScreen(screen);
1794 adjustToDesktop = true;
1797 #ifdef QT_KEYPAD_NAVIGATION
1798 if (!atAction && QApplication::keypadNavigationEnabled()) {
1799 // Try to have one item activated
1800 if (d->defaultAction && d->defaultAction->isEnabled()) {
1801 atAction = d->defaultAction;
1802 // TODO: This works for first level menus, not yet sub menus
1804 foreach (QAction *action, d->actions)
1805 if (action->isEnabled()) {
1810 d->currentAction = atAction;
1814 pos.setY(screen.top() + desktopFrame);
1815 } else if (atAction) {
1816 for (int i = 0, above_height = 0; i < d->actions.count(); i++) {
1817 QAction *action = d->actions.at(i);
1818 if (action == atAction) {
1819 int newY = pos.y() - above_height;
1820 if (d->scroll && newY < desktopFrame) {
1821 d->scroll->scrollFlags = d->scroll->scrollFlags
1822 | QMenuPrivate::QMenuScroller::ScrollUp;
1823 d->scroll->scrollOffset = newY;
1824 newY = desktopFrame;
1828 if (d->scroll && d->scroll->scrollFlags != QMenuPrivate::QMenuScroller::ScrollNone
1829 && !style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll, 0, this)) {
1830 int below_height = above_height + d->scroll->scrollOffset;
1831 for (int i2 = i; i2 < d->actionRects.count(); i2++)
1832 below_height += d->actionRects.at(i2).height();
1833 size.setHeight(below_height);
1837 above_height += d->actionRects.at(i).height();
1842 QPoint mouse = QCursor::pos();
1843 d->mousePopupPos = mouse;
1844 const bool snapToMouse = (QRect(p.x() - 3, p.y() - 3, 6, 6).contains(mouse));
1846 if (adjustToDesktop) {
1847 // handle popup falling "off screen"
1848 if (isRightToLeft()) {
1849 if (snapToMouse) // position flowing left from the mouse
1850 pos.setX(mouse.x() - size.width());
1852 #ifndef QT_NO_MENUBAR
1853 // if in a menubar, it should be right-aligned
1854 if (qobject_cast<QMenuBar*>(d->causedPopup.widget))
1855 pos.rx() -= size.width();
1856 #endif //QT_NO_MENUBAR
1858 if (pos.x() < screen.left() + desktopFrame)
1859 pos.setX(qMax(p.x(), screen.left() + desktopFrame));
1860 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
1861 pos.setX(qMax(p.x() - size.width(), screen.right() - desktopFrame - size.width() + 1));
1863 if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
1864 pos.setX(screen.right() - desktopFrame - size.width() + 1);
1865 if (pos.x() < screen.left() + desktopFrame)
1866 pos.setX(screen.left() + desktopFrame);
1868 if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
1870 pos.setY(qMin(mouse.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
1872 pos.setY(qMax(p.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
1873 } else if (pos.y() < screen.top() + desktopFrame) {
1874 pos.setY(screen.top() + desktopFrame);
1877 if (pos.y() < screen.top() + desktopFrame)
1878 pos.setY(screen.top() + desktopFrame);
1879 if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
1881 d->scroll->scrollFlags |= uint(QMenuPrivate::QMenuScroller::ScrollDown);
1882 int y = qMax(screen.y(),pos.y());
1883 size.setHeight(screen.bottom() - (desktopFrame * 2) - y);
1885 // Too big for screen, bias to see bottom of menu (for some reason)
1886 pos.setY(screen.bottom() - size.height() + 1);
1890 const int subMenuOffset = style()->pixelMetric(QStyle::PM_SubMenuOverlap, 0, this);
1891 const QSize menuSize(sizeHint());
1892 QMenu *caused = qobject_cast<QMenu*>(d_func()->causedPopup.widget);
1893 if (caused && caused->geometry().width() + menuSize.width() + subMenuOffset < screen.width()) {
1894 QRect parentActionRect(caused->d_func()->actionRect(caused->d_func()->currentAction));
1895 const QPoint actionTopLeft = caused->mapToGlobal(parentActionRect.topLeft());
1896 parentActionRect.moveTopLeft(actionTopLeft);
1897 if (isRightToLeft()) {
1898 if ((pos.x() + menuSize.width() > parentActionRect.left() - subMenuOffset)
1899 && (pos.x() < parentActionRect.right()))
1901 pos.rx() = parentActionRect.left() - menuSize.width();
1902 if (pos.x() < screen.x())
1903 pos.rx() = parentActionRect.right();
1904 if (pos.x() + menuSize.width() > screen.x() + screen.width())
1905 pos.rx() = screen.x();
1908 if ((pos.x() < parentActionRect.right() + subMenuOffset)
1909 && (pos.x() + menuSize.width() > parentActionRect.left()))
1911 pos.rx() = parentActionRect.right();
1912 if (pos.x() + menuSize.width() > screen.x() + screen.width())
1913 pos.rx() = parentActionRect.left() - menuSize.width();
1914 if (pos.x() < screen.x())
1915 pos.rx() = screen.x() + screen.width() - menuSize.width();
1919 setGeometry(QRect(pos, size));
1920 #ifndef QT_NO_EFFECTS
1921 int hGuess = isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll;
1922 int vGuess = QEffects::DownScroll;
1923 if (isRightToLeft()) {
1924 if ((snapToMouse && (pos.x() + size.width() / 2 > mouse.x())) ||
1925 (qobject_cast<QMenu*>(d->causedPopup.widget) && pos.x() + size.width() / 2 > d->causedPopup.widget->x()))
1926 hGuess = QEffects::RightScroll;
1928 if ((snapToMouse && (pos.x() + size.width() / 2 < mouse.x())) ||
1929 (qobject_cast<QMenu*>(d->causedPopup.widget) && pos.x() + size.width() / 2 < d->causedPopup.widget->x()))
1930 hGuess = QEffects::LeftScroll;
1933 #ifndef QT_NO_MENUBAR
1934 if ((snapToMouse && (pos.y() + size.height() / 2 < mouse.y())) ||
1935 (qobject_cast<QMenuBar*>(d->causedPopup.widget) &&
1936 pos.y() + size.width() / 2 < d->causedPopup.widget->mapToGlobal(d->causedPopup.widget->pos()).y()))
1937 vGuess = QEffects::UpScroll;
1939 if (QApplication::isEffectEnabled(Qt::UI_AnimateMenu)) {
1940 bool doChildEffects = true;
1941 #ifndef QT_NO_MENUBAR
1942 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->causedPopup.widget)) {
1943 doChildEffects = mb->d_func()->doChildEffects;
1944 mb->d_func()->doChildEffects = false;
1947 if (QMenu *m = qobject_cast<QMenu*>(d->causedPopup.widget)) {
1948 doChildEffects = m->d_func()->doChildEffects;
1949 m->d_func()->doChildEffects = false;
1952 if (doChildEffects) {
1953 if (QApplication::isEffectEnabled(Qt::UI_FadeMenu))
1955 else if (d->causedPopup.widget)
1956 qScrollEffect(this, qobject_cast<QMenu*>(d->causedPopup.widget) ? hGuess : vGuess);
1958 qScrollEffect(this, hGuess | vGuess);
1960 // kill any running effect
1972 #ifndef QT_NO_ACCESSIBILITY
1973 QAccessible::updateAccessibility(QAccessibleEvent(QAccessible::PopupMenuStart, this ,0));
1978 Executes this menu synchronously.
1980 This is equivalent to \c{exec(pos())}.
1982 This returns the triggered QAction in either the popup menu or one
1983 of its submenus, or 0 if no item was triggered (normally because
1984 the user pressed Esc).
1986 In most situations you'll want to specify the position yourself,
1987 for example, the current mouse position:
1988 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 0
1989 or aligned to a widget:
1990 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 1
1991 or in reaction to a QMouseEvent *e:
1992 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 2
1994 QAction *QMenu::exec()
2003 Executes this menu synchronously.
2005 Pops up the menu so that the action \a action will be at the
2006 specified \e global position \a p. To translate a widget's local
2007 coordinates into global coordinates, use QWidget::mapToGlobal().
2009 This returns the triggered QAction in either the popup menu or one
2010 of its submenus, or 0 if no item was triggered (normally because
2011 the user pressed Esc).
2013 Note that all signals are emitted as usual. If you connect a
2014 QAction to a slot and call the menu's exec(), you get the result
2015 both via the signal-slot connection and in the return value of
2018 Common usage is to position the menu at the current mouse
2020 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 3
2021 or aligned to a widget:
2022 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 4
2023 or in reaction to a QMouseEvent *e:
2024 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 5
2026 When positioning a menu with exec() or popup(), bear in mind that
2027 you cannot rely on the menu's current size(). For performance
2028 reasons, the menu adapts its size only when necessary. So in many
2029 cases, the size before and after the show is different. Instead,
2030 use sizeHint() which calculates the proper size depending on the
2031 menu's current contents.
2033 \sa popup(), QWidget::mapToGlobal()
2035 QAction *QMenu::exec(const QPoint &p, QAction *action)
2039 QEventLoop eventLoop;
2040 d->eventLoop = &eventLoop;
2043 QPointer<QObject> guard = this;
2044 (void) eventLoop.exec();
2048 action = d->syncAction;
2057 Executes a menu synchronously.
2059 The menu's actions are specified by the list of \a actions. The menu will
2060 pop up so that the specified action, \a at, appears at global position \a
2061 pos. If \a at is not specified then the menu appears at position \a
2062 pos. \a parent is the menu's parent widget; specifying the parent will
2063 provide context when \a pos alone is not enough to decide where the menu
2064 should go (e.g., with multiple desktops or when the parent is embedded in
2067 The function returns the triggered QAction in either the popup
2068 menu or one of its submenus, or 0 if no item was triggered
2069 (normally because the user pressed Esc).
2071 This is equivalent to:
2072 \snippet doc/src/snippets/code/src_gui_widgets_qmenu.cpp 6
2074 \sa popup(), QWidget::mapToGlobal()
2076 QAction *QMenu::exec(QList<QAction*> actions, const QPoint &pos, QAction *at, QWidget *parent)
2079 menu.addActions(actions);
2080 return menu.exec(pos, at);
2086 void QMenu::hideEvent(QHideEvent *)
2091 d->eventLoop->exit();
2092 d->setCurrentAction(0);
2093 #ifndef QT_NO_ACCESSIBILITY
2094 QAccessible::updateAccessibility(QAccessibleEvent(QAccessible::PopupMenuEnd, this, 0));
2096 #ifndef QT_NO_MENUBAR
2097 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->causedPopup.widget))
2098 mb->d_func()->setCurrentAction(0);
2101 d->hasHadMouse = false;
2102 d->causedPopup.widget = 0;
2103 d->causedPopup.action = 0;
2105 d->scroll->scrollTimer.stop(); //make sure the timer stops
2111 void QMenu::paintEvent(QPaintEvent *e)
2114 d->updateActionRects();
2116 QRegion emptyArea = QRegion(rect());
2118 QStyleOptionMenuItem menuOpt;
2119 menuOpt.initFrom(this);
2120 menuOpt.state = QStyle::State_None;
2121 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2122 menuOpt.maxIconWidth = 0;
2123 menuOpt.tabWidth = 0;
2124 style()->drawPrimitive(QStyle::PE_PanelMenu, &menuOpt, &p, this);
2126 //draw the items that need updating..
2127 for (int i = 0; i < d->actions.count(); ++i) {
2128 QAction *action = d->actions.at(i);
2129 QRect adjustedActionRect = d->actionRects.at(i);
2130 if (!e->rect().intersects(adjustedActionRect)
2131 || d->widgetItems.value(action))
2133 //set the clip region to be extra safe (and adjust for the scrollers)
2134 QRegion adjustedActionReg(adjustedActionRect);
2135 emptyArea -= adjustedActionReg;
2136 p.setClipRegion(adjustedActionReg);
2138 QStyleOptionMenuItem opt;
2139 initStyleOption(&opt, action);
2140 opt.rect = adjustedActionRect;
2141 style()->drawControl(QStyle::CE_MenuItem, &opt, &p, this);
2144 const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, this);
2145 //draw the scroller regions..
2147 menuOpt.menuItemType = QStyleOptionMenuItem::Scroller;
2148 menuOpt.state |= QStyle::State_Enabled;
2149 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) {
2150 menuOpt.rect.setRect(fw, fw, width() - (fw * 2), d->scrollerHeight());
2151 emptyArea -= QRegion(menuOpt.rect);
2152 p.setClipRect(menuOpt.rect);
2153 style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this);
2155 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown) {
2156 menuOpt.rect.setRect(fw, height() - d->scrollerHeight() - fw, width() - (fw * 2),
2157 d->scrollerHeight());
2158 emptyArea -= QRegion(menuOpt.rect);
2159 menuOpt.state |= QStyle::State_DownArrow;
2160 p.setClipRect(menuOpt.rect);
2161 style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this);
2164 //paint the tear off..
2166 menuOpt.menuItemType = QStyleOptionMenuItem::TearOff;
2167 menuOpt.rect.setRect(fw, fw, width() - (fw * 2),
2168 style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this));
2169 if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2170 menuOpt.rect.translate(0, d->scrollerHeight());
2171 emptyArea -= QRegion(menuOpt.rect);
2172 p.setClipRect(menuOpt.rect);
2173 menuOpt.state = QStyle::State_None;
2174 if (d->tearoffHighlighted)
2175 menuOpt.state |= QStyle::State_Selected;
2176 style()->drawControl(QStyle::CE_MenuTearoff, &menuOpt, &p, this);
2181 borderReg += QRect(0, 0, fw, height()); //left
2182 borderReg += QRect(width()-fw, 0, fw, height()); //right
2183 borderReg += QRect(0, 0, width(), fw); //top
2184 borderReg += QRect(0, height()-fw, width(), fw); //bottom
2185 p.setClipRegion(borderReg);
2186 emptyArea -= borderReg;
2187 QStyleOptionFrame frame;
2188 frame.rect = rect();
2189 frame.palette = palette();
2190 frame.state = QStyle::State_None;
2191 frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth);
2192 frame.midLineWidth = 0;
2193 style()->drawPrimitive(QStyle::PE_FrameMenu, &frame, &p, this);
2196 //finally the rest of the space
2197 p.setClipRegion(emptyArea);
2198 menuOpt.state = QStyle::State_None;
2199 menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea;
2200 menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2201 menuOpt.rect = rect();
2202 menuOpt.menuRect = rect();
2203 style()->drawControl(QStyle::CE_MenuEmptyArea, &menuOpt, &p, this);
2206 #ifndef QT_NO_WHEELEVENT
2210 void QMenu::wheelEvent(QWheelEvent *e)
2213 if (d->scroll && rect().contains(e->pos()))
2214 d->scrollMenu(e->delta() > 0 ?
2215 QMenuPrivate::QMenuScroller::ScrollUp : QMenuPrivate::QMenuScroller::ScrollDown);
2222 void QMenu::mousePressEvent(QMouseEvent *e)
2225 if (d->aboutToHide || d->mouseEventTaken(e))
2227 if (!rect().contains(e->pos())) {
2229 && QRect(d->noReplayFor->mapToGlobal(QPoint()), d->noReplayFor->size()).contains(e->globalPos()))
2230 setAttribute(Qt::WA_NoMouseReplay);
2231 if (d->eventLoop) // synchronous operation
2233 d->hideUpToMenuBar();
2236 d->mouseDown = this;
2238 QAction *action = d->actionAt(e->pos());
2239 d->setCurrentAction(action, 20);
2246 void QMenu::mouseReleaseEvent(QMouseEvent *e)
2249 if (d->aboutToHide || d->mouseEventTaken(e))
2251 if(d->mouseDown != this) {
2258 QAction *action = d->actionAt(e->pos());
2260 if (action && action == d->currentAction) {
2261 if (!action->menu()){
2262 #if defined(Q_OS_WIN)
2263 //On Windows only context menus can be activated with the right button
2264 if (e->button() == Qt::LeftButton || d->topCausedWidget() == 0)
2266 d->activateAction(action, QAction::Trigger);
2268 } else if (d->hasMouseMoved(e->globalPos())) {
2269 d->hideUpToMenuBar();
2276 void QMenu::changeEvent(QEvent *e)
2279 if (e->type() == QEvent::StyleChange || e->type() == QEvent::FontChange ||
2280 e->type() == QEvent::LayoutDirectionChange) {
2282 setMouseTracking(style()->styleHint(QStyle::SH_Menu_MouseTracking, 0, this));
2285 if (!style()->styleHint(QStyle::SH_Menu_Scrollable, 0, this)) {
2288 } else if (!d->scroll) {
2289 d->scroll = new QMenuPrivate::QMenuScroller;
2290 d->scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
2292 } else if (e->type() == QEvent::EnabledChange) {
2293 if (d->tornPopup) // torn-off menu
2294 d->tornPopup->setEnabled(isEnabled());
2295 d->menuAction->setEnabled(isEnabled());
2296 if (d->platformMenu)
2297 d->platformMenu->setMenuEnabled(isEnabled());
2299 QWidget::changeEvent(e);
2307 QMenu::event(QEvent *e)
2310 switch (e->type()) {
2311 case QEvent::Polish:
2312 d->updateLayoutDirection();
2314 case QEvent::ShortcutOverride: {
2315 QKeyEvent *kev = static_cast<QKeyEvent*>(e);
2316 if (kev->key() == Qt::Key_Up || kev->key() == Qt::Key_Down
2317 || kev->key() == Qt::Key_Left || kev->key() == Qt::Key_Right
2318 || kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return
2319 || kev->key() == Qt::Key_Escape) {
2325 case QEvent::KeyPress: {
2326 QKeyEvent *ke = (QKeyEvent*)e;
2327 if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
2332 case QEvent::ContextMenu:
2333 if(d->menuDelayTimer.isActive()) {
2334 d->menuDelayTimer.stop();
2335 internalDelayedPopup();
2338 case QEvent::Resize: {
2339 QStyleHintReturnMask menuMask;
2340 QStyleOption option;
2341 option.initFrom(this);
2342 if (style()->styleHint(QStyle::SH_Menu_Mask, &option, this, &menuMask)) {
2343 setMask(menuMask.region);
2346 d->updateActionRects();
2350 d->updateActionRects();
2351 if (d->currentAction)
2352 d->popupAction(d->currentAction, 0, false);
2354 #ifndef QT_NO_WHATSTHIS
2355 case QEvent::QueryWhatsThis:
2356 e->setAccepted(d->whatsThis.size());
2357 if (QAction *action = d->actionAt(static_cast<QHelpEvent*>(e)->pos())) {
2358 if (action->whatsThis().size() || action->menu())
2363 #ifdef QT_SOFTKEYS_ENABLED
2364 case QEvent::LanguageChange: {
2365 d->selectAction->setText(QSoftKeyManager::standardSoftKeyText(QSoftKeyManager::SelectSoftKey));
2366 d->cancelAction->setText(QSoftKeyManager::standardSoftKeyText(QSoftKeyManager::CancelSoftKey));
2373 return QWidget::event(e);
2379 bool QMenu::focusNextPrevChild(bool next)
2382 QKeyEvent ev(QEvent::KeyPress, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier);
2390 void QMenu::keyPressEvent(QKeyEvent *e)
2393 d->updateActionRects();
2395 if (isRightToLeft()) { // in reverse mode open/close key for submenues are reversed
2396 if (key == Qt::Key_Left)
2397 key = Qt::Key_Right;
2398 else if (key == Qt::Key_Right)
2402 if (key == Qt::Key_Tab) //means down
2404 if (key == Qt::Key_Backtab) //means up
2408 bool key_consumed = false;
2411 key_consumed = true;
2413 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop, true);
2416 key_consumed = true;
2418 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom, true);
2420 case Qt::Key_PageUp:
2421 key_consumed = true;
2422 if (d->currentAction && d->scroll) {
2423 if(d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2424 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollUp, true, true);
2426 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop, true);
2429 case Qt::Key_PageDown:
2430 key_consumed = true;
2431 if (d->currentAction && d->scroll) {
2432 if(d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
2433 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollDown, true, true);
2435 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom, true);
2439 case Qt::Key_Down: {
2440 key_consumed = true;
2441 QAction *nextAction = 0;
2442 QMenuPrivate::QMenuScroller::ScrollLocation scroll_loc = QMenuPrivate::QMenuScroller::ScrollStay;
2443 if (!d->currentAction) {
2444 if(key == Qt::Key_Down) {
2445 for(int i = 0; i < d->actions.count(); ++i) {
2446 QAction *act = d->actions.at(i);
2447 if (d->actionRects.at(i).isNull())
2449 if (!act->isSeparator() &&
2450 (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
2451 || act->isEnabled())) {
2457 for(int i = d->actions.count()-1; i >= 0; --i) {
2458 QAction *act = d->actions.at(i);
2459 if (d->actionRects.at(i).isNull())
2461 if (!act->isSeparator() &&
2462 (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
2463 || act->isEnabled())) {
2470 for(int i = 0, y = 0; !nextAction && i < d->actions.count(); i++) {
2471 QAction *act = d->actions.at(i);
2472 if (act == d->currentAction) {
2473 if (key == Qt::Key_Up) {
2474 for(int next_i = i-1; true; next_i--) {
2476 if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this))
2479 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
2480 next_i = d->actionRects.count()-1;
2482 QAction *next = d->actions.at(next_i);
2483 if (next == d->currentAction)
2485 if (d->actionRects.at(next_i).isNull())
2487 if (next->isSeparator() ||
2488 (!next->isEnabled() &&
2489 !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)))
2492 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)) {
2493 int topVisible = d->scrollerHeight();
2495 topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);
2496 if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.at(next_i).height())
2497 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
2501 if (!nextAction && d->tearoff)
2502 d->tearoffHighlighted = 1;
2504 y += d->actionRects.at(i).height();
2505 for(int next_i = i+1; true; next_i++) {
2506 if (next_i == d->actionRects.count()) {
2507 if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this))
2510 scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
2513 QAction *next = d->actions.at(next_i);
2514 if (next == d->currentAction)
2516 if (d->actionRects.at(next_i).isNull())
2518 if (next->isSeparator() ||
2519 (!next->isEnabled() &&
2520 !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)))
2523 if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)) {
2524 int bottomVisible = height() - d->scrollerHeight();
2525 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2526 bottomVisible -= d->scrollerHeight();
2528 bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);
2529 if ((y + d->scroll->scrollOffset + d->actionRects.at(next_i).height()) > bottomVisible)
2530 scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
2537 y += d->actionRects.at(i).height();
2541 if (d->scroll && scroll_loc != QMenuPrivate::QMenuScroller::ScrollStay) {
2542 d->scroll->scrollTimer.stop();
2543 d->scrollMenu(nextAction, scroll_loc);
2545 d->setCurrentAction(nextAction, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
2550 if (d->currentAction && d->currentAction->isEnabled() && d->currentAction->menu()) {
2551 d->popupAction(d->currentAction, 0, true);
2552 key_consumed = true;
2556 case Qt::Key_Left: {
2557 if (d->currentAction && !d->scroll) {
2558 QAction *nextAction = 0;
2559 if (key == Qt::Key_Left) {
2560 QRect actionR = d->actionRect(d->currentAction);
2561 for(int x = actionR.left()-1; !nextAction && x >= 0; x--)
2562 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
2564 QRect actionR = d->actionRect(d->currentAction);
2565 for(int x = actionR.right()+1; !nextAction && x < width(); x++)
2566 nextAction = d->actionAt(QPoint(x, actionR.center().y()));
2569 d->setCurrentAction(nextAction, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
2570 key_consumed = true;
2573 if (!key_consumed && key == Qt::Key_Left && qobject_cast<QMenu*>(d->causedPopup.widget)) {
2574 QPointer<QWidget> caused = d->causedPopup.widget;
2578 key_consumed = true;
2586 key_consumed = true;
2587 if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, 0, this))
2590 #ifndef QT_NO_MENUBAR
2591 if (QMenuBar *mb = qobject_cast<QMenuBar*>(QApplication::focusWidget())) {
2592 mb->d_func()->setKeyboardMode(false);
2598 case Qt::Key_Escape:
2599 #ifdef QT_KEYPAD_NAVIGATION
2602 key_consumed = true;
2608 QPointer<QWidget> caused = d->causedPopup.widget;
2609 d->hideMenu(this); // hide after getting causedPopup
2610 #ifndef QT_NO_MENUBAR
2611 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
2612 mb->d_func()->setCurrentAction(d->menuAction);
2613 mb->d_func()->setKeyboardMode(true);
2620 if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem, 0, this))
2622 // for motif, fall through
2623 #ifdef QT_KEYPAD_NAVIGATION
2624 case Qt::Key_Select:
2626 case Qt::Key_Return:
2627 case Qt::Key_Enter: {
2628 if (!d->currentAction) {
2629 d->setFirstActionActive();
2630 key_consumed = true;
2636 if (d->currentAction->menu())
2637 d->popupAction(d->currentAction, 0, true);
2639 d->activateAction(d->currentAction, QAction::Trigger);
2640 key_consumed = true;
2643 #ifndef QT_NO_WHATSTHIS
2645 if (!d->currentAction || d->currentAction->whatsThis().isNull())
2647 QWhatsThis::enterWhatsThisMode();
2648 d->activateAction(d->currentAction, QAction::Trigger);
2652 key_consumed = false;
2655 if (!key_consumed) { // send to menu bar
2656 if ((!e->modifiers() || e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ShiftModifier) &&
2657 e->text().length()==1) {
2658 bool activateAction = false;
2659 QAction *nextAction = 0;
2660 if (style()->styleHint(QStyle::SH_Menu_KeyboardSearch, 0, this) && !e->modifiers()) {
2661 int best_match_count = 0;
2662 d->searchBufferTimer.start(2000, this);
2663 d->searchBuffer += e->text();
2664 for(int i = 0; i < d->actions.size(); ++i) {
2665 int match_count = 0;
2666 if (d->actionRects.at(i).isNull())
2668 QAction *act = d->actions.at(i);
2669 const QString act_text = act->text();
2670 for(int c = 0; c < d->searchBuffer.size(); ++c) {
2671 if(act_text.indexOf(d->searchBuffer.at(c), 0, Qt::CaseInsensitive) != -1)
2674 if(match_count > best_match_count) {
2675 best_match_count = match_count;
2680 #ifndef QT_NO_SHORTCUT
2683 QAction *first = 0, *currentSelected = 0, *firstAfterCurrent = 0;
2684 QChar c = e->text().at(0).toUpper();
2685 for(int i = 0; i < d->actions.size(); ++i) {
2686 if (d->actionRects.at(i).isNull())
2688 QAction *act = d->actions.at(i);
2689 QKeySequence sequence = QKeySequence::mnemonic(act->text());
2690 int key = sequence[0] & 0xffff;
2691 if (key == c.unicode()) {
2695 if (act == d->currentAction)
2696 currentSelected = act;
2697 else if (!firstAfterCurrent && currentSelected)
2698 firstAfterCurrent = act;
2701 if (clashCount == 1)
2702 activateAction = true;
2703 if (clashCount >= 1) {
2704 if (clashCount == 1 || !currentSelected || !firstAfterCurrent)
2707 nextAction = firstAfterCurrent;
2712 key_consumed = true;
2714 d->scrollMenu(nextAction, QMenuPrivate::QMenuScroller::ScrollCenter, false);
2715 d->setCurrentAction(nextAction, 20, QMenuPrivate::SelectedFromElsewhere, true);
2716 if (!nextAction->menu() && activateAction) {
2718 d->activateAction(nextAction, QAction::Trigger);
2722 if (!key_consumed) {
2723 #ifndef QT_NO_MENUBAR
2724 if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->topCausedWidget())) {
2725 QAction *oldAct = mb->d_func()->currentAction;
2726 QApplication::sendEvent(mb, e);
2727 if (mb->d_func()->currentAction != oldAct)
2728 key_consumed = true;
2734 if (key_consumed && (e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta))
2735 QApplication::beep();
2736 #endif // Q_OS_WIN32
2747 void QMenu::mouseMoveEvent(QMouseEvent *e)
2750 if (!isVisible() || d->aboutToHide || d->mouseEventTaken(e))
2753 if (d->motions == 0) // ignore first mouse move event (see enterEvent())
2755 d->hasHadMouse = d->hasHadMouse || rect().contains(e->pos());
2757 QAction *action = d->actionAt(e->pos());
2760 && (!d->currentAction
2761 || !(d->currentAction->menu() && d->currentAction->menu()->isVisible())))
2762 d->setCurrentAction(0);
2764 } else if(e->buttons()) {
2765 d->mouseDown = this;
2767 if (d->sloppyRegion.contains(e->pos())) {
2768 d->sloppyAction = action;
2769 QMenuPrivate::sloppyDelayTimer = startTimer(style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this)*6);
2770 } else if (action != d->currentAction) {
2771 d->setCurrentAction(action, style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this));
2778 void QMenu::enterEvent(QEvent *)
2780 d_func()->motions = -1; // force us to ignore the generate mouse move in mouseMoveEvent()
2786 void QMenu::leaveEvent(QEvent *)
2789 d->sloppyAction = 0;
2790 if (!d->sloppyRegion.isEmpty())
2791 d->sloppyRegion = QRegion();
2792 if (!d->activeMenu && d->currentAction)
2800 QMenu::timerEvent(QTimerEvent *e)
2803 if (d->scroll && d->scroll->scrollTimer.timerId() == e->timerId()) {
2804 d->scrollMenu((QMenuPrivate::QMenuScroller::ScrollDirection)d->scroll->scrollDirection);
2805 if (d->scroll->scrollFlags == QMenuPrivate::QMenuScroller::ScrollNone)
2806 d->scroll->scrollTimer.stop();
2807 } else if(d->menuDelayTimer.timerId() == e->timerId()) {
2808 d->menuDelayTimer.stop();
2809 internalDelayedPopup();
2810 } else if(QMenuPrivate::sloppyDelayTimer == e->timerId()) {
2811 killTimer(QMenuPrivate::sloppyDelayTimer);
2812 QMenuPrivate::sloppyDelayTimer = 0;
2813 internalSetSloppyAction();
2814 } else if(d->searchBufferTimer.timerId() == e->timerId()) {
2815 d->searchBuffer.clear();
2822 void QMenu::actionEvent(QActionEvent *e)
2826 setAttribute(Qt::WA_Resized, false);
2828 d->tornPopup->syncWithMenu(this, e);
2829 if (e->type() == QEvent::ActionAdded) {
2831 connect(e->action(), SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
2832 connect(e->action(), SIGNAL(hovered()), this, SLOT(_q_actionHovered()));
2834 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
2835 QWidget *widget = wa->requestWidget(this);
2837 d->widgetItems.insert(wa, widget);
2839 } else if (e->type() == QEvent::ActionRemoved) {
2840 e->action()->disconnect(this);
2841 if (e->action() == d->currentAction)
2842 d->currentAction = 0;
2843 if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
2844 if (QWidget *widget = d->widgetItems.value(wa))
2845 wa->releaseWidget(widget);
2847 d->widgetItems.remove(e->action());
2850 if (d->platformMenu) {
2851 if (e->type() == QEvent::ActionAdded)
2852 d->platformMenu->addAction(e->action(), e->before());
2853 else if (e->type() == QEvent::ActionRemoved)
2854 d->platformMenu->removeAction(e->action());
2855 else if (e->type() == QEvent::ActionChanged)
2856 d->platformMenu->syncAction(e->action());
2859 #if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR)
2861 d->wce_menu = new QMenuPrivate::QWceMenuPrivate;
2862 if (e->type() == QEvent::ActionAdded)
2863 d->wce_menu->addAction(e->action(), d->wce_menu->findAction(e->before()));
2864 else if (e->type() == QEvent::ActionRemoved)
2865 d->wce_menu->removeAction(e->action());
2866 else if (e->type() == QEvent::ActionChanged)
2867 d->wce_menu->syncAction(e->action());
2871 d->updateActionRects();
2880 void QMenu::internalSetSloppyAction()
2882 if (d_func()->sloppyAction)
2883 d_func()->setCurrentAction(d_func()->sloppyAction, 0);
2889 void QMenu::internalDelayedPopup()
2893 //hide the current item
2894 if (QMenu *menu = d->activeMenu) {
2899 if (!d->currentAction || !d->currentAction->isEnabled() || !d->currentAction->menu() ||
2900 !d->currentAction->menu()->isEnabled() || d->currentAction->menu()->isVisible())
2904 d->activeMenu = d->currentAction->menu();
2905 d->activeMenu->d_func()->causedPopup.widget = this;
2906 d->activeMenu->d_func()->causedPopup.action = d->currentAction;
2908 int subMenuOffset = style()->pixelMetric(QStyle::PM_SubMenuOverlap, 0, this);
2909 const QRect actionRect(d->actionRect(d->currentAction));
2910 const QSize menuSize(d->activeMenu->sizeHint());
2911 const QPoint rightPos(mapToGlobal(QPoint(actionRect.right() + subMenuOffset + 1, actionRect.top())));
2913 QPoint pos(rightPos);
2915 //calc sloppy focus buffer
2916 if (style()->styleHint(QStyle::SH_Menu_SloppySubMenus, 0, this)) {
2917 QPoint cur = QCursor::pos();
2918 if (actionRect.contains(mapFromGlobal(cur))) {
2920 pts[0] = QPoint(cur.x(), cur.y() - 2);
2921 pts[3] = QPoint(cur.x(), cur.y() + 2);
2922 if (pos.x() >= cur.x()) {
2923 pts[1] = QPoint(geometry().right(), pos.y());
2924 pts[2] = QPoint(geometry().right(), pos.y() + menuSize.height());
2926 pts[1] = QPoint(pos.x() + menuSize.width(), pos.y());
2927 pts[2] = QPoint(pos.x() + menuSize.width(), pos.y() + menuSize.height());
2930 for(int i = 0; i < 4; i++)
2931 points.setPoint(i, mapFromGlobal(pts[i]));
2932 d->sloppyRegion = QRegion(points);
2937 d->activeMenu->popup(pos);
2941 \fn void QMenu::addAction(QAction *action)
2944 Appends the action \a action to the menu's list of actions.
2946 \sa QMenuBar::addAction(), QWidget::addAction()
2950 \fn void QMenu::aboutToHide()
2953 This signal is emitted just before the menu is hidden from the user.
2955 \sa aboutToShow(), hide()
2959 \fn void QMenu::aboutToShow()
2961 This signal is emitted just before the menu is shown to the user.
2963 \sa aboutToHide(), show()
2967 \fn void QMenu::triggered(QAction *action)
2969 This signal is emitted when an action in this menu is triggered.
2971 \a action is the action that caused the signal to be emitted.
2973 Normally, you connect each menu action's \l{QAction::}{triggered()} signal
2974 to its own custom slot, but sometimes you will want to connect several
2975 actions to a single slot, for example, when you have a group of closely
2976 related actions, such as "left justify", "center", "right justify".
2978 \note This signal is emitted for the main parent menu in a hierarchy.
2979 Hence, only the parent menu needs to be connected to a slot; sub-menus need
2982 \sa hovered(), QAction::triggered()
2986 \fn void QMenu::hovered(QAction *action)
2988 This signal is emitted when a menu action is highlighted; \a action
2989 is the action that caused the signal to be emitted.
2991 Often this is used to update status information.
2993 \sa triggered(), QAction::hovered()
2999 void QMenu::setNoReplayFor(QWidget *noReplayFor)
3002 d_func()->noReplayFor = noReplayFor;
3004 Q_UNUSED(noReplayFor);
3010 QPlatformMenu *QMenu::platformMenu()
3013 return d_func()->platformMenu;
3017 \property QMenu::separatorsCollapsible
3020 \brief whether consecutive separators should be collapsed
3022 This property specifies whether consecutive separators in the menu
3023 should be visually collapsed to a single one. Separators at the
3024 beginning or the end of the menu are also hidden.
3026 By default, this property is true.
3028 bool QMenu::separatorsCollapsible() const
3031 return d->collapsibleSeparators;
3034 void QMenu::setSeparatorsCollapsible(bool collapse)
3037 if (d->collapsibleSeparators == collapse)
3040 d->collapsibleSeparators = collapse;
3043 d->updateActionRects();
3046 if (d->platformMenu)
3047 d->platformMenu->syncSeparatorsCollapsible(collapse);
3052 // for private slots
3053 #include "moc_qmenu.cpp"
3054 #include "qmenu.moc"
3056 #endif // QT_NO_MENU