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