Convert QPA menus to use QIcon, and enable icons.
[profile/ivi/qtbase.git] / src / widgets / widgets / qmenu.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
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.
16 **
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.
20 **
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.
28 **
29 ** Other Usage
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.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 #include "qmenu.h"
43
44 #ifndef QT_NO_MENU
45
46 #include "qdebug.h"
47 #include "qstyle.h"
48 #include "qevent.h"
49 #include "qtimer.h"
50 #include "qlayout.h"
51 #include "qpainter.h"
52 #include <qpa/qplatformtheme.h>
53 #include "qapplication.h"
54 #include "qdesktopwidget.h"
55 #ifndef QT_NO_ACCESSIBILITY
56 # include "qaccessible.h"
57 #endif
58 #ifndef QT_NO_EFFECTS
59 # include <private/qeffects_p.h>
60 #endif
61 #ifndef QT_NO_WHATSTHIS
62 # include <qwhatsthis.h>
63 #endif
64
65 #include "qmenu_p.h"
66 #include "qmenubar_p.h"
67 #include "qwidgetaction.h"
68 #include "qtoolbutton.h"
69 #include "qpushbutton.h"
70 #include <private/qpushbutton_p.h>
71 #include <private/qaction_p.h>
72 #include <private/qsoftkeymanager_p.h>
73 #include <private/qguiapplication_p.h>
74 #include <qpa/qplatformtheme.h>
75
76 QT_BEGIN_NAMESPACE
77
78 QMenu *QMenuPrivate::mouseDown = 0;
79 int QMenuPrivate::sloppyDelayTimer = 0;
80
81 /* QMenu code */
82 // internal class used for the torn off popup
83 class QTornOffMenu : public QMenu
84 {
85     Q_OBJECT
86     class QTornOffMenuPrivate : public QMenuPrivate
87     {
88         Q_DECLARE_PUBLIC(QMenu)
89     public:
90         QTornOffMenuPrivate(QMenu *p) : causedMenu(p) {
91             tornoff = 1;
92             causedPopup.widget = 0;
93             causedPopup.action = ((QTornOffMenu*)p)->d_func()->causedPopup.action;
94             causedStack = ((QTornOffMenu*)p)->d_func()->calcCausedStack();
95         }
96         QList<QPointer<QWidget> > calcCausedStack() const { return causedStack; }
97         QPointer<QMenu> causedMenu;
98         QList<QPointer<QWidget> > causedStack;
99     };
100 public:
101     QTornOffMenu(QMenu *p) : QMenu(*(new QTornOffMenuPrivate(p)))
102     {
103         Q_D(QTornOffMenu);
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));
118     }
119     void syncWithMenu(QMenu *menu, QActionEvent *act)
120     {
121         Q_D(QTornOffMenu);
122         if(menu != d->causedMenu)
123             return;
124         if (act->type() == QEvent::ActionAdded) {
125             insertAction(act->before(), act->action());
126         } else if (act->type() == QEvent::ActionRemoved)
127             removeAction(act->action());
128     }
129     void actionEvent(QActionEvent *e)
130     {
131         QMenu::actionEvent(e);
132         setFixedSize(sizeHint());
133     }
134 public slots:
135     void onTrigger(QAction *action) { d_func()->activateAction(action, QAction::Trigger, false); }
136     void onHovered(QAction *action) { d_func()->activateAction(action, QAction::Hover, false); }
137 private:
138     Q_DECLARE_PRIVATE(QTornOffMenu)
139     friend class QMenuPrivate;
140 };
141
142 void QMenuPrivate::init()
143 {
144     Q_Q(QMenu);
145 #ifndef QT_NO_WHATSTHIS
146     q->setAttribute(Qt::WA_CustomWhatsThis);
147 #endif
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;
155     }
156
157     platformMenu = QGuiApplicationPrivate::platformTheme()->createPlatformMenu();
158     if (platformMenu) {
159         QObject::connect(platformMenu, SIGNAL(aboutToShow()), q, SIGNAL(aboutToShow()));
160         QObject::connect(platformMenu, SIGNAL(aboutToHide()), q, SIGNAL(aboutToHide()));
161     }
162
163 #ifdef QT_SOFTKEYS_ENABLED
164     selectAction = QSoftKeyManager::createKeyedAction(QSoftKeyManager::SelectSoftKey, Qt::Key_Select, q);
165     cancelAction = QSoftKeyManager::createKeyedAction(QSoftKeyManager::CancelSoftKey, Qt::Key_Back, q);
166     selectAction->setPriority(QAction::HighPriority);
167     cancelAction->setPriority(QAction::HighPriority);
168     q->addAction(selectAction);
169     q->addAction(cancelAction);
170 #endif
171 }
172
173 int QMenuPrivate::scrollerHeight() const
174 {
175     Q_Q(const QMenu);
176     return qMax(QApplication::globalStrut().height(), q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q));
177 }
178
179 //Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't
180 QRect QMenuPrivate::popupGeometry(const QWidget *widget) const
181 {
182     if (QGuiApplicationPrivate::platformTheme() &&
183             QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool()) {
184         return QApplication::desktop()->screenGeometry(widget);
185     } else {
186         return QApplication::desktop()->availableGeometry(widget);
187     }
188 }
189
190 //Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't
191 QRect QMenuPrivate::popupGeometry(int screen) const
192 {
193     if (QGuiApplicationPrivate::platformTheme() &&
194             QGuiApplicationPrivate::platformTheme()->themeHint(QPlatformTheme::UseFullScreenForPopupMenu).toBool()) {
195         return QApplication::desktop()->screenGeometry(screen);
196     } else {
197         return QApplication::desktop()->availableGeometry(screen);
198     }
199 }
200
201 QList<QPointer<QWidget> > QMenuPrivate::calcCausedStack() const
202 {
203     QList<QPointer<QWidget> > ret;
204     for(QWidget *widget = causedPopup.widget; widget; ) {
205         ret.append(widget);
206         if (QTornOffMenu *qtmenu = qobject_cast<QTornOffMenu*>(widget))
207             ret += qtmenu->d_func()->causedStack;
208         if (QMenu *qmenu = qobject_cast<QMenu*>(widget))
209             widget = qmenu->d_func()->causedPopup.widget;
210         else
211             break;
212     }
213     return ret;
214 }
215
216 void QMenuPrivate::updateActionRects() const
217 {
218     Q_Q(const QMenu);
219     updateActionRects(popupGeometry(q));
220 }
221
222 void QMenuPrivate::updateActionRects(const QRect &screen) const
223 {
224     Q_Q(const QMenu);
225     if (!itemsDirty)
226         return;
227
228     q->ensurePolished();
229
230     //let's reinitialize the buffer
231     actionRects.resize(actions.count());
232     actionRects.fill(QRect());
233
234     int lastVisibleAction = getLastVisibleAction();
235
236     int max_column_width = 0,
237         dh = screen.height(),
238         y = 0;
239     QStyle *style = q->style();
240     QStyleOption opt;
241     opt.init(q);
242     const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q),
243               vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q),
244               icone = style->pixelMetric(QStyle::PM_SmallIconSize, &opt, q);
245     const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q);
246     const int deskFw = style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, &opt, q);
247     const int tearoffHeight = tearoff ? style->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q) : 0;
248
249     //for compatibility now - will have to refactor this away
250     tabWidth = 0;
251     maxIconWidth = 0;
252     hasCheckableItems = false;
253     ncols = 1;
254     sloppyAction = 0;
255
256     for (int i = 0; i < actions.count(); ++i) {
257         QAction *action = actions.at(i);
258         if (action->isSeparator() || !action->isVisible() || widgetItems.contains(action))
259             continue;
260         //..and some members
261         hasCheckableItems |= action->isCheckable();
262         QIcon is = action->icon();
263         if (!is.isNull()) {
264             maxIconWidth = qMax<uint>(maxIconWidth, icone + 4);
265         }
266     }
267
268     //calculate size
269     QFontMetrics qfm = q->fontMetrics();
270     bool previousWasSeparator = true; // this is true to allow removing the leading separators
271     for(int i = 0; i <= lastVisibleAction; i++) {
272         QAction *action = actions.at(i);
273
274         if (!action->isVisible() ||
275             (collapsibleSeparators && previousWasSeparator && action->isSeparator()))
276             continue; // we continue, this action will get an empty QRect
277
278         previousWasSeparator = action->isSeparator();
279
280         //let the style modify the above size..
281         QStyleOptionMenuItem opt;
282         q->initStyleOption(&opt, action);
283         const QFontMetrics &fm = opt.fontMetrics;
284
285         QSize sz;
286         if (QWidget *w = widgetItems.value(action)) {
287           sz = w->sizeHint().expandedTo(w->minimumSize()).expandedTo(w->minimumSizeHint()).boundedTo(w->maximumSize());
288         } else {
289             //calc what I think the size is..
290             if (action->isSeparator()) {
291                 sz = QSize(2, 2);
292             } else {
293                 QString s = action->text();
294                 int t = s.indexOf(QLatin1Char('\t'));
295                 if (t != -1) {
296                     tabWidth = qMax(int(tabWidth), qfm.width(s.mid(t+1)));
297                     s = s.left(t);
298     #ifndef QT_NO_SHORTCUT
299                 } else {
300                     QKeySequence seq = action->shortcut();
301                     if (!seq.isEmpty())
302                         tabWidth = qMax(int(tabWidth), qfm.width(seq.toString(QKeySequence::NativeText)));
303     #endif
304                 }
305                 sz.setWidth(fm.boundingRect(QRect(), Qt::TextSingleLine | Qt::TextShowMnemonic, s).width());
306                 sz.setHeight(qMax(fm.height(), qfm.height()));
307
308                 QIcon is = action->icon();
309                 if (!is.isNull()) {
310                     QSize is_sz = QSize(icone, icone);
311                     if (is_sz.height() > sz.height())
312                         sz.setHeight(is_sz.height());
313                 }
314             }
315             sz = style->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, q);
316         }
317
318
319         if (!sz.isEmpty()) {
320             max_column_width = qMax(max_column_width, sz.width());
321             //wrapping
322             if (!scroll &&
323                y+sz.height()+vmargin > dh - (deskFw * 2)) {
324                 ncols++;
325                 y = vmargin;
326             }
327             y += sz.height();
328             //update the item
329             actionRects[i] = QRect(0, 0, sz.width(), sz.height());
330         }
331     }
332
333     max_column_width += tabWidth; //finally add in the tab width
334     const int sfcMargin = style->sizeFromContents(QStyle::CT_Menu, &opt, QApplication::globalStrut(), q).width() - QApplication::globalStrut().width();
335     const int min_column_width = q->minimumWidth() - (sfcMargin + leftmargin + rightmargin + 2 * (fw + hmargin));
336     max_column_width = qMax(min_column_width, max_column_width);
337
338     //calculate position
339     const int base_y = vmargin + fw + topmargin +
340         (scroll ? scroll->scrollOffset : 0) +
341         tearoffHeight;
342     int x = hmargin + fw + leftmargin;
343     y = base_y;
344
345     for(int i = 0; i < actions.count(); i++) {
346         QRect &rect = actionRects[i];
347         if (rect.isNull())
348             continue;
349         if (!scroll &&
350            y+rect.height() > dh - deskFw * 2) {
351             x += max_column_width + hmargin;
352             y = base_y;
353         }
354         rect.translate(x, y);                        //move
355         rect.setWidth(max_column_width); //uniform width
356
357         //we need to update the widgets geometry
358         if (QWidget *widget = widgetItems.value(actions.at(i))) {
359             widget->setGeometry(rect);
360             widget->setVisible(actions.at(i)->isVisible());
361         }
362
363         y += rect.height();
364     }
365     itemsDirty = 0;
366 }
367
368 QSize QMenuPrivate::adjustMenuSizeForScreen(const QRect &screen)
369 {
370     Q_Q(QMenu);
371     QSize ret = screen.size();
372     itemsDirty = true;
373     updateActionRects(screen);
374     const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
375     ret.setWidth(actionRects.at(getLastVisibleAction()).right() + fw);
376     return ret;
377 }
378
379 int QMenuPrivate::getLastVisibleAction() const
380 {
381     //let's try to get the last visible action
382     int lastVisibleAction = actions.count() - 1;
383     for (;lastVisibleAction >= 0; --lastVisibleAction) {
384         const QAction *action = actions.at(lastVisibleAction);
385         if (action->isVisible()) {
386             //removing trailing separators
387             if (action->isSeparator() && collapsibleSeparators)
388                 continue;
389             break;
390         }
391     }
392     return lastVisibleAction;
393 }
394
395
396 QRect QMenuPrivate::actionRect(QAction *act) const
397 {
398     int index = actions.indexOf(act);
399     if (index == -1)
400         return QRect();
401
402     updateActionRects();
403
404     //we found the action
405     return actionRects.at(index);
406 }
407
408 #if defined(Q_OS_MAC)
409 static const qreal MenuFadeTimeInSec = 0.150;
410 #endif
411
412 void QMenuPrivate::hideUpToMenuBar()
413 {
414     Q_Q(QMenu);
415     bool fadeMenus = q->style()->styleHint(QStyle::SH_Menu_FadeOutOnHide);
416     if (!tornoff) {
417         QWidget *caused = causedPopup.widget;
418         hideMenu(q); //hide after getting causedPopup
419         while(caused) {
420 #ifndef QT_NO_MENUBAR
421             if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
422                 mb->d_func()->setCurrentAction(0);
423                 mb->d_func()->setKeyboardMode(false);
424                 caused = 0;
425             } else
426 #endif
427             if (QMenu *m = qobject_cast<QMenu*>(caused)) {
428                 caused = m->d_func()->causedPopup.widget;
429                 if (!m->d_func()->tornoff)
430                     hideMenu(m, fadeMenus);
431                 if (!fadeMenus) // Mac doesn't clear the action until after hidden.
432                     m->d_func()->setCurrentAction(0);
433             } else {                caused = 0;
434             }
435         }
436 #if defined(Q_WS_MAC)
437         if (fadeMenus) {
438             QEventLoop eventLoop;
439             QTimer::singleShot(int(MenuFadeTimeInSec * 1000), &eventLoop, SLOT(quit()));
440             QMacWindowFader::currentFader()->performFade();
441             eventLoop.exec();
442         }
443 #endif
444     }
445     setCurrentAction(0);
446 }
447
448 void QMenuPrivate::hideMenu(QMenu *menu, bool justRegister)
449 {
450     if (!menu)
451         return;
452 #if !defined(QT_NO_EFFECTS)
453     menu->blockSignals(true);
454     aboutToHide = true;
455     // Flash item which is about to trigger (if any).
456     if (menu->style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)
457         && currentAction && currentAction == actionAboutToTrigger
458         && menu->actions().contains(currentAction)) {
459         QEventLoop eventLoop;
460         QAction *activeAction = currentAction;
461
462         menu->setActiveAction(0);
463         QTimer::singleShot(60, &eventLoop, SLOT(quit()));
464         eventLoop.exec();
465
466         // Select and wait 20 ms.
467         menu->setActiveAction(activeAction);
468         QTimer::singleShot(20, &eventLoop, SLOT(quit()));
469         eventLoop.exec();
470     }
471
472     // Fade out.
473     if (menu->style()->styleHint(QStyle::SH_Menu_FadeOutOnHide)) {
474         // ### Qt 4.4:
475         // Should be something like: q->transitionWindow(Qt::FadeOutTransition, MenuFadeTimeInSec);
476         // Hopefully we'll integrate qt/research/windowtransitions into main before 4.4.
477         // Talk to Richard, Trenton or Bjoern.
478 #if defined(Q_WS_MAC)
479         if (justRegister) {
480             QMacWindowFader::currentFader()->setFadeDuration(MenuFadeTimeInSec);
481             QMacWindowFader::currentFader()->registerWindowToFade(menu);
482         } else {
483             macWindowFade(qt_mac_window_for(menu), MenuFadeTimeInSec);
484         }
485
486 #endif // Q_WS_MAC
487     }
488     aboutToHide = false;
489     menu->blockSignals(false);
490 #endif // QT_NO_EFFECTS
491     if (!justRegister)
492         menu->close();
493 }
494
495 void QMenuPrivate::popupAction(QAction *action, int delay, bool activateFirst)
496 {
497     Q_Q(QMenu);
498     if (action && action->isEnabled()) {
499         if (!delay)
500             q->internalDelayedPopup();
501         else if (!menuDelayTimer.isActive() && (!action->menu() || !action->menu()->isVisible()))
502             menuDelayTimer.start(delay, q);
503         if (activateFirst && action->menu())
504             action->menu()->d_func()->setFirstActionActive();
505     } else if (QMenu *menu = activeMenu) {  //hide the current item
506         activeMenu = 0;
507         hideMenu(menu);
508     }
509 }
510
511 void QMenuPrivate::setSyncAction()
512 {
513     Q_Q(QMenu);
514     QAction *current = currentAction;
515     if(current && (!current->isEnabled() || current->menu() || current->isSeparator()))
516         current = 0;
517     for(QWidget *caused = q; caused;) {
518         if (QMenu *m = qobject_cast<QMenu*>(caused)) {
519             caused = m->d_func()->causedPopup.widget;
520             if (m->d_func()->eventLoop)
521                 m->d_func()->syncAction = current; // synchronous operation
522         } else {
523             break;
524         }
525     }
526 }
527
528
529 void QMenuPrivate::setFirstActionActive()
530 {
531     Q_Q(QMenu);
532     updateActionRects();
533     for(int i = 0, saccum = 0; i < actions.count(); i++) {
534         const QRect &rect = actionRects.at(i);
535         if (rect.isNull())
536             continue;
537         if (scroll && scroll->scrollFlags & QMenuScroller::ScrollUp) {
538             saccum -= rect.height();
539             if (saccum > scroll->scrollOffset - scrollerHeight())
540                 continue;
541         }
542         QAction *act = actions.at(i);
543         if (!act->isSeparator() &&
544            (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
545             || act->isEnabled())) {
546             setCurrentAction(act);
547             break;
548         }
549     }
550 }
551
552 // popup == -1 means do not popup, 0 means immediately, others mean use a timer
553 void QMenuPrivate::setCurrentAction(QAction *action, int popup, SelectionReason reason, bool activateFirst)
554 {
555     Q_Q(QMenu);
556     tearoffHighlighted = 0;
557     if (currentAction)
558         q->update(actionRect(currentAction));
559
560     sloppyAction = 0;
561     if (!sloppyRegion.isEmpty())
562         sloppyRegion = QRegion();
563     QMenu *hideActiveMenu = activeMenu;
564 #ifndef QT_NO_STATUSTIP
565     QAction *previousAction = currentAction;
566 #endif
567
568     currentAction = action;
569     if (action) {
570         if (!action->isSeparator()) {
571             activateAction(action, QAction::Hover);
572             if (popup != -1) {
573                 hideActiveMenu = 0; //will be done "later"
574                 // if the menu is visible then activate the required action,
575                 // otherwise we just mark the action as currentAction
576                 // and activate it when the menu will be popuped.
577                 if (q->isVisible())
578                     popupAction(currentAction, popup, activateFirst);
579             }
580             q->update(actionRect(action));
581
582             if (reason == SelectedFromKeyboard) {
583                 QWidget *widget = widgetItems.value(action);
584                 if (widget) {
585                     if (widget->focusPolicy() != Qt::NoFocus)
586                         widget->setFocus(Qt::TabFocusReason);
587                 } else {
588                     //when the action has no QWidget, the QMenu itself should
589                     // get the focus
590                     // Since the menu is a pop-up, it uses the popup reason.
591                     if (!q->hasFocus()) {
592                         q->setFocus(Qt::PopupFocusReason);
593                     }
594                 }
595             }
596         } else { //action is a separator
597             if (popup != -1)
598                 hideActiveMenu = 0; //will be done "later"
599         }
600 #ifndef QT_NO_STATUSTIP
601     }  else if (previousAction) {
602         previousAction->d_func()->showStatusText(topCausedWidget(), QString());
603 #endif
604     }
605     if (hideActiveMenu) {
606         activeMenu = 0;
607 #ifndef QT_NO_EFFECTS
608         // kill any running effect
609         qFadeEffect(0);
610         qScrollEffect(0);
611 #endif
612         hideMenu(hideActiveMenu);
613     }
614 }
615
616 //return the top causedPopup.widget that is not a QMenu
617 QWidget *QMenuPrivate::topCausedWidget() const
618 {
619     QWidget* top = causedPopup.widget;
620     while (QMenu* m = qobject_cast<QMenu *>(top))
621         top = m->d_func()->causedPopup.widget;
622     return top;
623 }
624
625 QAction *QMenuPrivate::actionAt(QPoint p) const
626 {
627     if (!q_func()->rect().contains(p))     //sanity check
628        return 0;
629
630     for(int i = 0; i < actionRects.count(); i++) {
631         if (actionRects.at(i).contains(p))
632             return actions.at(i);
633     }
634     return 0;
635 }
636
637 void QMenuPrivate::setOverrideMenuAction(QAction *a)
638 {
639     Q_Q(QMenu);
640     QObject::disconnect(menuAction, SIGNAL(destroyed()), q, SLOT(_q_overrideMenuActionDestroyed()));
641     if (a) {
642         menuAction = a;
643         QObject::connect(a, SIGNAL(destroyed()), q, SLOT(_q_overrideMenuActionDestroyed()));
644     } else { //we revert back to the default action created by the QMenu itself
645         menuAction = defaultMenuAction;
646     }
647 }
648
649 void QMenuPrivate::_q_overrideMenuActionDestroyed()
650 {
651     menuAction=defaultMenuAction;
652 }
653
654
655 void QMenuPrivate::updateLayoutDirection()
656 {
657     Q_Q(QMenu);
658     //we need to mimic the cause of the popup's layout direction
659     //to allow setting it on a mainwindow for example
660     //we call setLayoutDirection_helper to not overwrite a user-defined value
661     if (!q->testAttribute(Qt::WA_SetLayoutDirection)) {
662         if (QWidget *w = causedPopup.widget)
663             setLayoutDirection_helper(w->layoutDirection());
664         else if (QWidget *w = q->parentWidget())
665             setLayoutDirection_helper(w->layoutDirection());
666         else
667             setLayoutDirection_helper(QApplication::layoutDirection());
668     }
669 }
670
671
672 /*!
673     Returns the action associated with this menu.
674 */
675 QAction *QMenu::menuAction() const
676 {
677     return d_func()->menuAction;
678 }
679
680 /*!
681   \property QMenu::title
682   \brief The title of the menu
683
684   This is equivalent to the QAction::text property of the menuAction().
685
686   By default, this property contains an empty string.
687 */
688 QString QMenu::title() const
689 {
690     return d_func()->menuAction->text();
691 }
692
693 void QMenu::setTitle(const QString &text)
694 {
695     d_func()->menuAction->setText(text);
696 }
697
698 /*!
699   \property QMenu::icon
700
701   \brief The icon of the menu
702
703   This is equivalent to the QAction::icon property of the menuAction().
704
705   By default, if no icon is explicitly set, this property contains a null icon.
706 */
707 QIcon QMenu::icon() const
708 {
709     return d_func()->menuAction->icon();
710 }
711
712 void QMenu::setIcon(const QIcon &icon)
713 {
714     d_func()->menuAction->setIcon(icon);
715 }
716
717
718 //actually performs the scrolling
719 void QMenuPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation location, bool active)
720 {
721     Q_Q(QMenu);
722     if (!scroll || !scroll->scrollFlags)
723         return;
724     updateActionRects();
725     int newOffset = 0;
726     const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp)   ? scrollerHeight() : 0;
727     const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollerHeight() : 0;
728     const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
729     const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
730
731     if (location == QMenuScroller::ScrollTop) {
732         for(int i = 0, saccum = 0; i < actions.count(); i++) {
733             if (actions.at(i) == action) {
734                 newOffset = topScroll - saccum;
735                 break;
736             }
737             saccum += actionRects.at(i).height();
738         }
739     } else {
740         for(int i = 0, saccum = 0; i < actions.count(); i++) {
741             saccum += actionRects.at(i).height();
742             if (actions.at(i) == action) {
743                 if (location == QMenuScroller::ScrollCenter)
744                     newOffset = ((q->height() / 2) - botScroll) - (saccum - topScroll);
745                 else
746                     newOffset = (q->height() - botScroll) - saccum;
747                 break;
748             }
749         }
750         if(newOffset)
751             newOffset -= fw * 2;
752     }
753
754     //figure out which scroll flags
755     uint newScrollFlags = QMenuScroller::ScrollNone;
756     if (newOffset < 0) //easy and cheap one
757         newScrollFlags |= QMenuScroller::ScrollUp;
758     int saccum = newOffset;
759     for(int i = 0; i < actionRects.count(); i++) {
760         saccum += actionRects.at(i).height();
761         if (saccum > q->height()) {
762             newScrollFlags |= QMenuScroller::ScrollDown;
763             break;
764         }
765     }
766
767     if (!(newScrollFlags & QMenuScroller::ScrollDown) && (scroll->scrollFlags & QMenuScroller::ScrollDown)) {
768         newOffset = q->height() - (saccum - newOffset) - fw*2 - vmargin;    //last item at bottom
769     }
770
771     if (!(newScrollFlags & QMenuScroller::ScrollUp) && (scroll->scrollFlags & QMenuScroller::ScrollUp)) {
772         newOffset = 0;  //first item at top
773     }
774
775     if (newScrollFlags & QMenuScroller::ScrollUp)
776         newOffset -= vmargin;
777
778     QRect screen = popupGeometry(q);
779     const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q);
780     if (q->height() < screen.height()-(desktopFrame*2)-1) {
781         QRect geom = q->geometry();
782         if (newOffset > scroll->scrollOffset && (scroll->scrollFlags & newScrollFlags & QMenuScroller::ScrollUp)) { //scroll up
783             const int newHeight = geom.height()-(newOffset-scroll->scrollOffset);
784             if(newHeight > geom.height())
785                 geom.setHeight(newHeight);
786         } else if(scroll->scrollFlags & newScrollFlags & QMenuScroller::ScrollDown) {
787             int newTop = geom.top() + (newOffset-scroll->scrollOffset);
788             if (newTop < desktopFrame+screen.top())
789                 newTop = desktopFrame+screen.top();
790             if (newTop < geom.top()) {
791                 geom.setTop(newTop);
792                 newOffset = 0;
793                 newScrollFlags &= ~QMenuScroller::ScrollUp;
794             }
795         }
796         if (geom.bottom() > screen.bottom() - desktopFrame)
797             geom.setBottom(screen.bottom() - desktopFrame);
798         if (geom.top() < desktopFrame+screen.top())
799             geom.setTop(desktopFrame+screen.top());
800         if (geom != q->geometry()) {
801 #if 0
802             if (newScrollFlags & QMenuScroller::ScrollDown &&
803                q->geometry().top() - geom.top() >= -newOffset)
804                 newScrollFlags &= ~QMenuScroller::ScrollDown;
805 #endif
806             q->setGeometry(geom);
807         }
808     }
809
810     //actually update flags
811     const int delta = qMin(0, newOffset) - scroll->scrollOffset; //make sure the new offset is always negative
812     if (!itemsDirty && delta) {
813         //we've scrolled so we need to update the action rects
814         for (int i = 0; i < actionRects.count(); ++i) {
815             QRect &current = actionRects[i];
816             current.moveTop(current.top() + delta);
817
818             //we need to update the widgets geometry
819             if (QWidget *w = widgetItems.value(actions.at(i)))
820                 w->setGeometry(current);
821         }
822     }
823     scroll->scrollOffset += delta;
824     scroll->scrollFlags = newScrollFlags;
825     if (active)
826         setCurrentAction(action);
827
828     q->update();     //issue an update so we see all the new state..
829 }
830
831 void QMenuPrivate::scrollMenu(QMenuScroller::ScrollLocation location, bool active)
832 {
833     Q_Q(QMenu);
834     updateActionRects();
835     if(location == QMenuScroller::ScrollBottom) {
836         for(int i = actions.size()-1; i >= 0; --i) {
837             QAction *act = actions.at(i);
838             if (actionRects.at(i).isNull())
839                 continue;
840             if (!act->isSeparator() &&
841                 (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
842                  || act->isEnabled())) {
843                 if(scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
844                     scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollBottom, active);
845                 else if(active)
846                     setCurrentAction(act, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
847                 break;
848             }
849         }
850     } else if(location == QMenuScroller::ScrollTop) {
851         for(int i = 0; i < actions.size(); ++i) {
852             QAction *act = actions.at(i);
853             if (actionRects.at(i).isNull())
854                 continue;
855             if (!act->isSeparator() &&
856                 (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
857                  || act->isEnabled())) {
858                 if(scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
859                     scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollTop, active);
860                 else if(active)
861                     setCurrentAction(act, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
862                 break;
863             }
864         }
865     }
866 }
867
868 //only directional
869 void QMenuPrivate::scrollMenu(QMenuScroller::ScrollDirection direction, bool page, bool active)
870 {
871     Q_Q(QMenu);
872     if (!scroll || !(scroll->scrollFlags & direction)) //not really possible...
873         return;
874     updateActionRects();
875     const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp)   ? scrollerHeight() : 0;
876     const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollerHeight() : 0;
877     const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
878     const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
879     const int offset = topScroll ? topScroll-vmargin : 0;
880     if (direction == QMenuScroller::ScrollUp) {
881         for(int i = 0, saccum = 0; i < actions.count(); i++) {
882             saccum -= actionRects.at(i).height();
883             if (saccum <= scroll->scrollOffset-offset) {
884                 scrollMenu(actions.at(i), page ? QMenuScroller::ScrollBottom : QMenuScroller::ScrollTop, active);
885                 break;
886             }
887         }
888     } else if (direction == QMenuScroller::ScrollDown) {
889         bool scrolled = false;
890         for(int i = 0, saccum = 0; i < actions.count(); i++) {
891             const int iHeight = actionRects.at(i).height();
892             saccum -= iHeight;
893             if (saccum <= scroll->scrollOffset-offset) {
894                 const int scrollerArea = q->height() - botScroll - fw*2;
895                 int visible = (scroll->scrollOffset-offset) - saccum;
896                 for(i++ ; i < actions.count(); i++) {
897                     visible += actionRects.at(i).height();
898                     if (visible > scrollerArea - topScroll) {
899                         scrolled = true;
900                         scrollMenu(actions.at(i), page ? QMenuScroller::ScrollTop : QMenuScroller::ScrollBottom, active);
901                         break;
902                     }
903                 }
904                 break;
905             }
906         }
907         if(!scrolled) {
908             scroll->scrollFlags &= ~QMenuScroller::ScrollDown;
909             q->update();
910         }
911     }
912 }
913
914 /* This is poor-mans eventfilters. This avoids the use of
915    eventFilter (which can be nasty for users of QMenuBar's). */
916 bool QMenuPrivate::mouseEventTaken(QMouseEvent *e)
917 {
918     Q_Q(QMenu);
919     QPoint pos = q->mapFromGlobal(e->globalPos());
920     if (scroll && !activeMenu) { //let the scroller "steal" the event
921         bool isScroll = false;
922         if (pos.x() >= 0 && pos.x() < q->width()) {
923             for(int dir = QMenuScroller::ScrollUp; dir <= QMenuScroller::ScrollDown; dir = dir << 1) {
924                 if (scroll->scrollFlags & dir) {
925                     if (dir == QMenuScroller::ScrollUp)
926                         isScroll = (pos.y() <= scrollerHeight());
927                     else if (dir == QMenuScroller::ScrollDown)
928                         isScroll = (pos.y() >= q->height() - scrollerHeight());
929                     if (isScroll) {
930                         scroll->scrollDirection = dir;
931                         break;
932                     }
933                 }
934             }
935         }
936         if (isScroll) {
937             scroll->scrollTimer.start(50, q);
938             return true;
939         } else {
940             scroll->scrollTimer.stop();
941         }
942     }
943
944     if (tearoff) { //let the tear off thingie "steal" the event..
945         QRect tearRect(0, 0, q->width(), q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q));
946         if (scroll && scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
947             tearRect.translate(0, scrollerHeight());
948         q->update(tearRect);
949         if (tearRect.contains(pos) && hasMouseMoved(e->globalPos())) {
950             setCurrentAction(0);
951             tearoffHighlighted = 1;
952             if (e->type() == QEvent::MouseButtonRelease) {
953                 if (!tornPopup)
954                     tornPopup = new QTornOffMenu(q);
955                 tornPopup->setGeometry(q->geometry());
956                 tornPopup->show();
957                 hideUpToMenuBar();
958             }
959             return true;
960         }
961         tearoffHighlighted = 0;
962     }
963
964     if (q->frameGeometry().contains(e->globalPos())) //otherwise if the event is in our rect we want it..
965         return false;
966
967     for(QWidget *caused = causedPopup.widget; caused;) {
968         bool passOnEvent = false;
969         QWidget *next_widget = 0;
970         QPoint cpos = caused->mapFromGlobal(e->globalPos());
971 #ifndef QT_NO_MENUBAR
972         if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
973             passOnEvent = mb->rect().contains(cpos);
974         } else
975 #endif
976         if (QMenu *m = qobject_cast<QMenu*>(caused)) {
977             passOnEvent = m->rect().contains(cpos);
978             next_widget = m->d_func()->causedPopup.widget;
979         }
980         if (passOnEvent) {
981             if(e->type() != QEvent::MouseButtonRelease || mouseDown == caused) {
982             QMouseEvent new_e(e->type(), cpos, caused->mapTo(caused->topLevelWidget(), cpos), e->screenPos(),
983                               e->button(), e->buttons(), e->modifiers());
984             QApplication::sendEvent(caused, &new_e);
985             return true;
986         }
987         }
988         if (!next_widget)
989             break;
990         caused = next_widget;
991     }
992     return false;
993 }
994
995 void QMenuPrivate::activateCausedStack(const QList<QPointer<QWidget> > &causedStack, QAction *action, QAction::ActionEvent action_e, bool self)
996 {
997     QBoolBlocker guard(activationRecursionGuard);
998     if(self)
999         action->activate(action_e);
1000
1001     for(int i = 0; i < causedStack.size(); ++i) {
1002         QPointer<QWidget> widget = causedStack.at(i);
1003         if (!widget)
1004             continue;
1005         //fire
1006         if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
1007             widget = qmenu->d_func()->causedPopup.widget;
1008             if (action_e == QAction::Trigger) {
1009                 emit qmenu->triggered(action);
1010            } else if (action_e == QAction::Hover) {
1011                 emit qmenu->hovered(action);
1012             }
1013 #ifndef QT_NO_MENUBAR
1014         } else if (QMenuBar *qmenubar = qobject_cast<QMenuBar*>(widget)) {
1015             if (action_e == QAction::Trigger) {
1016                 emit qmenubar->triggered(action);
1017             } else if (action_e == QAction::Hover) {
1018                 emit qmenubar->hovered(action);
1019             }
1020             break; //nothing more..
1021 #endif
1022         }
1023     }
1024 }
1025
1026 void QMenuPrivate::activateAction(QAction *action, QAction::ActionEvent action_e, bool self)
1027 {
1028     Q_Q(QMenu);
1029 #ifndef QT_NO_WHATSTHIS
1030     bool inWhatsThisMode = QWhatsThis::inWhatsThisMode();
1031 #endif
1032     if (!action || !q->isEnabled()
1033         || (action_e == QAction::Trigger
1034 #ifndef QT_NO_WHATSTHIS
1035             && !inWhatsThisMode
1036 #endif
1037             && (action->isSeparator() ||!action->isEnabled())))
1038         return;
1039
1040     /* I have to save the caused stack here because it will be undone after popup execution (ie in the hide).
1041        Then I iterate over the list to actually send the events. --Sam
1042     */
1043     const QList<QPointer<QWidget> > causedStack = calcCausedStack();
1044     if (action_e == QAction::Trigger) {
1045 #ifndef QT_NO_WHATSTHIS
1046         if (!inWhatsThisMode)
1047             actionAboutToTrigger = action;
1048 #endif
1049
1050         if (q->testAttribute(Qt::WA_DontShowOnScreen)) {
1051             hideUpToMenuBar();
1052         } else {
1053             for(QWidget *widget = QApplication::activePopupWidget(); widget; ) {
1054                 if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
1055                     if(qmenu == q)
1056                         hideUpToMenuBar();
1057                     widget = qmenu->d_func()->causedPopup.widget;
1058                 } else {
1059                     break;
1060                 }
1061             }
1062         }
1063
1064 #ifndef QT_NO_WHATSTHIS
1065         if (inWhatsThisMode) {
1066             QString s = action->whatsThis();
1067             if (s.isEmpty())
1068                 s = whatsThis;
1069             QWhatsThis::showText(q->mapToGlobal(actionRect(action).center()), s, q);
1070             return;
1071         }
1072 #endif
1073     }
1074
1075
1076     activateCausedStack(causedStack, action, action_e, self);
1077
1078
1079     if (action_e == QAction::Hover) {
1080 #ifndef QT_NO_ACCESSIBILITY
1081         if (QAccessible::isActive()) {
1082             int actionIndex = indexOf(action);
1083             QAccessibleEvent focusEvent(q, QAccessible::Focus);
1084             focusEvent.setChild(actionIndex);
1085             QAccessible::updateAccessibility(&focusEvent);
1086             QAccessibleEvent selectionEvent(q, QAccessible::Selection);
1087             focusEvent.setChild(actionIndex);
1088             QAccessible::updateAccessibility(&selectionEvent);
1089         }
1090 #endif
1091         action->showStatusText(topCausedWidget());
1092     } else {
1093         actionAboutToTrigger = 0;
1094     }
1095 }
1096
1097 void QMenuPrivate::_q_actionTriggered()
1098 {
1099     Q_Q(QMenu);
1100     if (QAction *action = qobject_cast<QAction *>(q->sender())) {
1101         QPointer<QAction> actionGuard = action;
1102         emit q->triggered(action);
1103         if (!activationRecursionGuard && actionGuard) {
1104             //in case the action has not been activated by the mouse
1105             //we check the parent hierarchy
1106             QList< QPointer<QWidget> > list;
1107             for(QWidget *widget = q->parentWidget(); widget; ) {
1108                 if (qobject_cast<QMenu*>(widget)
1109 #ifndef QT_NO_MENUBAR
1110                     || qobject_cast<QMenuBar*>(widget)
1111 #endif
1112                     ) {
1113                     list.append(widget);
1114                     widget = widget->parentWidget();
1115                 } else {
1116                     break;
1117                 }
1118             }
1119             activateCausedStack(list, action, QAction::Trigger, false);
1120         }
1121     }
1122 }
1123
1124 void QMenuPrivate::_q_actionHovered()
1125 {
1126     Q_Q(QMenu);
1127     if (QAction * action = qobject_cast<QAction *>(q->sender())) {
1128         emit q->hovered(action);
1129     }
1130 }
1131
1132 bool QMenuPrivate::hasMouseMoved(const QPoint &globalPos)
1133 {
1134     //determines if the mouse has moved (ie its initial position has
1135     //changed by more than QApplication::startDragDistance()
1136     //or if there were at least 6 mouse motions)
1137     return motions > 6 ||
1138         QApplication::startDragDistance() < (mousePopupPos - globalPos).manhattanLength();
1139 }
1140
1141
1142 /*!
1143     Initialize \a option with the values from this menu and information from \a action. This method
1144     is useful for subclasses when they need a QStyleOptionMenuItem, but don't want
1145     to fill in all the information themselves.
1146
1147     \sa QStyleOption::initFrom(), QMenuBar::initStyleOption()
1148 */
1149 void QMenu::initStyleOption(QStyleOptionMenuItem *option, const QAction *action) const
1150 {
1151     if (!option || !action)
1152         return;
1153
1154     Q_D(const QMenu);
1155     option->initFrom(this);
1156     option->palette = palette();
1157     option->state = QStyle::State_None;
1158
1159     if (window()->isActiveWindow())
1160         option->state |= QStyle::State_Active;
1161     if (isEnabled() && action->isEnabled()
1162             && (!action->menu() || action->menu()->isEnabled()))
1163         option->state |= QStyle::State_Enabled;
1164     else
1165         option->palette.setCurrentColorGroup(QPalette::Disabled);
1166
1167     option->font = action->font().resolve(font());
1168     option->fontMetrics = QFontMetrics(option->font);
1169
1170     if (d->currentAction && d->currentAction == action && !d->currentAction->isSeparator()) {
1171         option->state |= QStyle::State_Selected
1172                      | (d->mouseDown ? QStyle::State_Sunken : QStyle::State_None);
1173     }
1174
1175     option->menuHasCheckableItems = d->hasCheckableItems;
1176     if (!action->isCheckable()) {
1177         option->checkType = QStyleOptionMenuItem::NotCheckable;
1178     } else {
1179         option->checkType = (action->actionGroup() && action->actionGroup()->isExclusive())
1180                             ? QStyleOptionMenuItem::Exclusive : QStyleOptionMenuItem::NonExclusive;
1181         option->checked = action->isChecked();
1182     }
1183     if (action->menu())
1184         option->menuItemType = QStyleOptionMenuItem::SubMenu;
1185     else if (action->isSeparator())
1186         option->menuItemType = QStyleOptionMenuItem::Separator;
1187     else if (d->defaultAction == action)
1188         option->menuItemType = QStyleOptionMenuItem::DefaultItem;
1189     else
1190         option->menuItemType = QStyleOptionMenuItem::Normal;
1191     if (action->isIconVisibleInMenu())
1192         option->icon = action->icon();
1193     QString textAndAccel = action->text();
1194 #ifndef QT_NO_SHORTCUT
1195     if (textAndAccel.indexOf(QLatin1Char('\t')) == -1) {
1196         QKeySequence seq = action->shortcut();
1197         if (!seq.isEmpty())
1198             textAndAccel += QLatin1Char('\t') + seq.toString(QKeySequence::NativeText);
1199     }
1200 #endif
1201     option->text = textAndAccel;
1202     option->tabWidth = d->tabWidth;
1203     option->maxIconWidth = d->maxIconWidth;
1204     option->menuRect = rect();
1205 }
1206
1207 /*!
1208     \class QMenu
1209     \brief The QMenu class provides a menu widget for use in menu
1210     bars, context menus, and other popup menus.
1211
1212     \ingroup mainwindow-classes
1213     \ingroup basicwidgets
1214     \inmodule QtWidgets
1215
1216     A menu widget is a selection menu. It can be either a pull-down
1217     menu in a menu bar or a standalone context menu. Pull-down menus
1218     are shown by the menu bar when the user clicks on the respective
1219     item or presses the specified shortcut key. Use
1220     QMenuBar::addMenu() to insert a menu into a menu bar. Context
1221     menus are usually invoked by some special keyboard key or by
1222     right-clicking. They can be executed either asynchronously with
1223     popup() or synchronously with exec(). Menus can also be invoked in
1224     response to button presses; these are just like context menus
1225     except for how they are invoked.
1226
1227     \table 100%
1228     \row
1229     \li \inlineimage plastique-menu.png
1230     \li \inlineimage windowsxp-menu.png
1231     \li \inlineimage macintosh-menu.png
1232     \endtable
1233     \caption Fig. A menu shown in \l{Plastique Style Widget Gallery}{Plastique widget style},
1234            \l{Windows XP Style Widget Gallery}{Windows XP widget style},
1235            and \l{Macintosh Style Widget Gallery}{Macintosh widget style}.
1236
1237     \section1 Actions
1238
1239     A menu consists of a list of action items. Actions are added with
1240     the addAction(), addActions() and insertAction() functions. An action
1241     is represented vertically and rendered by QStyle. In addition, actions
1242     can have a text label, an optional icon drawn on the very left side,
1243     and shortcut key sequence such as "Ctrl+X".
1244
1245     The existing actions held by a menu can be found with actions().
1246
1247     There are four kinds of action items: separators, actions that
1248     show a submenu, widgets, and actions that perform an action.
1249     Separators are inserted with addSeparator(), submenus with addMenu(),
1250     and all other items are considered action items.
1251
1252     When inserting action items you usually specify a receiver and a
1253     slot. The receiver will be notifed whenever the item is
1254     \l{QAction::triggered()}{triggered()}. In addition, QMenu provides
1255     two signals, activated() and highlighted(), which signal the
1256     QAction that was triggered from the menu.
1257
1258     You clear a menu with clear() and remove individual action items
1259     with removeAction().
1260
1261     A QMenu can also provide a tear-off menu. A tear-off menu is a
1262     top-level window that contains a copy of the menu. This makes it
1263     possible for the user to "tear off" frequently used menus and
1264     position them in a convenient place on the screen. If you want
1265     this functionality for a particular menu, insert a tear-off handle
1266     with setTearOffEnabled(). When using tear-off menus, bear in mind
1267     that the concept isn't typically used on Microsoft Windows so
1268     some users may not be familiar with it. Consider using a QToolBar
1269     instead.
1270
1271     Widgets can be inserted into menus with the QWidgetAction class.
1272     Instances of this class are used to hold widgets, and are inserted
1273     into menus with the addAction() overload that takes a QAction.
1274
1275     Conversely, actions can be added to widgets with the addAction(),
1276     addActions() and insertAction() functions.
1277
1278     \warning To make QMenu visible on the screen, exec() or popup() should be
1279     used instead of show().
1280
1281     \section1 QMenu on Qt for Windows CE
1282
1283     If a menu is integrated into the native menubar on Windows Mobile we
1284     do not support the signals: aboutToHide (), aboutToShow () and hovered ().
1285     It is not possible to display an icon in a native menu on Windows Mobile.
1286
1287     \section1 QMenu on Mac OS X with Qt build against Cocoa
1288
1289     QMenu can be inserted only once in a menu/menubar. Subsequent insertions will
1290     have no effect or will result in a disabled menu item.
1291
1292     See the \l{mainwindows/menus}{Menus} example for an example of how
1293     to use QMenuBar and QMenu in your application.
1294
1295     \b{Important inherited functions:} addAction(), removeAction(), clear(),
1296     addSeparator(), and addMenu().
1297
1298     \sa QMenuBar, {fowler}{GUI Design Handbook: Menu, Drop-Down and Pop-Up},
1299         {Application Example}, {Menus Example}, {Recent Files Example}
1300 */
1301
1302
1303 /*!
1304     Constructs a menu with parent \a parent.
1305
1306     Although a popup menu is always a top-level widget, if a parent is
1307     passed the popup menu will be deleted when that parent is
1308     destroyed (as with any other QObject).
1309 */
1310 QMenu::QMenu(QWidget *parent)
1311     : QWidget(*new QMenuPrivate, parent, Qt::Popup)
1312 {
1313     Q_D(QMenu);
1314     d->init();
1315 }
1316
1317 /*!
1318     Constructs a menu with a \a title and a \a parent.
1319
1320     Although a popup menu is always a top-level widget, if a parent is
1321     passed the popup menu will be deleted when that parent is
1322     destroyed (as with any other QObject).
1323
1324     \sa title
1325 */
1326 QMenu::QMenu(const QString &title, QWidget *parent)
1327     : QWidget(*new QMenuPrivate, parent, Qt::Popup)
1328 {
1329     Q_D(QMenu);
1330     d->init();
1331     d->menuAction->setText(title);
1332 }
1333
1334 /*! \internal
1335  */
1336 QMenu::QMenu(QMenuPrivate &dd, QWidget *parent)
1337     : QWidget(dd, parent, Qt::Popup)
1338 {
1339     Q_D(QMenu);
1340     d->init();
1341 }
1342
1343 /*!
1344     Destroys the menu.
1345 */
1346 QMenu::~QMenu()
1347 {
1348     Q_D(QMenu);
1349     if (!d->widgetItems.isEmpty()) {  // avoid detach on shared null hash
1350         QHash<QAction *, QWidget *>::iterator it = d->widgetItems.begin();
1351         for (; it != d->widgetItems.end(); ++it) {
1352             if (QWidget *widget = it.value()) {
1353                 QWidgetAction *action = static_cast<QWidgetAction *>(it.key());
1354                 action->releaseWidget(widget);
1355                 *it = 0;
1356             }
1357         }
1358     }
1359
1360     if (d->eventLoop)
1361         d->eventLoop->exit();
1362     hideTearOffMenu();
1363 }
1364
1365 /*!
1366     \overload
1367
1368     This convenience function creates a new action with \a text.
1369     The function adds the newly created action to the menu's
1370     list of actions, and returns it.
1371
1372     \sa QWidget::addAction()
1373 */
1374 QAction *QMenu::addAction(const QString &text)
1375 {
1376     QAction *ret = new QAction(text, this);
1377     addAction(ret);
1378     return ret;
1379 }
1380
1381 /*!
1382     \overload
1383
1384     This convenience function creates a new action with an \a icon
1385     and some \a text. The function adds the newly created action to
1386     the menu's list of actions, and returns it.
1387
1388     \sa QWidget::addAction()
1389 */
1390 QAction *QMenu::addAction(const QIcon &icon, const QString &text)
1391 {
1392     QAction *ret = new QAction(icon, text, this);
1393     addAction(ret);
1394     return ret;
1395 }
1396
1397 /*!
1398     \overload
1399
1400     This convenience function creates a new action with the text \a
1401     text and an optional shortcut \a shortcut. The action's
1402     \l{QAction::triggered()}{triggered()} signal is connected to the
1403     \a receiver's \a member slot. The function adds the newly created
1404     action to the menu's list of actions and returns it.
1405
1406     \sa QWidget::addAction()
1407 */
1408 QAction *QMenu::addAction(const QString &text, const QObject *receiver, const char* member, const QKeySequence &shortcut)
1409 {
1410     QAction *action = new QAction(text, this);
1411 #ifdef QT_NO_SHORTCUT
1412     Q_UNUSED(shortcut);
1413 #else
1414     action->setShortcut(shortcut);
1415 #endif
1416     QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
1417     addAction(action);
1418     return action;
1419 }
1420
1421 /*!
1422     \overload
1423
1424     This convenience function creates a new action with an \a icon and
1425     some \a text and an optional shortcut \a shortcut. The action's
1426     \l{QAction::triggered()}{triggered()} signal is connected to the
1427     \a member slot of the \a receiver object. The function adds the
1428     newly created action to the menu's list of actions, and returns it.
1429
1430     \sa QWidget::addAction()
1431 */
1432 QAction *QMenu::addAction(const QIcon &icon, const QString &text, const QObject *receiver,
1433                           const char* member, const QKeySequence &shortcut)
1434 {
1435     QAction *action = new QAction(icon, text, this);
1436 #ifdef QT_NO_SHORTCUT
1437     Q_UNUSED(shortcut);
1438 #else
1439     action->setShortcut(shortcut);
1440 #endif
1441     QObject::connect(action, SIGNAL(triggered(bool)), receiver, member);
1442     addAction(action);
1443     return action;
1444 }
1445
1446 /*!
1447     This convenience function adds \a menu as a submenu to this menu.
1448     It returns \a menu's menuAction(). This menu does not take
1449     ownership of \a menu.
1450
1451     \sa QWidget::addAction(), QMenu::menuAction()
1452 */
1453 QAction *QMenu::addMenu(QMenu *menu)
1454 {
1455     QAction *action = menu->menuAction();
1456     addAction(action);
1457     return action;
1458 }
1459
1460 /*!
1461   Appends a new QMenu with \a title to the menu. The menu
1462   takes ownership of the menu. Returns the new menu.
1463
1464   \sa QWidget::addAction(), QMenu::menuAction()
1465 */
1466 QMenu *QMenu::addMenu(const QString &title)
1467 {
1468     QMenu *menu = new QMenu(title, this);
1469     addAction(menu->menuAction());
1470     return menu;
1471 }
1472
1473 /*!
1474   Appends a new QMenu with \a icon and \a title to the menu. The menu
1475   takes ownership of the menu. Returns the new menu.
1476
1477   \sa QWidget::addAction(), QMenu::menuAction()
1478 */
1479 QMenu *QMenu::addMenu(const QIcon &icon, const QString &title)
1480 {
1481     QMenu *menu = new QMenu(title, this);
1482     menu->setIcon(icon);
1483     addAction(menu->menuAction());
1484     return menu;
1485 }
1486
1487 /*!
1488     This convenience function creates a new separator action, i.e. an
1489     action with QAction::isSeparator() returning true, and adds the new
1490     action to this menu's list of actions. It returns the newly
1491     created action.
1492
1493     \sa QWidget::addAction()
1494 */
1495 QAction *QMenu::addSeparator()
1496 {
1497     QAction *action = new QAction(this);
1498     action->setSeparator(true);
1499     addAction(action);
1500     return action;
1501 }
1502
1503 /*!
1504     This convenience function inserts \a menu before action \a before
1505     and returns the menus menuAction().
1506
1507     \sa QWidget::insertAction(), addMenu()
1508 */
1509 QAction *QMenu::insertMenu(QAction *before, QMenu *menu)
1510 {
1511     QAction *action = menu->menuAction();
1512     insertAction(before, action);
1513     return action;
1514 }
1515
1516 /*!
1517     This convenience function creates a new separator action, i.e. an
1518     action with QAction::isSeparator() returning true. The function inserts
1519     the newly created action into this menu's list of actions before
1520     action \a before and returns it.
1521
1522     \sa QWidget::insertAction(), addSeparator()
1523 */
1524 QAction *QMenu::insertSeparator(QAction *before)
1525 {
1526     QAction *action = new QAction(this);
1527     action->setSeparator(true);
1528     insertAction(before, action);
1529     return action;
1530 }
1531
1532 /*!
1533   This sets the default action to \a act. The default action may have
1534   a visual cue, depending on the current QStyle. A default action
1535   usually indicates what will happen by default when a drop occurs.
1536
1537   \sa defaultAction()
1538 */
1539 void QMenu::setDefaultAction(QAction *act)
1540 {
1541     d_func()->defaultAction = act;
1542 }
1543
1544 /*!
1545   Returns the current default action.
1546
1547   \sa setDefaultAction()
1548 */
1549 QAction *QMenu::defaultAction() const
1550 {
1551     return d_func()->defaultAction;
1552 }
1553
1554 /*!
1555     \property QMenu::tearOffEnabled
1556     \brief whether the menu supports being torn off
1557
1558     When true, the menu contains a special tear-off item (often shown as a dashed
1559     line at the top of the menu) that creates a copy of the menu when it is
1560     triggered.
1561
1562     This "torn-off" copy lives in a separate window. It contains the same menu
1563     items as the original menu, with the exception of the tear-off handle.
1564
1565     By default, this property is false.
1566 */
1567 void QMenu::setTearOffEnabled(bool b)
1568 {
1569     Q_D(QMenu);
1570     if (d->tearoff == b)
1571         return;
1572     if (!b)
1573         hideTearOffMenu();
1574     d->tearoff = b;
1575
1576     d->itemsDirty = true;
1577     if (isVisible())
1578         resize(sizeHint());
1579 }
1580
1581 bool QMenu::isTearOffEnabled() const
1582 {
1583     return d_func()->tearoff;
1584 }
1585
1586 /*!
1587   When a menu is torn off a second menu is shown to display the menu
1588   contents in a new window. When the menu is in this mode and the menu
1589   is visible returns true; otherwise false.
1590
1591   \sa hideTearOffMenu(), isTearOffEnabled()
1592 */
1593 bool QMenu::isTearOffMenuVisible() const
1594 {
1595     if (d_func()->tornPopup)
1596         return d_func()->tornPopup->isVisible();
1597     return false;
1598 }
1599
1600 /*!
1601    This function will forcibly hide the torn off menu making it
1602    disappear from the users desktop.
1603
1604    \sa isTearOffMenuVisible(), isTearOffEnabled()
1605 */
1606 void QMenu::hideTearOffMenu()
1607 {
1608     if (QWidget *w = d_func()->tornPopup)
1609         w->close();
1610 }
1611
1612
1613 /*!
1614   Sets the currently highlighted action to \a act.
1615 */
1616 void QMenu::setActiveAction(QAction *act)
1617 {
1618     Q_D(QMenu);
1619     d->setCurrentAction(act, 0);
1620     if (d->scroll)
1621         d->scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollCenter);
1622 }
1623
1624
1625 /*!
1626     Returns the currently highlighted action, or 0 if no
1627     action is currently highlighted.
1628 */
1629 QAction *QMenu::activeAction() const
1630 {
1631     return d_func()->currentAction;
1632 }
1633
1634 /*!
1635     \since 4.2
1636
1637     Returns true if there are no visible actions inserted into the menu, false
1638     otherwise.
1639
1640     \sa QWidget::actions()
1641 */
1642
1643 bool QMenu::isEmpty() const
1644 {
1645     bool ret = true;
1646     for(int i = 0; ret && i < actions().count(); ++i) {
1647         const QAction *action = actions().at(i);
1648         if (!action->isSeparator() && action->isVisible()) {
1649             ret = false;
1650         }
1651     }
1652     return ret;
1653 }
1654
1655 /*!
1656     Removes all the menu's actions. Actions owned by the menu and not
1657     shown in any other widget are deleted.
1658
1659     \sa removeAction()
1660 */
1661 void QMenu::clear()
1662 {
1663     QList<QAction*> acts = actions();
1664
1665     for(int i = 0; i < acts.size(); i++) {
1666 #ifdef QT_SOFTKEYS_ENABLED
1667         Q_D(QMenu);
1668         // Lets not touch to our internal softkey actions
1669         if(acts[i] == d->selectAction || acts[i] == d->cancelAction)
1670             continue;
1671 #endif
1672         removeAction(acts[i]);
1673         if (acts[i]->parent() == this && acts[i]->d_func()->widgets.isEmpty())
1674             delete acts[i];
1675     }
1676 }
1677
1678 /*!
1679   If a menu does not fit on the screen it lays itself out so that it
1680   does fit. It is style dependent what layout means (for example, on
1681   Windows it will use multiple columns).
1682
1683   This functions returns the number of columns necessary.
1684 */
1685 int QMenu::columnCount() const
1686 {
1687     return d_func()->ncols;
1688 }
1689
1690 /*!
1691   Returns the item at \a pt; returns 0 if there is no item there.
1692 */
1693 QAction *QMenu::actionAt(const QPoint &pt) const
1694 {
1695     if (QAction *ret = d_func()->actionAt(pt))
1696         return ret;
1697     return 0;
1698 }
1699
1700 /*!
1701   Returns the geometry of action \a act.
1702 */
1703 QRect QMenu::actionGeometry(QAction *act) const
1704 {
1705     return d_func()->actionRect(act);
1706 }
1707
1708 /*!
1709     \reimp
1710 */
1711 QSize QMenu::sizeHint() const
1712 {
1713     Q_D(const QMenu);
1714     d->updateActionRects();
1715
1716     QSize s;
1717     for (int i = 0; i < d->actionRects.count(); ++i) {
1718         const QRect &rect = d->actionRects.at(i);
1719         if (rect.isNull())
1720             continue;
1721         if (rect.bottom() >= s.height())
1722             s.setHeight(rect.y() + rect.height());
1723         if (rect.right() >= s.width())
1724             s.setWidth(rect.x() + rect.width());
1725     }
1726     // Note that the action rects calculated above already include
1727     // the top and left margins, so we only need to add margins for
1728     // the bottom and right.
1729     QStyleOption opt(0);
1730     opt.init(this);
1731     const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, this);
1732     s.rwidth() += style()->pixelMetric(QStyle::PM_MenuHMargin, &opt, this) + fw + d->rightmargin;
1733     s.rheight() += style()->pixelMetric(QStyle::PM_MenuVMargin, &opt, this) + fw + d->bottommargin;
1734
1735     return style()->sizeFromContents(QStyle::CT_Menu, &opt,
1736                                     s.expandedTo(QApplication::globalStrut()), this);
1737 }
1738
1739 /*!
1740     Displays the menu so that the action \a atAction will be at the
1741     specified \e global position \a p. To translate a widget's local
1742     coordinates into global coordinates, use QWidget::mapToGlobal().
1743
1744     When positioning a menu with exec() or popup(), bear in mind that
1745     you cannot rely on the menu's current size(). For performance
1746     reasons, the menu adapts its size only when necessary, so in many
1747     cases, the size before and after the show is different. Instead,
1748     use sizeHint() which calculates the proper size depending on the
1749     menu's current contents.
1750
1751     \sa QWidget::mapToGlobal(), exec()
1752 */
1753 void QMenu::popup(const QPoint &p, QAction *atAction)
1754 {
1755     Q_D(QMenu);
1756     if (d->scroll) { // reset scroll state from last popup
1757         d->scroll->scrollOffset = 0;
1758         d->scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
1759     }
1760     d->tearoffHighlighted = 0;
1761     d->motions = 0;
1762     d->doChildEffects = true;
1763     d->updateLayoutDirection();
1764
1765 #ifndef QT_NO_MENUBAR
1766     // if this menu is part of a chain attached to a QMenuBar, set the
1767     // _NET_WM_WINDOW_TYPE_DROPDOWN_MENU X11 window type
1768     setAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu, qobject_cast<QMenuBar *>(d->topCausedWidget()) != 0);
1769 #endif
1770
1771     ensurePolished(); // Get the right font
1772     emit aboutToShow();
1773     const bool actionListChanged = d->itemsDirty;
1774     d->updateActionRects();
1775     QPoint pos;
1776     QPushButton *causedButton = qobject_cast<QPushButton*>(d->causedPopup.widget);
1777     if (actionListChanged && causedButton)
1778         pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition();
1779     else
1780         pos = p;
1781
1782     QSize size = sizeHint();
1783     QRect screen;
1784 #ifndef QT_NO_GRAPHICSVIEW
1785     bool isEmbedded = !bypassGraphicsProxyWidget(this) && d->nearestGraphicsProxyWidget(this);
1786     if (isEmbedded)
1787         screen = d->popupGeometry(this);
1788     else
1789 #endif
1790     screen = d->popupGeometry(QApplication::desktop()->screenNumber(p));
1791     const int desktopFrame = style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, this);
1792     bool adjustToDesktop = !window()->testAttribute(Qt::WA_DontShowOnScreen);
1793
1794     // if the screens have very different geometries and the menu is too big, we have to recalculate
1795     if (size.height() > screen.height() || size.width() > screen.width()) {
1796         size = d->adjustMenuSizeForScreen(screen);
1797         adjustToDesktop = true;
1798     }
1799     // Layout is not right, we might be able to save horizontal space
1800     if (d->ncols >1 && size.height() < screen.height()) {
1801         size = d->adjustMenuSizeForScreen(screen);
1802         adjustToDesktop = true;
1803     }
1804
1805 #ifdef QT_KEYPAD_NAVIGATION
1806     if (!atAction && QApplication::keypadNavigationEnabled()) {
1807         // Try to have one item activated
1808         if (d->defaultAction && d->defaultAction->isEnabled()) {
1809             atAction = d->defaultAction;
1810             // TODO: This works for first level menus, not yet sub menus
1811         } else {
1812             foreach (QAction *action, d->actions)
1813                 if (action->isEnabled()) {
1814                     atAction = action;
1815                     break;
1816                 }
1817         }
1818         d->currentAction = atAction;
1819     }
1820 #endif
1821     if (d->ncols > 1) {
1822         pos.setY(screen.top() + desktopFrame);
1823     } else if (atAction) {
1824         for (int i = 0, above_height = 0; i < d->actions.count(); i++) {
1825             QAction *action = d->actions.at(i);
1826             if (action == atAction) {
1827                 int newY = pos.y() - above_height;
1828                 if (d->scroll && newY < desktopFrame) {
1829                     d->scroll->scrollFlags = d->scroll->scrollFlags
1830                                              | QMenuPrivate::QMenuScroller::ScrollUp;
1831                     d->scroll->scrollOffset = newY;
1832                     newY = desktopFrame;
1833                 }
1834                 pos.setY(newY);
1835
1836                 if (d->scroll && d->scroll->scrollFlags != QMenuPrivate::QMenuScroller::ScrollNone
1837                     && !style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll, 0, this)) {
1838                     int below_height = above_height + d->scroll->scrollOffset;
1839                     for (int i2 = i; i2 < d->actionRects.count(); i2++)
1840                         below_height += d->actionRects.at(i2).height();
1841                     size.setHeight(below_height);
1842                 }
1843                 break;
1844             } else {
1845                 above_height += d->actionRects.at(i).height();
1846             }
1847         }
1848     }
1849
1850     QPoint mouse = QCursor::pos();
1851     d->mousePopupPos = mouse;
1852     const bool snapToMouse = (QRect(p.x() - 3, p.y() - 3, 6, 6).contains(mouse));
1853
1854     if (adjustToDesktop) {
1855         // handle popup falling "off screen"
1856         if (isRightToLeft()) {
1857             if (snapToMouse) // position flowing left from the mouse
1858                 pos.setX(mouse.x() - size.width());
1859
1860 #ifndef QT_NO_MENUBAR
1861             // if in a menubar, it should be right-aligned
1862             if (qobject_cast<QMenuBar*>(d->causedPopup.widget))
1863                 pos.rx() -= size.width();
1864 #endif //QT_NO_MENUBAR
1865
1866             if (pos.x() < screen.left() + desktopFrame)
1867                 pos.setX(qMax(p.x(), screen.left() + desktopFrame));
1868             if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
1869                 pos.setX(qMax(p.x() - size.width(), screen.right() - desktopFrame - size.width() + 1));
1870         } else {
1871             if (pos.x() + size.width() - 1 > screen.right() - desktopFrame)
1872                 pos.setX(screen.right() - desktopFrame - size.width() + 1);
1873             if (pos.x() < screen.left() + desktopFrame)
1874                 pos.setX(screen.left() + desktopFrame);
1875         }
1876         if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
1877             if(snapToMouse)
1878                 pos.setY(qMin(mouse.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
1879             else
1880                 pos.setY(qMax(p.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()+1));
1881         } else if (pos.y() < screen.top() + desktopFrame) {
1882             pos.setY(screen.top() + desktopFrame);
1883         }
1884
1885         if (pos.y() < screen.top() + desktopFrame)
1886             pos.setY(screen.top() + desktopFrame);
1887         if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
1888             if (d->scroll) {
1889                 d->scroll->scrollFlags |= uint(QMenuPrivate::QMenuScroller::ScrollDown);
1890                 int y = qMax(screen.y(),pos.y());
1891                 size.setHeight(screen.bottom() - (desktopFrame * 2) - y);
1892             } else {
1893                 // Too big for screen, bias to see bottom of menu (for some reason)
1894                 pos.setY(screen.bottom() - size.height() + 1);
1895             }
1896         }
1897     }
1898     const int subMenuOffset = style()->pixelMetric(QStyle::PM_SubMenuOverlap, 0, this);
1899     const QSize menuSize(sizeHint());
1900     QMenu *caused = qobject_cast<QMenu*>(d_func()->causedPopup.widget);
1901     if (caused && caused->geometry().width() + menuSize.width() + subMenuOffset < screen.width()) {
1902         QRect parentActionRect(caused->d_func()->actionRect(caused->d_func()->currentAction));
1903         const QPoint actionTopLeft = caused->mapToGlobal(parentActionRect.topLeft());
1904         parentActionRect.moveTopLeft(actionTopLeft);
1905         if (isRightToLeft()) {
1906             if ((pos.x() + menuSize.width() > parentActionRect.left() - subMenuOffset)
1907                 && (pos.x() < parentActionRect.right()))
1908             {
1909                 pos.rx() = parentActionRect.left() - menuSize.width();
1910                 if (pos.x() < screen.x())
1911                     pos.rx() = parentActionRect.right();
1912                 if (pos.x() + menuSize.width() > screen.x() + screen.width())
1913                     pos.rx() = screen.x();
1914             }
1915         } else {
1916             if ((pos.x() < parentActionRect.right() + subMenuOffset)
1917                 && (pos.x() + menuSize.width() > parentActionRect.left()))
1918             {
1919                 pos.rx() = parentActionRect.right();
1920                 if (pos.x() + menuSize.width() > screen.x() + screen.width())
1921                     pos.rx() = parentActionRect.left() - menuSize.width();
1922                 if (pos.x() < screen.x())
1923                     pos.rx() = screen.x() + screen.width() - menuSize.width();
1924             }
1925         }
1926     }
1927     setGeometry(QRect(pos, size));
1928 #ifndef QT_NO_EFFECTS
1929     int hGuess = isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll;
1930     int vGuess = QEffects::DownScroll;
1931     if (isRightToLeft()) {
1932         if ((snapToMouse && (pos.x() + size.width() / 2 > mouse.x())) ||
1933            (qobject_cast<QMenu*>(d->causedPopup.widget) && pos.x() + size.width() / 2 > d->causedPopup.widget->x()))
1934             hGuess = QEffects::RightScroll;
1935     } else {
1936         if ((snapToMouse && (pos.x() + size.width() / 2 < mouse.x())) ||
1937            (qobject_cast<QMenu*>(d->causedPopup.widget) && pos.x() + size.width() / 2 < d->causedPopup.widget->x()))
1938             hGuess = QEffects::LeftScroll;
1939     }
1940
1941 #ifndef QT_NO_MENUBAR
1942     if ((snapToMouse && (pos.y() + size.height() / 2 < mouse.y())) ||
1943        (qobject_cast<QMenuBar*>(d->causedPopup.widget) &&
1944         pos.y() + size.width() / 2 < d->causedPopup.widget->mapToGlobal(d->causedPopup.widget->pos()).y()))
1945        vGuess = QEffects::UpScroll;
1946 #endif
1947     if (QApplication::isEffectEnabled(Qt::UI_AnimateMenu)) {
1948         bool doChildEffects = true;
1949 #ifndef QT_NO_MENUBAR
1950         if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->causedPopup.widget)) {
1951             doChildEffects = mb->d_func()->doChildEffects;
1952             mb->d_func()->doChildEffects = false;
1953         } else
1954 #endif
1955         if (QMenu *m = qobject_cast<QMenu*>(d->causedPopup.widget)) {
1956             doChildEffects = m->d_func()->doChildEffects;
1957             m->d_func()->doChildEffects = false;
1958         }
1959
1960         if (doChildEffects) {
1961             if (QApplication::isEffectEnabled(Qt::UI_FadeMenu))
1962                 qFadeEffect(this);
1963             else if (d->causedPopup.widget)
1964                 qScrollEffect(this, qobject_cast<QMenu*>(d->causedPopup.widget) ? hGuess : vGuess);
1965             else
1966                 qScrollEffect(this, hGuess | vGuess);
1967         } else {
1968             // kill any running effect
1969             qFadeEffect(0);
1970             qScrollEffect(0);
1971
1972             show();
1973         }
1974     } else
1975 #endif
1976     {
1977         show();
1978     }
1979
1980 #ifndef QT_NO_ACCESSIBILITY
1981     QAccessibleEvent event(this, QAccessible::PopupMenuStart);
1982     QAccessible::updateAccessibility(&event);
1983 #endif
1984 }
1985
1986 /*!
1987     Executes this menu synchronously.
1988
1989     This is equivalent to \c{exec(pos())}.
1990
1991     This returns the triggered QAction in either the popup menu or one
1992     of its submenus, or 0 if no item was triggered (normally because
1993     the user pressed Esc).
1994
1995     In most situations you'll want to specify the position yourself,
1996     for example, the current mouse position:
1997     \snippet code/src_gui_widgets_qmenu.cpp 0
1998     or aligned to a widget:
1999     \snippet code/src_gui_widgets_qmenu.cpp 1
2000     or in reaction to a QMouseEvent *e:
2001     \snippet code/src_gui_widgets_qmenu.cpp 2
2002 */
2003 QAction *QMenu::exec()
2004 {
2005     return exec(pos());
2006 }
2007
2008
2009 /*!
2010     \overload
2011
2012     Executes this menu synchronously.
2013
2014     Pops up the menu so that the action \a action will be at the
2015     specified \e global position \a p. To translate a widget's local
2016     coordinates into global coordinates, use QWidget::mapToGlobal().
2017
2018     This returns the triggered QAction in either the popup menu or one
2019     of its submenus, or 0 if no item was triggered (normally because
2020     the user pressed Esc).
2021
2022     Note that all signals are emitted as usual. If you connect a
2023     QAction to a slot and call the menu's exec(), you get the result
2024     both via the signal-slot connection and in the return value of
2025     exec().
2026
2027     Common usage is to position the menu at the current mouse
2028     position:
2029     \snippet code/src_gui_widgets_qmenu.cpp 3
2030     or aligned to a widget:
2031     \snippet code/src_gui_widgets_qmenu.cpp 4
2032     or in reaction to a QMouseEvent *e:
2033     \snippet code/src_gui_widgets_qmenu.cpp 5
2034
2035     When positioning a menu with exec() or popup(), bear in mind that
2036     you cannot rely on the menu's current size(). For performance
2037     reasons, the menu adapts its size only when necessary. So in many
2038     cases, the size before and after the show is different. Instead,
2039     use sizeHint() which calculates the proper size depending on the
2040     menu's current contents.
2041
2042     \sa popup(), QWidget::mapToGlobal()
2043 */
2044 QAction *QMenu::exec(const QPoint &p, QAction *action)
2045 {
2046     Q_D(QMenu);
2047     createWinId();
2048     QEventLoop eventLoop;
2049     d->eventLoop = &eventLoop;
2050     popup(p, action);
2051
2052     QPointer<QObject> guard = this;
2053     (void) eventLoop.exec();
2054     if (guard.isNull())
2055         return 0;
2056
2057     action = d->syncAction;
2058     d->syncAction = 0;
2059     d->eventLoop = 0;
2060     return action;
2061 }
2062
2063 /*!
2064     \overload
2065
2066     Executes a menu synchronously.
2067
2068     The menu's actions are specified by the list of \a actions. The menu will
2069     pop up so that the specified action, \a at, appears at global position \a
2070     pos. If \a at is not specified then the menu appears at position \a
2071     pos. \a parent is the menu's parent widget; specifying the parent will
2072     provide context when \a pos alone is not enough to decide where the menu
2073     should go (e.g., with multiple desktops or when the parent is embedded in
2074     QGraphicsView).
2075
2076     The function returns the triggered QAction in either the popup
2077     menu or one of its submenus, or 0 if no item was triggered
2078     (normally because the user pressed Esc).
2079
2080     This is equivalent to:
2081     \snippet code/src_gui_widgets_qmenu.cpp 6
2082
2083     \sa popup(), QWidget::mapToGlobal()
2084 */
2085 QAction *QMenu::exec(QList<QAction*> actions, const QPoint &pos, QAction *at, QWidget *parent)
2086 {
2087     QMenu menu(parent);
2088     menu.addActions(actions);
2089     return menu.exec(pos, at);
2090 }
2091
2092 /*!
2093   \reimp
2094 */
2095 void QMenu::hideEvent(QHideEvent *)
2096 {
2097     Q_D(QMenu);
2098     emit aboutToHide();
2099     if (d->eventLoop)
2100         d->eventLoop->exit();
2101     d->setCurrentAction(0);
2102 #ifndef QT_NO_ACCESSIBILITY
2103     QAccessibleEvent event(this, QAccessible::PopupMenuEnd);
2104     QAccessible::updateAccessibility(&event);
2105 #endif
2106 #ifndef QT_NO_MENUBAR
2107     if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->causedPopup.widget))
2108         mb->d_func()->setCurrentAction(0);
2109 #endif
2110     d->mouseDown = 0;
2111     d->hasHadMouse = false;
2112     d->causedPopup.widget = 0;
2113     d->causedPopup.action = 0;
2114     if (d->scroll)
2115         d->scroll->scrollTimer.stop(); //make sure the timer stops
2116 }
2117
2118 /*!
2119   \reimp
2120 */
2121 void QMenu::paintEvent(QPaintEvent *e)
2122 {
2123     Q_D(QMenu);
2124     d->updateActionRects();
2125     QPainter p(this);
2126     QRegion emptyArea = QRegion(rect());
2127
2128     QStyleOptionMenuItem menuOpt;
2129     menuOpt.initFrom(this);
2130     menuOpt.state = QStyle::State_None;
2131     menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2132     menuOpt.maxIconWidth = 0;
2133     menuOpt.tabWidth = 0;
2134     style()->drawPrimitive(QStyle::PE_PanelMenu, &menuOpt, &p, this);
2135
2136     //draw the items that need updating..
2137     for (int i = 0; i < d->actions.count(); ++i) {
2138         QAction *action = d->actions.at(i);
2139         QRect adjustedActionRect = d->actionRects.at(i);
2140         if (!e->rect().intersects(adjustedActionRect)
2141             || d->widgetItems.value(action))
2142            continue;
2143         //set the clip region to be extra safe (and adjust for the scrollers)
2144         QRegion adjustedActionReg(adjustedActionRect);
2145         emptyArea -= adjustedActionReg;
2146         p.setClipRegion(adjustedActionReg);
2147
2148         QStyleOptionMenuItem opt;
2149         initStyleOption(&opt, action);
2150         opt.rect = adjustedActionRect;
2151         style()->drawControl(QStyle::CE_MenuItem, &opt, &p, this);
2152     }
2153
2154     const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, this);
2155     //draw the scroller regions..
2156     if (d->scroll) {
2157         menuOpt.menuItemType = QStyleOptionMenuItem::Scroller;
2158         menuOpt.state |= QStyle::State_Enabled;
2159         if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) {
2160             menuOpt.rect.setRect(fw, fw, width() - (fw * 2), d->scrollerHeight());
2161             emptyArea -= QRegion(menuOpt.rect);
2162             p.setClipRect(menuOpt.rect);
2163             style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this);
2164         }
2165         if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown) {
2166             menuOpt.rect.setRect(fw, height() - d->scrollerHeight() - fw, width() - (fw * 2),
2167                                      d->scrollerHeight());
2168             emptyArea -= QRegion(menuOpt.rect);
2169             menuOpt.state |= QStyle::State_DownArrow;
2170             p.setClipRect(menuOpt.rect);
2171             style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this);
2172         }
2173     }
2174     //paint the tear off..
2175     if (d->tearoff) {
2176         menuOpt.menuItemType = QStyleOptionMenuItem::TearOff;
2177         menuOpt.rect.setRect(fw, fw, width() - (fw * 2),
2178                              style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this));
2179         if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2180             menuOpt.rect.translate(0, d->scrollerHeight());
2181         emptyArea -= QRegion(menuOpt.rect);
2182         p.setClipRect(menuOpt.rect);
2183         menuOpt.state = QStyle::State_None;
2184         if (d->tearoffHighlighted)
2185             menuOpt.state |= QStyle::State_Selected;
2186         style()->drawControl(QStyle::CE_MenuTearoff, &menuOpt, &p, this);
2187     }
2188     //draw border
2189     if (fw) {
2190         QRegion borderReg;
2191         borderReg += QRect(0, 0, fw, height()); //left
2192         borderReg += QRect(width()-fw, 0, fw, height()); //right
2193         borderReg += QRect(0, 0, width(), fw); //top
2194         borderReg += QRect(0, height()-fw, width(), fw); //bottom
2195         p.setClipRegion(borderReg);
2196         emptyArea -= borderReg;
2197         QStyleOptionFrame frame;
2198         frame.rect = rect();
2199         frame.palette = palette();
2200         frame.state = QStyle::State_None;
2201         frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth);
2202         frame.midLineWidth = 0;
2203         style()->drawPrimitive(QStyle::PE_FrameMenu, &frame, &p, this);
2204     }
2205
2206     //finally the rest of the space
2207     p.setClipRegion(emptyArea);
2208     menuOpt.state = QStyle::State_None;
2209     menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea;
2210     menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
2211     menuOpt.rect = rect();
2212     menuOpt.menuRect = rect();
2213     style()->drawControl(QStyle::CE_MenuEmptyArea, &menuOpt, &p, this);
2214 }
2215
2216 #ifndef QT_NO_WHEELEVENT
2217 /*!
2218   \reimp
2219 */
2220 void QMenu::wheelEvent(QWheelEvent *e)
2221 {
2222     Q_D(QMenu);
2223     if (d->scroll && rect().contains(e->pos()))
2224         d->scrollMenu(e->delta() > 0 ?
2225                       QMenuPrivate::QMenuScroller::ScrollUp : QMenuPrivate::QMenuScroller::ScrollDown);
2226 }
2227 #endif
2228
2229 /*!
2230   \reimp
2231 */
2232 void QMenu::mousePressEvent(QMouseEvent *e)
2233 {
2234     Q_D(QMenu);
2235     if (d->aboutToHide || d->mouseEventTaken(e))
2236         return;
2237     if (!rect().contains(e->pos())) {
2238          if (d->noReplayFor
2239              && QRect(d->noReplayFor->mapToGlobal(QPoint()), d->noReplayFor->size()).contains(e->globalPos()))
2240              setAttribute(Qt::WA_NoMouseReplay);
2241          if (d->eventLoop) // synchronous operation
2242              d->syncAction = 0;
2243         d->hideUpToMenuBar();
2244         return;
2245     }
2246     d->mouseDown = this;
2247
2248     QAction *action = d->actionAt(e->pos());
2249     d->setCurrentAction(action, 20);
2250     update();
2251 }
2252
2253 /*!
2254   \reimp
2255 */
2256 void QMenu::mouseReleaseEvent(QMouseEvent *e)
2257 {
2258     Q_D(QMenu);
2259     if (d->aboutToHide || d->mouseEventTaken(e))
2260         return;
2261     if(d->mouseDown != this) {
2262         d->mouseDown = 0;
2263         return;
2264     }
2265
2266     d->mouseDown = 0;
2267     d->setSyncAction();
2268     QAction *action = d->actionAt(e->pos());
2269
2270     if (action && action == d->currentAction) {
2271         if (!action->menu()){
2272 #if defined(Q_OS_WIN)
2273             //On Windows only context menus can be activated with the right button
2274             if (e->button() == Qt::LeftButton || d->topCausedWidget() == 0)
2275 #endif
2276                 d->activateAction(action, QAction::Trigger);
2277         }
2278     } else if (d->hasMouseMoved(e->globalPos())) {
2279         d->hideUpToMenuBar();
2280     }
2281 }
2282
2283 /*!
2284   \reimp
2285 */
2286 void QMenu::changeEvent(QEvent *e)
2287 {
2288     Q_D(QMenu);
2289     if (e->type() == QEvent::StyleChange || e->type() == QEvent::FontChange ||
2290         e->type() == QEvent::LayoutDirectionChange) {
2291         d->itemsDirty = 1;
2292         setMouseTracking(style()->styleHint(QStyle::SH_Menu_MouseTracking, 0, this));
2293         if (isVisible())
2294             resize(sizeHint());
2295         if (!style()->styleHint(QStyle::SH_Menu_Scrollable, 0, this)) {
2296             delete d->scroll;
2297             d->scroll = 0;
2298         } else if (!d->scroll) {
2299             d->scroll = new QMenuPrivate::QMenuScroller;
2300             d->scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
2301         }
2302     } else if (e->type() == QEvent::EnabledChange) {
2303         if (d->tornPopup) // torn-off menu
2304             d->tornPopup->setEnabled(isEnabled());
2305         d->menuAction->setEnabled(isEnabled());
2306         if (d->platformMenu)
2307             d->platformMenu->setEnabled(isEnabled());
2308     }
2309     QWidget::changeEvent(e);
2310 }
2311
2312
2313 /*!
2314   \reimp
2315 */
2316 bool
2317 QMenu::event(QEvent *e)
2318 {
2319     Q_D(QMenu);
2320     switch (e->type()) {
2321     case QEvent::Polish:
2322         d->updateLayoutDirection();
2323         break;
2324     case QEvent::ShortcutOverride: {
2325             QKeyEvent *kev = static_cast<QKeyEvent*>(e);
2326             if (kev->key() == Qt::Key_Up || kev->key() == Qt::Key_Down
2327                 || kev->key() == Qt::Key_Left || kev->key() == Qt::Key_Right
2328                 || kev->key() == Qt::Key_Enter || kev->key() == Qt::Key_Return
2329                 || kev->key() == Qt::Key_Escape) {
2330                 e->accept();
2331                 return true;
2332             }
2333         }
2334         break;
2335     case QEvent::KeyPress: {
2336         QKeyEvent *ke = (QKeyEvent*)e;
2337         if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
2338             keyPressEvent(ke);
2339             return true;
2340         }
2341     } break;
2342     case QEvent::ContextMenu:
2343         if(d->menuDelayTimer.isActive()) {
2344             d->menuDelayTimer.stop();
2345             internalDelayedPopup();
2346         }
2347         break;
2348     case QEvent::Resize: {
2349         QStyleHintReturnMask menuMask;
2350         QStyleOption option;
2351         option.initFrom(this);
2352         if (style()->styleHint(QStyle::SH_Menu_Mask, &option, this, &menuMask)) {
2353             setMask(menuMask.region);
2354         }
2355         d->itemsDirty = 1;
2356         d->updateActionRects();
2357         break; }
2358     case QEvent::Show:
2359         d->mouseDown = 0;
2360         d->updateActionRects();
2361         if (d->currentAction)
2362             d->popupAction(d->currentAction, 0, false);
2363         break;
2364 #ifndef QT_NO_WHATSTHIS
2365     case QEvent::QueryWhatsThis:
2366         e->setAccepted(d->whatsThis.size());
2367         if (QAction *action = d->actionAt(static_cast<QHelpEvent*>(e)->pos())) {
2368             if (action->whatsThis().size() || action->menu())
2369                 e->accept();
2370         }
2371         return true;
2372 #endif
2373 #ifdef QT_SOFTKEYS_ENABLED
2374     case QEvent::LanguageChange: {
2375         d->selectAction->setText(QSoftKeyManager::standardSoftKeyText(QSoftKeyManager::SelectSoftKey));
2376         d->cancelAction->setText(QSoftKeyManager::standardSoftKeyText(QSoftKeyManager::CancelSoftKey));
2377         }
2378         break;
2379 #endif
2380     default:
2381         break;
2382     }
2383     return QWidget::event(e);
2384 }
2385
2386 /*!
2387     \reimp
2388 */
2389 bool QMenu::focusNextPrevChild(bool next)
2390 {
2391     setFocus();
2392     QKeyEvent ev(QEvent::KeyPress, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier);
2393     keyPressEvent(&ev);
2394     return true;
2395 }
2396
2397 /*!
2398   \reimp
2399 */
2400 void QMenu::keyPressEvent(QKeyEvent *e)
2401 {
2402     Q_D(QMenu);
2403     d->updateActionRects();
2404     int key = e->key();
2405     if (isRightToLeft()) {  // in reverse mode open/close key for submenues are reversed
2406         if (key == Qt::Key_Left)
2407             key = Qt::Key_Right;
2408         else if (key == Qt::Key_Right)
2409             key = Qt::Key_Left;
2410     }
2411 #ifndef Q_OS_MAC
2412     if (key == Qt::Key_Tab) //means down
2413         key = Qt::Key_Down;
2414     if (key == Qt::Key_Backtab) //means up
2415         key = Qt::Key_Up;
2416 #endif
2417
2418     bool key_consumed = false;
2419     switch(key) {
2420     case Qt::Key_Home:
2421         key_consumed = true;
2422         if (d->scroll)
2423             d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop, true);
2424         break;
2425     case Qt::Key_End:
2426         key_consumed = true;
2427         if (d->scroll)
2428             d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom, true);
2429         break;
2430     case Qt::Key_PageUp:
2431         key_consumed = true;
2432         if (d->currentAction && d->scroll) {
2433             if(d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2434                 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollUp, true, true);
2435             else
2436                 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollTop, true);
2437         }
2438         break;
2439     case Qt::Key_PageDown:
2440         key_consumed = true;
2441         if (d->currentAction && d->scroll) {
2442             if(d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
2443                 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollDown, true, true);
2444             else
2445                 d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollBottom, true);
2446         }
2447         break;
2448     case Qt::Key_Up:
2449     case Qt::Key_Down: {
2450         key_consumed = true;
2451         QAction *nextAction = 0;
2452         QMenuPrivate::QMenuScroller::ScrollLocation scroll_loc = QMenuPrivate::QMenuScroller::ScrollStay;
2453         if (!d->currentAction) {
2454             if(key == Qt::Key_Down) {
2455                 for(int i = 0; i < d->actions.count(); ++i) {
2456                     QAction *act = d->actions.at(i);
2457                     if (d->actionRects.at(i).isNull())
2458                         continue;
2459                     if (!act->isSeparator() &&
2460                         (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
2461                          || act->isEnabled())) {
2462                         nextAction = act;
2463                         break;
2464                     }
2465                 }
2466             } else {
2467                 for(int i = d->actions.count()-1; i >= 0; --i) {
2468                     QAction *act = d->actions.at(i);
2469                     if (d->actionRects.at(i).isNull())
2470                         continue;
2471                     if (!act->isSeparator() &&
2472                         (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
2473                          || act->isEnabled())) {
2474                         nextAction = act;
2475                         break;
2476                     }
2477                 }
2478             }
2479         } else {
2480             for(int i = 0, y = 0; !nextAction && i < d->actions.count(); i++) {
2481                 QAction *act = d->actions.at(i);
2482                 if (act == d->currentAction) {
2483                     if (key == Qt::Key_Up) {
2484                         for(int next_i = i-1; true; next_i--) {
2485                             if (next_i == -1) {
2486                                 if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this))
2487                                     break;
2488                                 if (d->scroll)
2489                                     scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
2490                                 next_i = d->actionRects.count()-1;
2491                             }
2492                             QAction *next = d->actions.at(next_i);
2493                             if (next == d->currentAction)
2494                                 break;
2495                             if (d->actionRects.at(next_i).isNull())
2496                                 continue;
2497                             if (next->isSeparator() ||
2498                                (!next->isEnabled() &&
2499                                 !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)))
2500                                 continue;
2501                             nextAction = next;
2502                             if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)) {
2503                                 int topVisible = d->scrollerHeight();
2504                                 if (d->tearoff)
2505                                     topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);
2506                                 if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.at(next_i).height())
2507                                     scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
2508                             }
2509                             break;
2510                         }
2511                         if (!nextAction && d->tearoff)
2512                             d->tearoffHighlighted = 1;
2513                     } else {
2514                         y += d->actionRects.at(i).height();
2515                         for(int next_i = i+1; true; next_i++) {
2516                             if (next_i == d->actionRects.count()) {
2517                                 if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this))
2518                                     break;
2519                                 if (d->scroll)
2520                                     scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
2521                                 next_i = 0;
2522                             }
2523                             QAction *next = d->actions.at(next_i);
2524                             if (next == d->currentAction)
2525                                 break;
2526                             if (d->actionRects.at(next_i).isNull())
2527                                 continue;
2528                             if (next->isSeparator() ||
2529                                (!next->isEnabled() &&
2530                                 !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)))
2531                                 continue;
2532                             nextAction = next;
2533                             if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)) {
2534                                 int bottomVisible = height() - d->scrollerHeight();
2535                                 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
2536                                     bottomVisible -= d->scrollerHeight();
2537                                 if (d->tearoff)
2538                                     bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);
2539                                 if ((y + d->scroll->scrollOffset + d->actionRects.at(next_i).height()) > bottomVisible)
2540                                     scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
2541                             }
2542                             break;
2543                         }
2544                     }
2545                     break;
2546                 }
2547                 y += d->actionRects.at(i).height();
2548             }
2549         }
2550         if (nextAction) {
2551             if (d->scroll && scroll_loc != QMenuPrivate::QMenuScroller::ScrollStay) {
2552                 d->scroll->scrollTimer.stop();
2553                 d->scrollMenu(nextAction, scroll_loc);
2554             }
2555             d->setCurrentAction(nextAction, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
2556         }
2557         break; }
2558
2559     case Qt::Key_Right:
2560         if (d->currentAction && d->currentAction->isEnabled() && d->currentAction->menu()) {
2561             d->popupAction(d->currentAction, 0, true);
2562             key_consumed = true;
2563             break;
2564         }
2565         //FALL THROUGH
2566     case Qt::Key_Left: {
2567         if (d->currentAction && !d->scroll) {
2568             QAction *nextAction = 0;
2569             if (key == Qt::Key_Left) {
2570                 QRect actionR = d->actionRect(d->currentAction);
2571                 for(int x = actionR.left()-1; !nextAction && x >= 0; x--)
2572                     nextAction = d->actionAt(QPoint(x, actionR.center().y()));
2573             } else {
2574                 QRect actionR = d->actionRect(d->currentAction);
2575                 for(int x = actionR.right()+1; !nextAction && x < width(); x++)
2576                     nextAction = d->actionAt(QPoint(x, actionR.center().y()));
2577             }
2578             if (nextAction) {
2579                 d->setCurrentAction(nextAction, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
2580                 key_consumed = true;
2581             }
2582         }
2583         if (!key_consumed && key == Qt::Key_Left && qobject_cast<QMenu*>(d->causedPopup.widget)) {
2584             QPointer<QWidget> caused = d->causedPopup.widget;
2585             d->hideMenu(this);
2586             if (caused)
2587                 caused->setFocus();
2588             key_consumed = true;
2589         }
2590         break; }
2591
2592     case Qt::Key_Alt:
2593         if (d->tornoff)
2594             break;
2595
2596         key_consumed = true;
2597         if (style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, 0, this))
2598         {
2599             d->hideMenu(this);
2600 #ifndef QT_NO_MENUBAR
2601             if (QMenuBar *mb = qobject_cast<QMenuBar*>(QApplication::focusWidget())) {
2602                 mb->d_func()->setKeyboardMode(false);
2603             }
2604 #endif
2605         }
2606         break;
2607
2608     case Qt::Key_Escape:
2609 #ifdef QT_KEYPAD_NAVIGATION
2610     case Qt::Key_Back:
2611 #endif
2612         key_consumed = true;
2613         if (d->tornoff) {
2614             close();
2615             return;
2616         }
2617         {
2618             QPointer<QWidget> caused = d->causedPopup.widget;
2619             d->hideMenu(this); // hide after getting causedPopup
2620 #ifndef QT_NO_MENUBAR
2621             if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
2622                 mb->d_func()->setCurrentAction(d->menuAction);
2623                 mb->d_func()->setKeyboardMode(true);
2624             }
2625 #endif
2626         }
2627         break;
2628
2629     case Qt::Key_Space:
2630         if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem, 0, this))
2631             break;
2632         // for motif, fall through
2633 #ifdef QT_KEYPAD_NAVIGATION
2634     case Qt::Key_Select:
2635 #endif
2636     case Qt::Key_Return:
2637     case Qt::Key_Enter: {
2638         if (!d->currentAction) {
2639             d->setFirstActionActive();
2640             key_consumed = true;
2641             break;
2642         }
2643
2644         d->setSyncAction();
2645
2646         if (d->currentAction->menu())
2647             d->popupAction(d->currentAction, 0, true);
2648         else
2649             d->activateAction(d->currentAction, QAction::Trigger);
2650         key_consumed = true;
2651         break; }
2652
2653 #ifndef QT_NO_WHATSTHIS
2654     case Qt::Key_F1:
2655         if (!d->currentAction || d->currentAction->whatsThis().isNull())
2656             break;
2657         QWhatsThis::enterWhatsThisMode();
2658         d->activateAction(d->currentAction, QAction::Trigger);
2659         return;
2660 #endif
2661     default:
2662         key_consumed = false;
2663     }
2664
2665     if (!key_consumed) {                                // send to menu bar
2666         if ((!e->modifiers() || e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ShiftModifier) &&
2667            e->text().length()==1) {
2668             bool activateAction = false;
2669             QAction *nextAction = 0;
2670             if (style()->styleHint(QStyle::SH_Menu_KeyboardSearch, 0, this) && !e->modifiers()) {
2671                 int best_match_count = 0;
2672                 d->searchBufferTimer.start(2000, this);
2673                 d->searchBuffer += e->text();
2674                 for(int i = 0; i < d->actions.size(); ++i) {
2675                     int match_count = 0;
2676                     if (d->actionRects.at(i).isNull())
2677                         continue;
2678                     QAction *act = d->actions.at(i);
2679                     const QString act_text = act->text();
2680                     for(int c = 0; c < d->searchBuffer.size(); ++c) {
2681                         if(act_text.indexOf(d->searchBuffer.at(c), 0, Qt::CaseInsensitive) != -1)
2682                             ++match_count;
2683                     }
2684                     if(match_count > best_match_count) {
2685                         best_match_count = match_count;
2686                         nextAction = act;
2687                     }
2688                 }
2689             }
2690 #ifndef QT_NO_SHORTCUT
2691             else {
2692                 int clashCount = 0;
2693                 QAction *first = 0, *currentSelected = 0, *firstAfterCurrent = 0;
2694                 QChar c = e->text().at(0).toUpper();
2695                 for(int i = 0; i < d->actions.size(); ++i) {
2696                     if (d->actionRects.at(i).isNull())
2697                         continue;
2698                     QAction *act = d->actions.at(i);
2699                     QKeySequence sequence = QKeySequence::mnemonic(act->text());
2700                     int key = sequence[0] & 0xffff;
2701                     if (key == c.unicode()) {
2702                         clashCount++;
2703                         if (!first)
2704                             first = act;
2705                         if (act == d->currentAction)
2706                             currentSelected = act;
2707                         else if (!firstAfterCurrent && currentSelected)
2708                             firstAfterCurrent = act;
2709                     }
2710                 }
2711                 if (clashCount == 1)
2712                     activateAction = true;
2713                 if (clashCount >= 1) {
2714                     if (clashCount == 1 || !currentSelected || !firstAfterCurrent)
2715                         nextAction = first;
2716                     else
2717                         nextAction = firstAfterCurrent;
2718                 }
2719             }
2720 #endif
2721             if (nextAction) {
2722                 key_consumed = true;
2723                 if(d->scroll)
2724                     d->scrollMenu(nextAction, QMenuPrivate::QMenuScroller::ScrollCenter, false);
2725                 d->setCurrentAction(nextAction, 20, QMenuPrivate::SelectedFromElsewhere, true);
2726                 if (!nextAction->menu() && activateAction) {
2727                     d->setSyncAction();
2728                     d->activateAction(nextAction, QAction::Trigger);
2729                 }
2730             }
2731         }
2732         if (!key_consumed) {
2733 #ifndef QT_NO_MENUBAR
2734             if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->topCausedWidget())) {
2735                 QAction *oldAct = mb->d_func()->currentAction;
2736                 QApplication::sendEvent(mb, e);
2737                 if (mb->d_func()->currentAction != oldAct)
2738                     key_consumed = true;
2739             }
2740 #endif
2741         }
2742
2743 #ifdef Q_OS_WIN32
2744         if (key_consumed && (e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta))
2745             QApplication::beep();
2746 #endif // Q_OS_WIN32
2747     }
2748     if (key_consumed)
2749         e->accept();
2750     else
2751         e->ignore();
2752 }
2753
2754 /*!
2755   \reimp
2756 */
2757 void QMenu::mouseMoveEvent(QMouseEvent *e)
2758 {
2759     Q_D(QMenu);
2760     if (!isVisible() || d->aboutToHide || d->mouseEventTaken(e))
2761         return;
2762     d->motions++;
2763     if (d->motions == 0) // ignore first mouse move event (see enterEvent())
2764         return;
2765     d->hasHadMouse = d->hasHadMouse || rect().contains(e->pos());
2766
2767     QAction *action = d->actionAt(e->pos());
2768     if (!action) {
2769         if (d->hasHadMouse
2770             && (!d->currentAction
2771                 || !(d->currentAction->menu() && d->currentAction->menu()->isVisible())))
2772             d->setCurrentAction(0);
2773         return;
2774     } else if(e->buttons()) {
2775         d->mouseDown = this;
2776     }
2777     if (d->sloppyRegion.contains(e->pos())) {
2778         d->sloppyAction = action;
2779         QMenuPrivate::sloppyDelayTimer = startTimer(style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this)*6);
2780     } else if (action != d->currentAction) {
2781         d->setCurrentAction(action, style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this));
2782     }
2783 }
2784
2785 /*!
2786   \reimp
2787 */
2788 void QMenu::enterEvent(QEvent *)
2789 {
2790     d_func()->motions = -1; // force us to ignore the generate mouse move in mouseMoveEvent()
2791 }
2792
2793 /*!
2794   \reimp
2795 */
2796 void QMenu::leaveEvent(QEvent *)
2797 {
2798     Q_D(QMenu);
2799     d->sloppyAction = 0;
2800     if (!d->sloppyRegion.isEmpty())
2801         d->sloppyRegion = QRegion();
2802     if (!d->activeMenu && d->currentAction)
2803         setActiveAction(0);
2804 }
2805
2806 /*!
2807   \reimp
2808 */
2809 void
2810 QMenu::timerEvent(QTimerEvent *e)
2811 {
2812     Q_D(QMenu);
2813     if (d->scroll && d->scroll->scrollTimer.timerId() == e->timerId()) {
2814         d->scrollMenu((QMenuPrivate::QMenuScroller::ScrollDirection)d->scroll->scrollDirection);
2815         if (d->scroll->scrollFlags == QMenuPrivate::QMenuScroller::ScrollNone)
2816             d->scroll->scrollTimer.stop();
2817     } else if(d->menuDelayTimer.timerId() == e->timerId()) {
2818         d->menuDelayTimer.stop();
2819         internalDelayedPopup();
2820     } else if(QMenuPrivate::sloppyDelayTimer == e->timerId()) {
2821         killTimer(QMenuPrivate::sloppyDelayTimer);
2822         QMenuPrivate::sloppyDelayTimer = 0;
2823         internalSetSloppyAction();
2824     } else if(d->searchBufferTimer.timerId() == e->timerId()) {
2825         d->searchBuffer.clear();
2826     }
2827 }
2828
2829 void copyActionToPlatformItem(const QAction *action, QPlatformMenuItem* item)
2830 {
2831     item->setText(action->text());
2832     item->setIsSeparator(action->isSeparator());
2833     if (action->isIconVisibleInMenu())
2834         item->setIcon(action->icon());
2835     item->setVisible(action->isVisible());
2836     item->setShortcut(action->shortcut());
2837     item->setChecked(action->isChecked());
2838     item->setFont(action->font());
2839     item->setRole((QPlatformMenuItem::MenuRole) action->menuRole());
2840     item->setEnabled(action->isEnabled());
2841
2842     if (action->menu()) {
2843         item->setMenu(action->menu()->platformMenu());
2844     } else {
2845         item->setMenu(0);
2846     }
2847 }
2848
2849 /*!
2850   \reimp
2851 */
2852 void QMenu::actionEvent(QActionEvent *e)
2853 {
2854     Q_D(QMenu);
2855     d->itemsDirty = 1;
2856     setAttribute(Qt::WA_Resized, false);
2857     if (d->tornPopup)
2858         d->tornPopup->syncWithMenu(this, e);
2859     if (e->type() == QEvent::ActionAdded) {
2860         if(!d->tornoff) {
2861             connect(e->action(), SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
2862             connect(e->action(), SIGNAL(hovered()), this, SLOT(_q_actionHovered()));
2863         }
2864         if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
2865             QWidget *widget = wa->requestWidget(this);
2866             if (widget)
2867                 d->widgetItems.insert(wa, widget);
2868         }
2869     } else if (e->type() == QEvent::ActionRemoved) {
2870         e->action()->disconnect(this);
2871         if (e->action() == d->currentAction)
2872             d->currentAction = 0;
2873         if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
2874             if (QWidget *widget = d->widgetItems.value(wa))
2875                 wa->releaseWidget(widget);
2876         }
2877         d->widgetItems.remove(e->action());
2878     }
2879
2880     if (d->platformMenu) {
2881         if (e->type() == QEvent::ActionAdded) {
2882             QPlatformMenuItem *menuItem =
2883                 QGuiApplicationPrivate::platformTheme()->createPlatformMenuItem();
2884             menuItem->setTag(reinterpret_cast<quintptr>(e->action()));
2885             QObject::connect(menuItem, SIGNAL(activated()), e->action(), SLOT(trigger()));
2886             copyActionToPlatformItem(e->action(), menuItem);
2887             QPlatformMenuItem* beforeItem = d->platformMenu->menuItemForTag(reinterpret_cast<quintptr>(e->before()));
2888             d->platformMenu->insertMenuItem(menuItem, beforeItem);
2889         } else if (e->type() == QEvent::ActionRemoved) {
2890             QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(reinterpret_cast<quintptr>(e->action()));
2891             d->platformMenu->removeMenuItem(menuItem);
2892         } else if (e->type() == QEvent::ActionChanged) {
2893             QPlatformMenuItem *menuItem = d->platformMenu->menuItemForTag(reinterpret_cast<quintptr>(e->action()));
2894             copyActionToPlatformItem(e->action(), menuItem);
2895             d->platformMenu->syncMenuItem(menuItem);
2896         }
2897
2898         d->platformMenu->syncSeparatorsCollapsible(d->collapsibleSeparators);
2899     }
2900
2901 #if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR)
2902     if (!d->wce_menu)
2903         d->wce_menu = new QMenuPrivate::QWceMenuPrivate;
2904     if (e->type() == QEvent::ActionAdded)
2905         d->wce_menu->addAction(e->action(), d->wce_menu->findAction(e->before()));
2906     else if (e->type() == QEvent::ActionRemoved)
2907         d->wce_menu->removeAction(e->action());
2908     else if (e->type() == QEvent::ActionChanged)
2909         d->wce_menu->syncAction(e->action());
2910 #endif
2911
2912     if (isVisible()) {
2913         d->updateActionRects();
2914         resize(sizeHint());
2915         update();
2916     }
2917 }
2918
2919 /*!
2920   \internal
2921 */
2922 void QMenu::internalSetSloppyAction()
2923 {
2924     if (d_func()->sloppyAction)
2925         d_func()->setCurrentAction(d_func()->sloppyAction, 0);
2926 }
2927
2928 /*!
2929   \internal
2930 */
2931 void QMenu::internalDelayedPopup()
2932 {
2933     Q_D(QMenu);
2934
2935     //hide the current item
2936     if (QMenu *menu = d->activeMenu) {
2937         d->activeMenu = 0;
2938         d->hideMenu(menu);
2939     }
2940
2941     if (!d->currentAction || !d->currentAction->isEnabled() || !d->currentAction->menu() ||
2942         !d->currentAction->menu()->isEnabled() || d->currentAction->menu()->isVisible())
2943         return;
2944
2945     //setup
2946     d->activeMenu = d->currentAction->menu();
2947     d->activeMenu->d_func()->causedPopup.widget = this;
2948     d->activeMenu->d_func()->causedPopup.action = d->currentAction;
2949
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())));
2954
2955     QPoint pos(rightPos);
2956
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))) {
2961             QPoint pts[4];
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());
2967             } else {
2968                 pts[1] = QPoint(pos.x() + menuSize.width(), pos.y());
2969                 pts[2] = QPoint(pos.x() + menuSize.width(), pos.y() + menuSize.height());
2970             }
2971             QPolygon points(4);
2972             for(int i = 0; i < 4; i++)
2973                 points.setPoint(i, mapFromGlobal(pts[i]));
2974             d->sloppyRegion = QRegion(points);
2975         }
2976     }
2977
2978     //do the popup
2979     d->activeMenu->popup(pos);
2980 }
2981
2982 /*!
2983     \fn void QMenu::addAction(QAction *action)
2984     \overload
2985
2986     Appends the action \a action to the menu's list of actions.
2987
2988     \sa QMenuBar::addAction(), QWidget::addAction()
2989 */
2990
2991 /*!
2992     \fn void QMenu::aboutToHide()
2993     \since 4.2
2994
2995     This signal is emitted just before the menu is hidden from the user.
2996
2997     \sa aboutToShow(), hide()
2998 */
2999
3000 /*!
3001     \fn void QMenu::aboutToShow()
3002
3003     This signal is emitted just before the menu is shown to the user.
3004
3005     \sa aboutToHide(), show()
3006 */
3007
3008 /*!
3009     \fn void QMenu::triggered(QAction *action)
3010
3011     This signal is emitted when an action in this menu is triggered.
3012
3013     \a action is the action that caused the signal to be emitted.
3014
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".
3019
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
3022     not be connected.
3023
3024     \sa hovered(), QAction::triggered()
3025 */
3026
3027 /*!
3028     \fn void QMenu::hovered(QAction *action)
3029
3030     This signal is emitted when a menu action is highlighted; \a action
3031     is the action that caused the signal to be emitted.
3032
3033     Often this is used to update status information.
3034
3035     \sa triggered(), QAction::hovered()
3036 */
3037
3038
3039 /*!\internal
3040 */
3041 void QMenu::setNoReplayFor(QWidget *noReplayFor)
3042 {
3043 #ifdef Q_OS_WIN
3044     d_func()->noReplayFor = noReplayFor;
3045 #else
3046     Q_UNUSED(noReplayFor);
3047 #endif
3048 }
3049
3050 /*!\internal
3051 */
3052 QPlatformMenu *QMenu::platformMenu()
3053 {
3054
3055     return d_func()->platformMenu;
3056 }
3057
3058 /*!
3059   \property QMenu::separatorsCollapsible
3060   \since 4.2
3061
3062   \brief whether consecutive separators should be collapsed
3063
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.
3067
3068   By default, this property is true.
3069 */
3070 bool QMenu::separatorsCollapsible() const
3071 {
3072     Q_D(const QMenu);
3073     return d->collapsibleSeparators;
3074 }
3075
3076 void QMenu::setSeparatorsCollapsible(bool collapse)
3077 {
3078     Q_D(QMenu);
3079     if (d->collapsibleSeparators == collapse)
3080         return;
3081
3082     d->collapsibleSeparators = collapse;
3083     d->itemsDirty = 1;
3084     if (isVisible()) {
3085         d->updateActionRects();
3086         update();
3087     }
3088     if (d->platformMenu)
3089         d->platformMenu->syncSeparatorsCollapsible(collapse);
3090 }
3091
3092 QT_END_NAMESPACE
3093
3094 // for private slots
3095 #include "moc_qmenu.cpp"
3096 #include "qmenu.moc"
3097
3098 #endif // QT_NO_MENU