Ignore:
Timestamp:
Feb 11, 2010, 11:19:06 PM (15 years ago)
Author:
Dmitry A. Kuminov
Message:

trunk: Merged in qt 4.6.1 sources.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk

  • trunk/src/gui/widgets/qmenu.cpp

    r305 r561  
    22**
    33** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
    4 ** Contact: Qt Software Information (qt-info@nokia.com)
     4** All rights reserved.
     5** Contact: Nokia Corporation (qt-info@nokia.com)
    56**
    67** This file is part of the QtGui module of the Qt Toolkit.
     
    2122** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
    2223**
    23 ** In addition, as a special exception, Nokia gives you certain
    24 ** additional rights. These rights are described in the Nokia Qt LGPL
    25 ** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
    26 ** package.
     24** In addition, as a special exception, Nokia gives you certain additional
     25** rights.  These rights are described in the Nokia Qt LGPL Exception
     26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
    2727**
    2828** GNU General Public License Usage
     
    3434** met: http://www.gnu.org/copyleft/gpl.html.
    3535**
    36 ** If you are unsure which license is appropriate for your use, please
    37 ** contact the sales department at qt-sales@nokia.com.
     36** If you have questions regarding the use of this file, please contact
     37** Nokia at qt-info@nokia.com.
    3838** $QT_END_LICENSE$
    3939**
     
    6666#include "qwidgetaction.h"
    6767#include "qtoolbutton.h"
     68#include "qpushbutton.h"
     69#include <private/qpushbutton_p.h>
    6870#include <private/qaction_p.h>
     71#include <private/qsoftkeymanager_p.h>
    6972#ifdef QT3_SUPPORT
    7073#include <qmenudata.h>
     
    151154{
    152155    Q_Q(QMenu);
    153     activationRecursionGuard = false;
    154156#ifndef QT_NO_WHATSTHIS
    155157    q->setAttribute(Qt::WA_CustomWhatsThis);
     
    163165        scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
    164166    }
     167
     168#ifdef QT_SOFTKEYS_ENABLED
     169    selectAction = QSoftKeyManager::createKeyedAction(QSoftKeyManager::SelectSoftKey, Qt::Key_Select, q);
     170    cancelAction = QSoftKeyManager::createKeyedAction(QSoftKeyManager::CancelSoftKey, Qt::Key_Back, q);
     171    selectAction->setVisible(false); // Don't show these in the menu
     172    cancelAction->setVisible(false);
     173    q->addAction(selectAction);
     174    q->addAction(cancelAction);
     175#endif
     176}
     177
     178int QMenuPrivate::scrollerHeight() const
     179{
     180    Q_Q(const QMenu);
     181    return qMax(QApplication::globalStrut().height(), q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q));
     182}
     183
     184// Windows, OS/2 and KDE allows menus to cover the taskbar, while GNOME and Mac don't
     185QRect QMenuPrivate::popupGeometry(const QWidget *widget) const
     186{
     187#if defined(Q_WS_WIN) || defined(Q_WS_PM)
     188    return QApplication::desktop()->screenGeometry(widget);
     189#elif defined Q_WS_X11
     190    if (X11->desktopEnvironment == DE_KDE)
     191        return QApplication::desktop()->screenGeometry(widget);
     192    else
     193        return QApplication::desktop()->availableGeometry(widget);
     194#else
     195        return QApplication::desktop()->availableGeometry(widget);
     196#endif
    165197}
    166198
     
    195227}
    196228
    197 void QMenuPrivate::calcActionRects(QMap<QAction*, QRect> &actionRects, QList<QAction*> &actionList) const
     229void QMenuPrivate::updateActionRects() const
    198230{
    199231    Q_Q(const QMenu);
    200     if (!itemsDirty) {
    201         actionRects = this->actionRects;
    202         actionList = this->actionList;
     232    if (!itemsDirty)
    203233        return;
    204     }
    205 
    206     actionRects.clear();
    207     actionList.clear();
    208     QList<QAction*> items = filterActions(q->actions());
     234
     235    q->ensurePolished();
     236
     237    //let's reinitialize the buffer
     238    actionRects.resize(actions.count());
     239    actionRects.fill(QRect());
     240
     241    //let's try to get the last visible action
     242    int lastVisibleAction = actions.count() - 1;
     243    for(;lastVisibleAction >= 0; --lastVisibleAction) {
     244        const QAction *action = actions.at(lastVisibleAction);
     245        if (action->isVisible()) {
     246            //removing trailing separators
     247            if (action->isSeparator() && collapsibleSeparators)
     248                continue;
     249            break;
     250        }
     251    }
     252
    209253    int max_column_width = 0,
    210         dh = popupGeometry(QApplication::desktop()->screenNumber(q)).height(),
    211         ncols = 1,
     254        dh = popupGeometry(q).height(),
    212255        y = 0;
    213     const int hmargin = q->style()->pixelMetric(QStyle::PM_MenuHMargin, 0, q),
    214               vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q),
    215               icone = q->style()->pixelMetric(QStyle::PM_SmallIconSize, 0, q);
     256    QStyle *style = q->style();
     257    QStyleOption opt;
     258    opt.init(q);
     259    const int hmargin = style->pixelMetric(QStyle::PM_MenuHMargin, &opt, q),
     260              vmargin = style->pixelMetric(QStyle::PM_MenuVMargin, &opt, q),
     261              icone = style->pixelMetric(QStyle::PM_SmallIconSize, &opt, q);
     262    const int fw = style->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, q);
     263    const int deskFw = style->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, &opt, q);
     264
     265    const int sfcMargin = style->sizeFromContents(QStyle::CT_Menu, &opt, QApplication::globalStrut(), q).width() - QApplication::globalStrut().width();
     266    const int min_column_width = q->minimumWidth() - (sfcMargin + leftmargin + rightmargin + 2 * (fw + hmargin));
     267    const int tearoffHeight = tearoff ? style->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, q) : 0;
    216268
    217269    //for compatability now - will have to refactor this away..
     
    219271    maxIconWidth = 0;
    220272    hasCheckableItems = false;
    221     for(int i = 0; i < items.count(); i++) {
    222         QAction *action = items.at(i);
    223         if (widgetItems.value(action))
     273    ncols = 1;
     274    sloppyAction = 0;
     275
     276    for (int i = 0; i < actions.count(); ++i) {
     277        QAction *action = actions.at(i);
     278        if (action->isSeparator() || !action->isVisible() || widgetItems.contains(action))
    224279            continue;
     280        //..and some members
    225281        hasCheckableItems |= action->isCheckable();
    226282        QIcon is = action->icon();
    227283        if (!is.isNull()) {
    228             uint miw = maxIconWidth;
    229             maxIconWidth = qMax<uint>(miw, icone + 4);
     284            maxIconWidth = qMax<uint>(maxIconWidth, icone + 4);
    230285        }
    231286    }
     
    233288    //calculate size
    234289    QFontMetrics qfm = q->fontMetrics();
    235     for(int i = 0; i < items.count(); i++) {
    236         QAction *action = items.at(i);
    237 
    238         QFontMetrics fm(action->font().resolve(q->font()));
    239         QSize sz;
     290    bool previousWasSeparator = true; // this is true to allow removing the leading separators
     291    for(int i = 0; i <= lastVisibleAction; i++) {
     292        QAction *action = actions.at(i);
     293
     294        if (!action->isVisible() ||
     295            (collapsibleSeparators && previousWasSeparator && action->isSeparator()))
     296            continue; // we continue, this action will get an empty QRect
     297
     298        previousWasSeparator = action->isSeparator();
    240299
    241300        //let the style modify the above size..
    242301        QStyleOptionMenuItem opt;
    243302        q->initStyleOption(&opt, action);
    244         opt.rect = q->rect();
    245 
     303        const QFontMetrics &fm = opt.fontMetrics;
     304
     305        QSize sz;
    246306        if (QWidget *w = widgetItems.value(action)) {
    247           sz=w->sizeHint().expandedTo(w->minimumSize()).expandedTo(w->minimumSizeHint()).boundedTo(w->maximumSize());
     307          sz = w->sizeHint().expandedTo(w->minimumSize()).expandedTo(w->minimumSizeHint()).boundedTo(w->maximumSize());
    248308        } else {
    249309            //calc what I think the size is..
     
    263323    #endif
    264324                }
    265                 int w = fm.boundingRect(QRect(), Qt::TextSingleLine, s).width();
    266                 w -= s.count(QLatin1Char('&')) * fm.width(QLatin1Char('&'));
    267                 w += s.count(QLatin1String("&&")) * fm.width(QLatin1Char('&'));
    268                 sz.setWidth(w);
     325                sz.setWidth(fm.boundingRect(QRect(), Qt::TextSingleLine | Qt::TextShowMnemonic, s).width());
    269326                sz.setHeight(qMax(fm.height(), qfm.height()));
    270327
     
    276333                }
    277334            }
    278           sz = q->style()->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, q);
     335            sz = style->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, q);
    279336        }
    280337
    281338
    282339        if (!sz.isEmpty()) {
    283             max_column_width = qMax(max_column_width, sz.width());
     340            max_column_width = qMax(min_column_width, qMax(max_column_width, sz.width()));
    284341            //wrapping
    285342            if (!scroll &&
    286                y+sz.height()+vmargin > dh - (q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q) * 2)) {
     343               y+sz.height()+vmargin > dh - (deskFw * 2)) {
    287344                ncols++;
    288345                y = vmargin;
    289346            }
    290347            y += sz.height();
    291             //append item
    292             actionRects.insert(action, QRect(0, 0, sz.width(), sz.height()));
    293             actionList.append(action);
    294         }
    295     }
    296 
    297     if (tabWidth)
    298         max_column_width += tabWidth; //finally add in the tab width
     348            //update the item
     349            actionRects[i] = QRect(0, 0, sz.width(), sz.height());
     350        }
     351    }
     352
     353    max_column_width += tabWidth; //finally add in the tab width
    299354
    300355    //calculate position
    301     int x = hmargin;
    302     y = vmargin;
    303 
    304     for(int i = 0; i < actionList.count(); i++) {
    305         QAction *action = actionList.at(i);
    306         QRect &rect = actionRects[action];
     356    const int base_y = vmargin + fw + topmargin +
     357        (scroll ? scroll->scrollOffset : 0) +
     358        tearoffHeight;
     359    int x = hmargin + fw + leftmargin;
     360    y = base_y;
     361
     362    for(int i = 0; i < actions.count(); i++) {
     363        QRect &rect = actionRects[i];
    307364        if (rect.isNull())
    308365            continue;
    309366        if (!scroll &&
    310            y+rect.height() > dh - (q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q) * 2)) {
    311             ncols--;
    312             if (ncols < 0)
    313                 qWarning("QMenu: Column calculation mismatch (%d)", ncols);
     367           y+rect.height() > dh - deskFw * 2) {
    314368            x += max_column_width + hmargin;
    315             y = vmargin;
     369            y = base_y;
    316370        }
    317371        rect.translate(x, y);                        //move
    318372        rect.setWidth(max_column_width); //uniform width
     373
     374        //we need to update the widgets geometry
     375        if (QWidget *widget = widgetItems.value(actions.at(i))) {
     376            widget->setGeometry(rect);
     377            widget->setVisible(actions.at(i)->isVisible());
     378        }
     379
    319380        y += rect.height();
    320381    }
    321 }
    322 
    323 void QMenuPrivate::updateActions()
    324 {
    325     Q_Q(const QMenu);
    326     if (!itemsDirty)
    327         return;
    328     sloppyAction = 0;
    329     calcActionRects(actionRects, actionList);
    330     for (QHash<QAction *, QWidget *>::ConstIterator item = widgetItems.constBegin(),
    331          end = widgetItems.constEnd(); item != end; ++item) {
    332         QAction *action = item.key();
    333         QWidget *widget = item.value();
    334         widget->setGeometry(actionRect(action));
    335         widget->setVisible(action->isVisible());
    336     }
    337     ncols = 1;
    338     int last_left = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
    339     if (!scroll) {
    340         for(int i = 0; i < actionList.count(); i++) {
    341             int left = actionRects.value(actionList.at(i)).left();
    342             if (left > last_left) {
    343                 last_left = left;
    344                 ncols++;
    345             }
    346         }
    347     }
    348382    itemsDirty = 0;
    349383}
    350384
    351 QList<QAction *> QMenuPrivate::filterActions(const QList<QAction *> &actions) const
    352 {
    353     QList<QAction *> visibleActions;
    354     int i = 0;
    355     while (i < actions.count()) {
    356         QAction *action = actions.at(i);
    357         if (!action->isVisible()) {
    358             ++i;
    359             continue;
    360         }
    361         if (!action->isSeparator() || !collapsibleSeparators) {
    362             visibleActions.append(action);
    363             ++i;
    364             continue;
    365         }
    366 
    367         // no leading separators
    368         if (!visibleActions.isEmpty())
    369             visibleActions.append(action);
    370 
    371         // skip double/tripple/etc. separators
    372         while (i < actions.count()
    373                && (!actions.at(i)->isVisible() || actions.at(i)->isSeparator()))
    374             ++i;
    375     }
    376 
    377     if (collapsibleSeparators) {
    378         // remove trailing separators
    379         while (!visibleActions.isEmpty() && visibleActions.last()->isSeparator())
    380             visibleActions.removeLast();
    381     }
    382 
    383     return visibleActions;
    384 }
    385 
    386385QRect QMenuPrivate::actionRect(QAction *act) const
    387386{
    388     Q_Q(const QMenu);
    389     QRect ret = actionRects.value(act);
    390     if (ret.isNull())
    391         return ret;
    392     if (scroll)
    393         ret.translate(0, scroll->scrollOffset);
    394     if (tearoff)
    395         ret.translate(0, q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q));
    396     const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
    397     ret.translate(fw+leftmargin, fw+topmargin);
    398     return ret;
    399 }
     387    int index = actions.indexOf(act);
     388    if (index == -1)
     389        return QRect();
     390
     391    updateActionRects();
     392
     393    //we found the action
     394    return actionRects.at(index);
     395}
     396
     397#if defined(Q_WS_MAC)
     398static const qreal MenuFadeTimeInSec = 0.150;
     399#endif
    400400
    401401void QMenuPrivate::hideUpToMenuBar()
    402402{
    403403    Q_Q(QMenu);
     404    bool fadeMenus = q->style()->styleHint(QStyle::SH_Menu_FadeOutOnHide);
    404405    if (!tornoff) {
    405406        QWidget *caused = causedPopup.widget;
     
    416417                caused = m->d_func()->causedPopup.widget;
    417418                if (!m->d_func()->tornoff)
    418                     hideMenu(m);
    419                 m->d_func()->setCurrentAction(0);
    420             } else {
    421 #ifndef QT_NO_TOOLBUTTON
    422                 if (qobject_cast<QToolButton*>(caused) == 0)
    423 #endif
    424                     qWarning("QMenu: Internal error");
    425                 caused = 0;
     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;
    426423            }
    427424        }
     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
    428433    }
    429434    setCurrentAction(0);
    430435}
    431436
    432 void QMenuPrivate::hideMenu(QMenu *menu)
     437void QMenuPrivate::hideMenu(QMenu *menu, bool justRegister)
    433438{
    434439    if (!menu)
    435440        return;
    436 
    437441#if !defined(QT_NO_EFFECTS)
    438442    menu->blockSignals(true);
     
    440444    // Flash item which is about to trigger (if any).
    441445    if (menu->style()->styleHint(QStyle::SH_Menu_FlashTriggeredItem)
    442         && currentAction && currentAction == actionAboutToTrigger) {
    443 
     446        && currentAction && currentAction == actionAboutToTrigger
     447        && menu->actions().contains(currentAction)) {
    444448        QEventLoop eventLoop;
    445449        QAction *activeAction = currentAction;
    446450
    447         // Deselect and wait 60 ms.
    448451        menu->setActiveAction(0);
    449452        QTimer::singleShot(60, &eventLoop, SLOT(quit()));
     
    459462    if (menu->style()->styleHint(QStyle::SH_Menu_FadeOutOnHide)) {
    460463        // ### Qt 4.4:
    461         // Should be something like: q->transitionWindow(Qt::FadeOutTransition, 150);
     464        // Should be something like: q->transitionWindow(Qt::FadeOutTransition, MenuFadeTimeInSec);
    462465        // Hopefully we'll integrate qt/research/windowtransitions into main before 4.4.
    463466        // Talk to Richard, Trenton or Bjoern.
    464467#if defined(Q_WS_MAC)
    465                 macWindowFade(qt_mac_window_for(menu));         // FIXME - what is the default duration for view animations
    466 
    467         // Wait for the transition to complete.
    468                 QEventLoop eventLoop;
    469         QTimer::singleShot(150, &eventLoop, SLOT(quit()));
    470         eventLoop.exec();
     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
    471475#endif // Q_WS_MAC
    472476    }
     
    474478    menu->blockSignals(false);
    475479#endif // QT_NO_EFFECTS
    476     menu->hide();
     480    if (!justRegister)
     481        menu->hide();
    477482}
    478483
     
    514519{
    515520    Q_Q(QMenu);
    516     const int scrollerHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q);
    517     for(int i = 0, saccum = 0; i < actionList.count(); i++) {
    518         QAction *act = actionList[i];
     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;
    519526        if (scroll && scroll->scrollFlags & QMenuScroller::ScrollUp) {
    520             saccum -= actionRects.value(act).height();
    521             if (saccum > scroll->scrollOffset-scrollerHeight)
     527            saccum -= rect.height();
     528            if (saccum > scroll->scrollOffset - scrollerHeight())
    522529                continue;
    523530        }
     531        QAction *act = actions.at(i);
    524532        if (!act->isSeparator() &&
    525533           (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
     
    536544    Q_Q(QMenu);
    537545    tearoffHighlighted = 0;
    538     if (action == currentAction && !(action && action->menu() && action->menu() != activeMenu)) {
    539         if(QMenu *menu = qobject_cast<QMenu*>(causedPopup.widget)) {
    540             if(causedPopup.action && menu->d_func()->activeMenu == q)
    541                 menu->d_func()->setCurrentAction(causedPopup.action, 0, reason, false);
     546    if (action == currentAction) {
     547        if (!action || !action->menu() || action->menu() == activeMenu) {
     548            if(QMenu *menu = qobject_cast<QMenu*>(causedPopup.widget)) {
     549                if(causedPopup.action && menu->d_func()->activeMenu == q)
     550                    menu->d_func()->setCurrentAction(causedPopup.action, 0, reason, false);
     551            }
    542552        }
    543553        return;
     
    554564#endif
    555565#ifdef QT3_SUPPORT
    556     emitHighlighted = (action && action != currentAction);
     566    emitHighlighted = action;
    557567#endif
    558568    currentAction = action;
     
    569579            }
    570580            q->update(actionRect(action));
    571             QWidget *widget = widgetItems.value(action);
    572581
    573582            if (reason == SelectedFromKeyboard) {
     583                QWidget *widget = widgetItems.value(action);
    574584                if (widget) {
    575585                    if (widget->focusPolicy() != Qt::NoFocus)
     
    579589                    // get the focus
    580590                    // Since the menu is a pop-up, it uses the popup reason.
    581                     if (!q->hasFocus())
     591                    if (!q->hasFocus()) {
    582592                        q->setFocus(Qt::PopupFocusReason);
     593                    }
    583594                }
    584595            }
     
    589600#ifndef QT_NO_STATUSTIP
    590601    }  else if (previousAction) {
    591         QWidget *w = causedPopup.widget;
    592         while (QMenu *m = qobject_cast<QMenu*>(w))
    593             w = m->d_func()->causedPopup.widget;
    594         if (w) {
    595             QString empty;
    596             QStatusTipEvent tip(empty);
    597             QApplication::sendEvent(w, &tip);
    598         }
     602        previousAction->d_func()->showStatusText(topCausedWidget(), QString());
    599603#endif
    600604    }
     
    610614}
    611615
     616//return the top causedPopup.widget that is not a QMenu
     617QWidget *QMenuPrivate::topCausedWidget() const
     618{
     619    QWidget* top = causedPopup.widget;
     620    while (QMenu* m = qobject_cast<QMenu *>(top))
     621        top = m->d_func()->causedPopup.widget;
     622    return top;
     623}
     624
    612625QAction *QMenuPrivate::actionAt(QPoint p) const
    613626{
     
    615628       return 0;
    616629
    617     for(int i = 0; i < actionList.count(); i++) {
    618         QAction *act = actionList[i];
    619         if (actionRect(act).contains(p))
    620             return act;
     630    for(int i = 0; i < actionRects.count(); i++) {
     631        if (actionRects.at(i).contains(p))
     632            return actions.at(i);
    621633    }
    622634    return 0;
     
    640652}
    641653
     654
     655void QMenuPrivate::updateLayoutDirection()
     656{
     657    Q_Q(QMenu);
     658    //we need to mimic the cause of the popup's layout direction
     659    //to allow setting it on a mainwindow for example
     660    //we call setLayoutDirection_helper to not overwrite a user-defined value
     661    if (!q->testAttribute(Qt::WA_SetLayoutDirection)) {
     662        if (QWidget *w = causedPopup.widget)
     663            setLayoutDirection_helper(w->layoutDirection());
     664        else if (QWidget *w = q->parentWidget())
     665            setLayoutDirection_helper(w->layoutDirection());
     666        else
     667            setLayoutDirection_helper(QApplication::layoutDirection());
     668    }
     669}
     670
     671
    642672/*!
    643673    Returns the action associated with this menu.
     
    692722    if (!scroll || !scroll->scrollFlags)
    693723        return;
     724    updateActionRects();
    694725    int newOffset = 0;
    695     const int scrollHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q);
    696     const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp)   ? scrollHeight : 0;
    697     const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollHeight : 0;
     726    const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp)   ? scrollerHeight() : 0;
     727    const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollerHeight() : 0;
    698728    const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
    699729    const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
    700730
    701731    if (location == QMenuScroller::ScrollTop) {
    702         for(int i = 0, saccum = 0; i < actionList.count(); i++) {
    703             QAction *act = actionList.at(i);
    704             if (act == action) {
     732        for(int i = 0, saccum = 0; i < actions.count(); i++) {
     733            if (actions.at(i) == action) {
    705734                newOffset = topScroll - saccum;
    706735                break;
    707736            }
    708             saccum += actionRects.value(act).height();
     737            saccum += actionRects.at(i).height();
    709738        }
    710739    } else {
    711         for(int i = 0, saccum = 0; i < actionList.count(); i++) {
    712             QAction *act = actionList.at(i);
    713             saccum += actionRects.value(act).height();
    714             if (act == action) {
     740        for(int i = 0, saccum = 0; i < actions.count(); i++) {
     741            saccum += actionRects.at(i).height();
     742            if (actions.at(i) == action) {
    715743                if (location == QMenuScroller::ScrollCenter)
    716744                    newOffset = ((q->height() / 2) - botScroll) - (saccum - topScroll);
     
    721749        }
    722750        if(newOffset)
    723             newOffset -= fw*2;
     751            newOffset -= fw * 2;
    724752    }
    725753
     
    729757        newScrollFlags |= QMenuScroller::ScrollUp;
    730758    int saccum = newOffset;
    731     for(int i = 0; i < actionList.count(); i++) {
    732         saccum += actionRects.value(actionList.at(i)).height();
     759    for(int i = 0; i < actionRects.count(); i++) {
     760        saccum += actionRects.at(i).height();
    733761        if (saccum > q->height()) {
    734762            newScrollFlags |= QMenuScroller::ScrollDown;
     
    748776        newOffset -= vmargin;
    749777
    750     QRect screen = popupGeometry(QApplication::desktop()->screenNumber(q));
     778    QRect screen = popupGeometry(q);
    751779    const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q);
    752780    if (q->height() < screen.height()-(desktopFrame*2)-1) {
     
    781809
    782810    //actually update flags
    783     scroll->scrollOffset = newOffset;
    784     if (scroll->scrollOffset > 0)
    785         scroll->scrollOffset = 0;
     811    const int delta = qMin(0, newOffset) - scroll->scrollOffset; //make sure the new offset is always negative
     812    if (!itemsDirty && delta) {
     813        //we've scrolled so we need to update the action rects
     814        for (int i = 0; i < actionRects.count(); ++i) {
     815            QRect &current = actionRects[i];
     816            current.moveTop(current.top() + delta);
     817
     818            //we need to update the widgets geometry
     819            if (QWidget *w = widgetItems.value(actions.at(i)))
     820                w->setGeometry(current);
     821        }
     822    }
     823    scroll->scrollOffset += delta;
    786824    scroll->scrollFlags = newScrollFlags;
    787825    if (active)
     
    794832{
    795833    Q_Q(QMenu);
     834    updateActionRects();
    796835    if(location == QMenuScroller::ScrollBottom) {
    797         for(int i = actionList.size()-1; i >= 0; --i) {
    798             QAction *act = actionList.at(i);
     836        for(int i = actions.size()-1; i >= 0; --i) {
     837            QAction *act = actions.at(i);
     838            if (actionRects.at(i).isNull())
     839                continue;
    799840            if (!act->isSeparator() &&
    800841                (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
     
    808849        }
    809850    } else if(location == QMenuScroller::ScrollTop) {
    810         for(int i = 0; i < actionList.size(); ++i) {
    811             QAction *act = actionList.at(i);
     851        for(int i = 0; i < actions.size(); ++i) {
     852            QAction *act = actions.at(i);
     853            if (actionRects.at(i).isNull())
     854                continue;
    812855            if (!act->isSeparator() &&
    813856                (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
     
    829872    if (!scroll || !(scroll->scrollFlags & direction)) //not really possible...
    830873        return;
    831     const int scrollHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q);
    832     const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp)   ? scrollHeight : 0;
    833     const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollHeight : 0;
     874    updateActionRects();
     875    const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp)   ? scrollerHeight() : 0;
     876    const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollerHeight() : 0;
    834877    const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
    835878    const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
    836879    const int offset = topScroll ? topScroll-vmargin : 0;
    837880    if (direction == QMenuScroller::ScrollUp) {
    838         for(int i = 0, saccum = 0; i < actionList.count(); i++) {
    839             QAction *act = actionList.at(i);
    840             const int iHeight = actionRects.value(act).height();
    841             saccum -= iHeight;
     881        for(int i = 0, saccum = 0; i < actions.count(); i++) {
     882            saccum -= actionRects.at(i).height();
    842883            if (saccum <= scroll->scrollOffset-offset) {
    843                 scrollMenu(act, page ? QMenuScroller::ScrollBottom : QMenuScroller::ScrollTop, active);
     884                scrollMenu(actions.at(i), page ? QMenuScroller::ScrollBottom : QMenuScroller::ScrollTop, active);
    844885                break;
    845886            }
     
    847888    } else if (direction == QMenuScroller::ScrollDown) {
    848889        bool scrolled = false;
    849         for(int i = 0, saccum = 0; i < actionList.count(); i++) {
    850             QAction *act = actionList.at(i);
    851             const int iHeight = actionRects.value(act).height();
     890        for(int i = 0, saccum = 0; i < actions.count(); i++) {
     891            const int iHeight = actionRects.at(i).height();
    852892            saccum -= iHeight;
    853893            if (saccum <= scroll->scrollOffset-offset) {
    854894                const int scrollerArea = q->height() - botScroll - fw*2;
    855895                int visible = (scroll->scrollOffset-offset) - saccum;
    856                 for(i++ ; i < actionList.count(); i++) {
    857                     act = actionList.at(i);
    858                     const int iHeight = actionRects.value(act).height();
    859                     visible += iHeight;
     896                for(i++ ; i < actions.count(); i++) {
     897                    visible += actionRects.at(i).height();
    860898                    if (visible > scrollerArea - topScroll) {
    861899                        scrolled = true;
    862                         scrollMenu(act, page ? QMenuScroller::ScrollTop : QMenuScroller::ScrollBottom, active);
     900                        scrollMenu(actions.at(i), page ? QMenuScroller::ScrollTop : QMenuScroller::ScrollBottom, active);
    863901                        break;
    864902                    }
     
    883921        bool isScroll = false;
    884922        if (pos.x() >= 0 && pos.x() < q->width()) {
    885             const int scrollerHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q);
    886923            for(int dir = QMenuScroller::ScrollUp; dir <= QMenuScroller::ScrollDown; dir = dir << 1) {
    887924                if (scroll->scrollFlags & dir) {
    888925                    if (dir == QMenuScroller::ScrollUp)
    889                         isScroll = (pos.y() <= scrollerHeight);
     926                        isScroll = (pos.y() <= scrollerHeight());
    890927                    else if (dir == QMenuScroller::ScrollDown)
    891                         isScroll = (pos.y() >= q->height()-scrollerHeight);
     928                        isScroll = (pos.y() >= q->height() - scrollerHeight());
    892929                    if (isScroll) {
    893930                        scroll->scrollDirection = dir;
     
    898935        }
    899936        if (isScroll) {
    900             if (!scroll->scrollTimer)
    901                 scroll->scrollTimer = new QBasicTimer;
    902             scroll->scrollTimer->start(50, q);
     937            scroll->scrollTimer.start(50, q);
    903938            return true;
    904         } else if (scroll->scrollTimer && scroll->scrollTimer->isActive()) {
    905             scroll->scrollTimer->stop();
     939        } else {
     940            scroll->scrollTimer.stop();
    906941        }
    907942    }
     
    910945        QRect tearRect(0, 0, q->width(), q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q));
    911946        if (scroll && scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
    912             tearRect.translate(0, q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q));
     947            tearRect.translate(0, scrollerHeight());
    913948        q->update(tearRect);
    914949        if (tearRect.contains(pos) && hasMouseMoved(e->globalPos())) {
     
    957992}
    958993
     994class ExceptionGuard
     995{
     996public:
     997    inline ExceptionGuard(bool *w = 0) : watched(w) { Q_ASSERT(!(*watched)); *watched = true; }
     998    inline ~ExceptionGuard() { *watched = false; }
     999    inline operator bool() { return *watched; }
     1000private:
     1001    bool *watched;
     1002};
     1003
    9591004void QMenuPrivate::activateCausedStack(const QList<QPointer<QWidget> > &causedStack, QAction *action, QAction::ActionEvent action_e, bool self)
    9601005{
    961     Q_ASSERT(!activationRecursionGuard);
    962     activationRecursionGuard = true;
     1006    ExceptionGuard guard(&activationRecursionGuard);
    9631007#ifdef QT3_SUPPORT
    9641008    const int actionId = q_func()->findIdForAction(action);
     
    10051049        }
    10061050    }
    1007     activationRecursionGuard = false;
    10081051}
    10091052
     
    10351078            hideUpToMenuBar();
    10361079        } else {
    1037             for(QWidget *widget = qApp->activePopupWidget(); widget; ) {
     1080            for(QWidget *widget = QApplication::activePopupWidget(); widget; ) {
    10381081                if (QMenu *qmenu = qobject_cast<QMenu*>(widget)) {
    10391082                    if(qmenu == q)
     
    10691112        }
    10701113#endif
    1071         QWidget *w = causedPopup.widget;
    1072         while (QMenu *m = qobject_cast<QMenu*>(w))
    1073             w = m->d_func()->causedPopup.widget;
    1074         action->showStatusText(w);
     1114        action->showStatusText(topCausedWidget());
    10751115    } else {
    10761116        actionAboutToTrigger = 0;
     
    10821122    Q_Q(QMenu);
    10831123    if (QAction *action = qobject_cast<QAction *>(q->sender())) {
     1124        QWeakPointer<QAction> actionGuard = action;
    10841125#ifdef QT3_SUPPORT
    10851126        //we store it here because the action might be deleted/changed by connected slots
     
    10911132#endif
    10921133
    1093         if (!activationRecursionGuard) {
     1134        if (!activationRecursionGuard && actionGuard) {
    10941135            //in case the action has not been activated by the mouse
    10951136            //we check the parent hierarchy
     
    11651206        option->palette.setCurrentColorGroup(QPalette::Disabled);
    11661207
    1167     option->font = action->font();
     1208    option->font = action->font().resolve(font());
     1209    option->fontMetrics = QFontMetrics(option->font);
    11681210
    11691211    if (d->currentAction && d->currentAction == action && !d->currentAction->isSeparator()) {
     
    12091251    bars, context menus, and other popup menus.
    12101252
    1211     \ingroup application
     1253    \ingroup mainwindow-classes
    12121254    \ingroup basicwidgets
    1213     \mainclass
     1255
    12141256
    12151257    A menu widget is a selection menu. It can be either a pull-down
     
    13031345    It is not possible to display an icon in a native menu on Windows Mobile.
    13041346
     1347    \section1 QMenu on Mac OS X with Qt build against Cocoa
     1348
     1349    QMenu can be inserted only once in a menu/menubar. Subsequent insertions will
     1350    have no effect or will result in a disabled menu item.
     1351
    13051352    See the \l{mainwindows/menus}{Menus} example for an example of how
    13061353    to use QMenuBar and QMenu in your application.
     
    13601407{
    13611408    Q_D(QMenu);
    1362     for (QHash<QAction *, QWidget *>::ConstIterator item = d->widgetItems.constBegin(),
    1363          end = d->widgetItems.constEnd(); item != end; ++item) {
    1364         QWidgetAction *action = static_cast<QWidgetAction *>(item.key());
    1365         QWidget *widget = item.value();
    1366         if (action && widget)
     1409    QHash<QAction *, QWidget *>::iterator it = d->widgetItems.begin();
     1410    for (; it != d->widgetItems.end(); ++it) {
     1411        if (QWidget *widget = it.value()) {
     1412            QWidgetAction *action = static_cast<QWidgetAction *>(it.key());
    13671413            action->releaseWidget(widget);
    1368     }
    1369     d->widgetItems.clear();
     1414            *it = 0;
     1415        }
     1416    }
    13701417
    13711418    if (d->eventLoop)
    13721419        d->eventLoop->exit();
    1373     if (d->tornPopup)
    1374         d->tornPopup->close();
     1420    hideTearOffMenu();
    13751421}
    13761422
     
    15831629    if (d->tearoff == b)
    15841630        return;
    1585     if (!b && d->tornPopup)
    1586         d->tornPopup->close();
     1631    if (!b)
     1632        hideTearOffMenu();
    15871633    d->tearoff = b;
    15881634
     
    16191665void QMenu::hideTearOffMenu()
    16201666{
    1621     if (d_func()->tornPopup)
    1622         d_func()->tornPopup->close();
     1667    if (QWidget *w = d_func()->tornPopup)
     1668        w->close();
    16231669}
    16241670
     
    17181764{
    17191765    Q_D(const QMenu);
    1720     ensurePolished();
    1721     QMap<QAction*, QRect> actionRects;
    1722     QList<QAction*> actionList;
    1723     d->calcActionRects(actionRects, actionList);
     1766    d->updateActionRects();
    17241767
    17251768    QSize s;
    1726     QStyleOption opt(0);
    1727     opt.rect = rect();
    1728     opt.palette = palette();
    1729     opt.state = QStyle::State_None;
    1730     for (QMap<QAction*, QRect>::const_iterator i = actionRects.constBegin();
    1731          i != actionRects.constEnd(); ++i) {
    1732         if (i.value().bottom() > s.height())
    1733             s.setHeight(i.value().y()+i.value().height());
    1734         if (i.value().right() > s.width())
    1735             s.setWidth(i.value().right());
    1736     }
    1737     if (d->tearoff)
    1738         s.rheight() += style()->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, this);
    1739     if (const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, this)) {
    1740         s.rwidth() += fw*2;
    1741         s.rheight() += fw*2;
     1769    for (int i = 0; i < d->actionRects.count(); ++i) {
     1770        const QRect &rect = d->actionRects.at(i);
     1771        if (rect.isNull())
     1772            continue;
     1773        if (rect.bottom() >= s.height())
     1774            s.setHeight(rect.y() + rect.height());
     1775        if (rect.right() >= s.width())
     1776            s.setWidth(rect.x() + rect.width());
    17421777    }
    17431778    // Note that the action rects calculated above already include
    17441779    // the top and left margins, so we only need to add margins for
    17451780    // the bottom and right.
    1746     s.rwidth() += style()->pixelMetric(QStyle::PM_MenuHMargin, &opt, this);
    1747     s.rheight() += style()->pixelMetric(QStyle::PM_MenuVMargin, &opt, this);
    1748 
    1749     s += QSize(d->leftmargin + d->rightmargin, d->topmargin + d->bottommargin);
     1781    QStyleOption opt(0);
     1782    opt.init(this);
     1783    const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, this);
     1784    s.rwidth() += style()->pixelMetric(QStyle::PM_MenuHMargin, &opt, this) + fw + d->rightmargin;
     1785    s.rheight() += style()->pixelMetric(QStyle::PM_MenuVMargin, &opt, this) + fw + d->bottommargin;
    17501786
    17511787    return style()->sizeFromContents(QStyle::CT_Menu, &opt,
     
    17771813    d->motions = 0;
    17781814    d->doChildEffects = true;
     1815    d->updateLayoutDirection();
    17791816
    17801817#ifndef QT_NO_MENUBAR
    17811818    // if this menu is part of a chain attached to a QMenuBar, set the
    17821819    // _NET_WM_WINDOW_TYPE_DROPDOWN_MENU X11 window type
    1783     QWidget* top = this;
    1784     while (QMenu* m = qobject_cast<QMenu *>(top))
    1785         top = m->d_func()->causedPopup.widget;
    1786     setAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu, qobject_cast<QMenuBar *>(top) != 0);
     1820    setAttribute(Qt::WA_X11NetWmWindowTypeDropDownMenu, qobject_cast<QMenuBar *>(d->topCausedWidget()) != 0);
    17871821#endif
    17881822
    17891823    ensurePolished(); // Get the right font
    17901824    emit aboutToShow();
    1791     d->updateActions();
    1792     QPoint pos = p;
     1825    const bool actionListChanged = d->itemsDirty;
     1826    d->updateActionRects();
     1827    QPoint pos;
     1828    QPushButton *causedButton = qobject_cast<QPushButton*>(d->causedPopup.widget);
     1829    if (actionListChanged && causedButton)
     1830        pos = QPushButtonPrivate::get(causedButton)->adjustedMenuPosition();
     1831    else
     1832        pos = p;
     1833
    17931834    QSize size = sizeHint();
    1794     QRect screen = d->popupGeometry(QApplication::desktop()->screenNumber(p));
     1835    QRect screen;
     1836#ifndef QT_NO_GRAPHICSVIEW
     1837    bool isEmbedded = d->nearestGraphicsProxyWidget(this);
     1838    if (isEmbedded)
     1839        screen = d->popupGeometry(this);
     1840    else
     1841#endif
     1842    screen = d->popupGeometry(QApplication::desktop()->screenNumber(p));
     1843
    17951844    const int desktopFrame = style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, this);
    17961845    bool adjustToDesktop = !window()->testAttribute(Qt::WA_DontShowOnScreen);
     1846#ifdef QT_KEYPAD_NAVIGATION
     1847    if (!atAction && QApplication::keypadNavigationEnabled()) {
     1848        // Try to have one item activated
     1849        if (d->defaultAction && d->defaultAction->isEnabled()) {
     1850            atAction = d->defaultAction;
     1851            // TODO: This works for first level menus, not yet sub menus
     1852        } else {
     1853            foreach (QAction *action, d->actions)
     1854                if (action->isEnabled()) {
     1855                    atAction = action;
     1856                    break;
     1857                }
     1858        }
     1859        d->currentAction = atAction;
     1860    }
     1861#endif
    17971862    if (d->ncols > 1) {
    17981863        pos.setY(screen.top()+desktopFrame);
    17991864    } else if (atAction) {
    1800         for(int i=0, above_height=0; i<(int)d->actionList.count(); i++) {
    1801             QAction *action = d->actionList.at(i);
     1865        for(int i = 0, above_height = 0; i < d->actions.count(); i++) {
     1866            QAction *action = d->actions.at(i);
    18021867            if (action == atAction) {
    1803                 int newY = pos.y()-above_height;
     1868                int newY = pos.y() - above_height;
    18041869                if (d->scroll && newY < desktopFrame) {
    18051870                    d->scroll->scrollFlags = d->scroll->scrollFlags
     
    18131878                    && !style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll, 0, this)) {
    18141879                    int below_height = above_height + d->scroll->scrollOffset;
    1815                     for(int i2 = i; i2 < (int)d->actionList.count(); i2++)
    1816                         below_height += d->actionRects.value(d->actionList.at(i2)).height();
     1880                    for(int i2 = i; i2 < d->actionRects.count(); i2++)
     1881                        below_height += d->actionRects.at(i2).height();
    18171882                    size.setHeight(below_height);
    18181883                }
    18191884                break;
    18201885            } else {
    1821                 above_height += d->actionRects.value(action).height();
     1886                above_height += d->actionRects.at(i).height();
    18221887            }
    18231888        }
     
    18301895    if (adjustToDesktop) {
    18311896        //handle popup falling "off screen"
    1832         if (qApp->layoutDirection() == Qt::RightToLeft) {
     1897        if (isRightToLeft()) {
    18331898            if(snapToMouse) //position flowing left from the mouse
    18341899                pos.setX(mouse.x()-size.width());
     1900
     1901#ifndef QT_NO_MENUBAR
     1902            //if in a menubar, it should be right-aligned
     1903            if (qobject_cast<QMenuBar*>(d->causedPopup.widget))
     1904                pos.rx() -= size.width();
     1905#endif //QT_NO_MENUBAR
    18351906
    18361907            if (pos.x() < screen.left()+desktopFrame)
     
    18401911        } else {
    18411912            if (pos.x()+size.width()-1 > screen.right()-desktopFrame)
    1842                 pos.setX(qMin(p.x()+size.width(), screen.right()-desktopFrame-size.width()+1));
     1913                pos.setX(screen.right()-desktopFrame-size.width()+1);
    18431914            if (pos.x() < screen.left()+desktopFrame)
    1844                 pos.setX(qMax(p.x(), screen.left() + desktopFrame));
     1915                pos.setX(screen.left() + desktopFrame);
    18451916        }
    18461917        if (pos.y() + size.height() - 1 > screen.bottom() - desktopFrame) {
     
    18681939    setGeometry(QRect(pos, size));
    18691940#ifndef QT_NO_EFFECTS
    1870     int hGuess = qApp->layoutDirection() == Qt::RightToLeft ? QEffects::LeftScroll : QEffects::RightScroll;
     1941    int hGuess = isRightToLeft() ? QEffects::LeftScroll : QEffects::RightScroll;
    18711942    int vGuess = QEffects::DownScroll;
    1872     if (qApp->layoutDirection() == Qt::RightToLeft) {
     1943    if (isRightToLeft()) {
    18731944        if ((snapToMouse && (pos.x() + size.width()/2 > mouse.x())) ||
    18741945           (qobject_cast<QMenu*>(d->causedPopup.widget) && pos.x() + size.width()/2 > d->causedPopup.widget->x()))
     
    20262097{
    20272098    QMenu menu(parent);
    2028     for(QList<QAction*>::ConstIterator it = actions.constBegin(); it != actions.constEnd(); ++it)
    2029         menu.addAction((*it));
     2099    menu.addActions(actions);
    20302100    return menu.exec(pos, at);
    20312101}
     
    20772147    d->causedPopup.widget = 0;
    20782148    d->causedPopup.action = 0;
     2149    if (d->scroll)
     2150        d->scroll->scrollTimer.stop(); //make sure the timer stops
    20792151}
    20802152
     
    20852157{
    20862158    Q_D(QMenu);
     2159    d->updateActionRects();
    20872160    QPainter p(this);
    20882161    QRegion emptyArea = QRegion(rect());
     
    20972170
    20982171    //draw the items that need updating..
    2099     for (int i = 0; i < d->actionList.count(); ++i) {
    2100         QAction *action = d->actionList.at(i);
    2101         QRect adjustedActionRect = d->actionRect(action);
     2172    for (int i = 0; i < d->actions.count(); ++i) {
     2173        QAction *action = d->actions.at(i);
     2174        QRect adjustedActionRect = d->actionRects.at(i);
    21022175        if (!e->rect().intersects(adjustedActionRect)
    21032176            || d->widgetItems.value(action))
     
    21172190    //draw the scroller regions..
    21182191    if (d->scroll) {
    2119         const int scrollerHeight = style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this);
    21202192        menuOpt.menuItemType = QStyleOptionMenuItem::Scroller;
    21212193        menuOpt.state |= QStyle::State_Enabled;
    21222194        if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) {
    2123             menuOpt.rect.setRect(fw, fw, width() - (fw * 2), scrollerHeight);
     2195            menuOpt.rect.setRect(fw, fw, width() - (fw * 2), d->scrollerHeight());
    21242196            emptyArea -= QRegion(menuOpt.rect);
    21252197            p.setClipRect(menuOpt.rect);
     
    21272199        }
    21282200        if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown) {
    2129             menuOpt.rect.setRect(fw, height() - scrollerHeight - fw, width() - (fw * 2),
    2130                                      scrollerHeight);
     2201            menuOpt.rect.setRect(fw, height() - d->scrollerHeight() - fw, width() - (fw * 2),
     2202                                     d->scrollerHeight());
    21312203            emptyArea -= QRegion(menuOpt.rect);
    21322204            menuOpt.state |= QStyle::State_DownArrow;
     
    21412213                             style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this));
    21422214        if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
    2143             menuOpt.rect.translate(0, style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this));
     2215            menuOpt.rect.translate(0, d->scrollerHeight());
    21442216        emptyArea -= QRegion(menuOpt.rect);
    21452217        p.setClipRect(menuOpt.rect);
     
    22352307            action->menu()->d_func()->setFirstActionActive();
    22362308        else {
    2237 #if defined(Q_WS_WIN) && !defined(QT_NO_MENUBAR)
     2309#if defined(Q_WS_WIN)
    22382310            //On Windows only context menus can be activated with the right button
    2239             bool isContextMenu = true;
    2240             const QWidget *cause = d->causedPopup.widget;
    2241             while (cause) {
    2242                 //if the popup was caused by either QMenuBar or a QToolButton, it is not a context menu
    2243                 if (qobject_cast<const QMenuBar *>(cause) || qobject_cast<const QToolButton *>(cause)) {
    2244                     isContextMenu = false;
    2245                     break;
    2246                 } else if (const QMenu *menu = qobject_cast<const QMenu *>(cause)) {
    2247                     cause = menu->d_func()->causedPopup.widget;
    2248                 } else {
    2249                     break;
    2250                 }
    2251             }
    2252             if (e->button() == Qt::LeftButton || (e->button() == Qt::RightButton && isContextMenu))
     2311            if (e->button() == Qt::LeftButton || d->topCausedWidget() == 0)
    22532312#endif
    22542313                d->activateAction(action, QAction::Trigger);
     
    22992358    Q_D(QMenu);
    23002359    switch (e->type()) {
     2360    case QEvent::Polish:
     2361        d->updateLayoutDirection();
     2362        break;
    23012363    case QEvent::ShortcutOverride: {
    23022364            QKeyEvent *kev = static_cast<QKeyEvent*>(e);
     
    23312393        }
    23322394        d->itemsDirty = 1;
    2333         d->updateActions();
     2395        d->updateActionRects();
    23342396        break; }
    23352397    case QEvent::Show:
    23362398        d->mouseDown = 0;
    2337         d->updateActions();
     2399        d->updateActionRects();
    23382400        if (d->currentAction)
    23392401            d->popupAction(d->currentAction, 0, false);
     
    23712433{
    23722434    Q_D(QMenu);
     2435    d->updateActionRects();
    23732436    int key = e->key();
    23742437    if (isRightToLeft()) {  // in reverse mode open/close key for submenues are reversed
     
    24222485        if (!d->currentAction) {
    24232486            if(key == Qt::Key_Down) {
    2424                 for(int i = 0; i < d->actionList.size(); ++i) {
    2425                     QAction *act = d->actionList.at(i);
     2487                for(int i = 0; i < d->actions.count(); ++i) {
     2488                    QAction *act = d->actions.at(i);
     2489                    if (d->actionRects.at(i).isNull())
     2490                        continue;
    24262491                    if (!act->isSeparator() &&
    24272492                        (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
     
    24322497                }
    24332498            } else {
    2434                 for(int i = d->actionList.size()-1; i >= 0; --i) {
    2435                     QAction *act = d->actionList.at(i);
     2499                for(int i = d->actions.count()-1; i >= 0; --i) {
     2500                    QAction *act = d->actions.at(i);
     2501                    if (d->actionRects.at(i).isNull())
     2502                        continue;
    24362503                    if (!act->isSeparator() &&
    24372504                        (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
     
    24432510            }
    24442511        } else {
    2445             for(int i=0, y=0; !nextAction && i < (int)d->actionList.count(); i++) {
    2446                 QAction *act = d->actionList.at(i);
     2512            for(int i = 0, y = 0; !nextAction && i < d->actions.count(); i++) {
     2513                QAction *act = d->actions.at(i);
    24472514                if (act == d->currentAction) {
    24482515                    if (key == Qt::Key_Up) {
     
    24532520                                if (d->scroll)
    24542521                                    scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
    2455                                 next_i = d->actionList.count()-1;
     2522                                next_i = d->actionRects.count()-1;
    24562523                            }
    2457                             QAction *next = d->actionList.at(next_i);
     2524                            QAction *next = d->actions.at(next_i);
    24582525                            if (next == d->currentAction)
    24592526                                break;
     2527                            if (d->actionRects.at(next_i).isNull())
     2528                                continue;
    24602529                            if (next->isSeparator() ||
    24612530                               (!next->isEnabled() &&
     
    24642533                            nextAction = next;
    24652534                            if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)) {
    2466                                 int topVisible = style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this);
     2535                                int topVisible = d->scrollerHeight();
    24672536                                if (d->tearoff)
    24682537                                    topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);
    2469                                 if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.value(nextAction).height())
     2538                                if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.at(next_i).height())
    24702539                                    scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
    24712540                            }
     
    24752544                            d->tearoffHighlighted = 1;
    24762545                    } else {
    2477                         y += d->actionRects.value(act).height();
     2546                        y += d->actionRects.at(i).height();
    24782547                        for(int next_i = i+1; true; next_i++) {
    2479                             if (next_i == d->actionList.count()) {
     2548                            if (next_i == d->actionRects.count()) {
    24802549                                if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this))
    24812550                                    break;
     
    24842553                                next_i = 0;
    24852554                            }
    2486                             QAction *next = d->actionList.at(next_i);
     2555                            QAction *next = d->actions.at(next_i);
    24872556                            if (next == d->currentAction)
    24882557                                break;
     2558                            if (d->actionRects.at(next_i).isNull())
     2559                                continue;
    24892560                            if (next->isSeparator() ||
    24902561                               (!next->isEnabled() &&
     
    24932564                            nextAction = next;
    24942565                            if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)) {
    2495                                 const int scrollerHeight = style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this);
    2496                                 int bottomVisible = height()-scrollerHeight;
     2566                                int bottomVisible = height() - d->scrollerHeight();
    24972567                                if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
    2498                                     bottomVisible -= scrollerHeight;
     2568                                    bottomVisible -= d->scrollerHeight();
    24992569                                if (d->tearoff)
    25002570                                    bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);
    2501                                 if ((y + d->scroll->scrollOffset + d->actionRects.value(nextAction).height()) > bottomVisible)
     2571                                if ((y + d->scroll->scrollOffset + d->actionRects.at(next_i).height()) > bottomVisible)
    25022572                                    scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
    25032573                            }
     
    25072577                    break;
    25082578                }
    2509                 y += d->actionRects.value(act).height();
     2579                y += d->actionRects.at(i).height();
    25102580            }
    25112581        }
    25122582        if (nextAction) {
    25132583            if (d->scroll && scroll_loc != QMenuPrivate::QMenuScroller::ScrollStay) {
    2514                 if (d->scroll->scrollTimer)
    2515                     d->scroll->scrollTimer->stop();
     2584                d->scroll->scrollTimer.stop();
    25162585                d->scrollMenu(nextAction, scroll_loc);
    25172586            }
     
    25622631            d->hideMenu(this);
    25632632#ifndef QT_NO_MENUBAR
    2564             if (QMenuBar *mb = qobject_cast<QMenuBar*>(qApp->focusWidget())) {
     2633            if (QMenuBar *mb = qobject_cast<QMenuBar*>(QApplication::focusWidget())) {
    25652634                mb->d_func()->setKeyboardMode(false);
    25662635            }
     
    26352704                d->searchBufferTimer.start(2000, this);
    26362705                d->searchBuffer += e->text();
    2637                 for(int i = 0; i < d->actionList.size(); ++i) {
     2706                for(int i = 0; i < d->actions.size(); ++i) {
    26382707                    int match_count = 0;
    2639                     register QAction *act = d->actionList.at(i);
     2708                    if (d->actionRects.at(i).isNull())
     2709                        continue;
     2710                    QAction *act = d->actions.at(i);
    26402711                    const QString act_text = act->text();
    26412712                    for(int c = 0; c < d->searchBuffer.size(); ++c) {
     
    26542725                QAction *first = 0, *currentSelected = 0, *firstAfterCurrent = 0;
    26552726                QChar c = e->text().at(0).toUpper();
    2656                 for(int i = 0; i < d->actionList.size(); ++i) {
    2657                     register QAction *act = d->actionList.at(i);
     2727                for(int i = 0; i < d->actions.size(); ++i) {
     2728                    if (d->actionRects.at(i).isNull())
     2729                        continue;
     2730                    QAction *act = d->actions.at(i);
    26582731                    QKeySequence sequence = QKeySequence::mnemonic(act->text());
    26592732                    int key = sequence[0] & 0xffff;
     
    26902763        }
    26912764        if (!key_consumed) {
    2692             if (QWidget *caused = d->causedPopup.widget) {
    2693                 while(QMenu *m = qobject_cast<QMenu*>(caused))
    2694                     caused = m->d_func()->causedPopup.widget;
    26952765#ifndef QT_NO_MENUBAR
    2696                 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
    2697                     QAction *oldAct = mb->d_func()->currentAction;
    2698                     QApplication::sendEvent(mb, e);
    2699                     if (mb->d_func()->currentAction != oldAct)
    2700                         key_consumed = true;
    2701                 }
    2702 #endif
     2766            if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->topCausedWidget())) {
     2767                QAction *oldAct = mb->d_func()->currentAction;
     2768                QApplication::sendEvent(mb, e);
     2769                if (mb->d_func()->currentAction != oldAct)
     2770                    key_consumed = true;
    27032771            }
     2772#endif
    27042773        }
    27052774
    27062775#ifdef Q_OS_WIN32
    27072776        if (key_consumed && (e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta))
    2708             qApp->beep();
     2777            QApplication::beep();
    27092778#endif // Q_OS_WIN32
    27102779    }
     
    27262795    if (d->motions == 0) // ignore first mouse move event (see enterEvent())
    27272796        return;
    2728     d->hasHadMouse |= rect().contains(e->pos());
     2797    d->hasHadMouse = d->hasHadMouse || rect().contains(e->pos());
    27292798
    27302799    QAction *action = d->actionAt(e->pos());
    27312800    if (!action) {
    2732         if (d->hasHadMouse && !rect().contains(e->pos()))
     2801        if (d->hasHadMouse)
    27332802            d->setCurrentAction(0);
    27342803        return;
    2735     } else if(e->buttons() & (Qt::LeftButton | Qt::RightButton)) {
     2804    } else if(e->buttons()) {
    27362805        d->mouseDown = this;
    27372806    }
     
    27612830    if (!d->sloppyRegion.isEmpty())
    27622831        d->sloppyRegion = QRegion();
     2832    if (!d->activeMenu && d->currentAction)
     2833        setActiveAction(0);
    27632834}
    27642835
     
    27702841{
    27712842    Q_D(QMenu);
    2772     if (d->scroll && d->scroll->scrollTimer && d->scroll->scrollTimer->timerId() == e->timerId()) {
     2843    if (d->scroll && d->scroll->scrollTimer.timerId() == e->timerId()) {
    27732844        d->scrollMenu((QMenuPrivate::QMenuScroller::ScrollDirection)d->scroll->scrollDirection);
    27742845        if (d->scroll->scrollFlags == QMenuPrivate::QMenuScroller::ScrollNone)
    2775             d->scroll->scrollTimer->stop();
     2846            d->scroll->scrollTimer.stop();
    27762847    } else if(QMenuPrivate::menuDelayTimer.timerId() == e->timerId()) {
    27772848        QMenuPrivate::menuDelayTimer.stop();
     
    28002871            connect(e->action(), SIGNAL(hovered()), this, SLOT(_q_actionHovered()));
    28012872        }
    2802 
    28032873        if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
    28042874            QWidget *widget = wa->requestWidget(this);
     
    28072877        }
    28082878    } else if (e->type() == QEvent::ActionRemoved) {
    2809         d->actionRects.clear();
    2810         d->actionList.clear();
    28112879        e->action()->disconnect(this);
    28122880        if (e->action() == d->currentAction)
    28132881            d->currentAction = 0;
    28142882        if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
    2815             QWidget *widget = d->widgetItems.take(wa);
    2816             if (widget)
     2883            if (QWidget *widget = d->widgetItems.value(wa))
    28172884                wa->releaseWidget(widget);
    2818         } else {
    2819             // If this is called from the QAction destructor, the
    2820             // previous call to qobject_cast will fail because the
    2821             // QWidgetAction has been destroyed already. We need to
    2822             // remove it from the hash anyway or it might crash later
    2823             // the widget itself has been already destroyed in
    2824             // ~QWidgetAction
    2825             d->widgetItems.remove(e->action());
    2826         }
     2885        }
     2886        d->widgetItems.remove(e->action());
    28272887    }
    28282888
     
    28382898#endif
    28392899
    2840 #if defined(Q_OS_WINCE) && !defined(QT_NO_MENUBAR)
     2900#if defined(Q_WS_WINCE) && !defined(QT_NO_MENUBAR)
    28412901    if (!d->wce_menu)
    28422902        d->wce_menu = new QMenuPrivate::QWceMenuPrivate;
     
    28492909#endif
    28502910
     2911#ifdef Q_WS_S60
     2912    if (!d->symbian_menu)
     2913        d->symbian_menu = new QMenuPrivate::QSymbianMenuPrivate;
     2914    if (e->type() == QEvent::ActionAdded)
     2915        d->symbian_menu->addAction(e->action(), d->symbian_menu->findAction(e->before()));
     2916    else if (e->type() == QEvent::ActionRemoved)
     2917        d->symbian_menu->removeAction(e->action());
     2918    else if (e->type() == QEvent::ActionChanged)
     2919        d->symbian_menu->syncAction(e->action());
     2920#endif
    28512921    if (isVisible()) {
    2852         d->updateActions();
     2922        d->updateActionRects();
    28532923        resize(sizeHint());
    28542924        update();
     
    28962966    QMenu *caused = qobject_cast<QMenu*>(d->activeMenu->d_func()->causedPopup.widget);
    28972967
    2898     const QRect availGeometry(d->popupGeometry(QApplication::desktop()->screenNumber(caused)));
     2968    const QRect availGeometry(d->popupGeometry(caused));
    28992969    if (isRightToLeft()) {
    29002970        pos = leftPos;
     
    30283098{
    30293099    Q_D(QMenu);
     3100    if (d->collapsibleSeparators == collapse)
     3101        return;
     3102
    30303103    d->collapsibleSeparators = collapse;
    30313104    d->itemsDirty = 1;
    30323105    if (isVisible()) {
    3033         d->updateActions();
     3106        d->updateActionRects();
    30343107        update();
    30353108    }
     3109#ifdef Q_WS_MAC
     3110    if (d->mac_menu)
     3111        d->syncSeparatorsCollapsible(collapse);
     3112#endif
    30363113}
    30373114
     
    30903167QAction *QMenu::findActionForId(int id) const
    30913168{
    3092     QList<QAction *> list = actions();
    3093     for (int i = 0; i < list.size(); ++i) {
    3094         QAction *act = list.at(i);
     3169    Q_D(const QMenu);
     3170    for (int i = 0; i < d->actions.size(); ++i) {
     3171        QAction *act = d->actions.at(i);
    30953172        if (findIdForAction(act)== id)
    30963173            return act;
Note: See TracChangeset for help on using the changeset viewer.