~ubuntu-branches/ubuntu/wily/qtbase-opensource-src/wily

« back to all changes in this revision

Viewing changes to src/widgets/widgets/qmenu.cpp

  • Committer: Package Import Robot
  • Author(s): Timo Jyrinki
  • Date: 2013-02-05 12:46:17 UTC
  • Revision ID: package-import@ubuntu.com-20130205124617-c8jouts182j002fx
Tags: upstream-5.0.1+dfsg
ImportĀ upstreamĀ versionĀ 5.0.1+dfsg

Show diffs side-by-side

added added

removed removed

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