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