source: trunk/src/gui/widgets/qmenu.cpp@ 943

Last change on this file since 943 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

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