src/gui/widgets/qmenu.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the QtGui module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include "qmenu.h"
00025 
00026 #ifndef QT_NO_MENU
00027 
00028 #include "qdebug.h"
00029 #include "qstyle.h"
00030 #include "qevent.h"
00031 #include "qtimer.h"
00032 #include "qlayout.h"
00033 #include "qpainter.h"
00034 #include "qapplication.h"
00035 #include "qdesktopwidget.h"
00036 #ifndef QT_NO_ACCESSIBILITY
00037 # include "qaccessible.h"
00038 #endif
00039 #ifndef QT_NO_EFFECTS
00040 # include <private/qeffects_p.h>
00041 #endif
00042 #ifndef QT_NO_WHATSTHIS
00043 # include <qwhatsthis.h>
00044 #endif
00045 
00046 #include "qmenu_p.h"
00047 #include "qmenubar_p.h"
00048 #include "qwidgetaction.h"
00049 #include <private/qaction_p.h>
00050 #ifdef QT3_SUPPORT
00051 #include <qmenudata.h>
00052 #endif // QT3_SUPPORT
00053 
00054 #ifdef Q_WS_X11
00055 #include <private/qt_x11_p.h>
00056 #endif
00057 
00058 
00059 QBasicTimer QMenuPrivate::menuDelayTimer;
00060 QBasicTimer QMenuPrivate::sloppyDelayTimer;
00061 
00062 /* QMenu code */
00063 // internal class used for the torn off popup
00064 class QTornOffMenu : public QMenu
00065 {
00066     Q_OBJECT
00067 public:
00068     QTornOffMenu(QMenu *p) : QMenu(0)
00069     {
00070         d_func()->tornoff = 1;
00071         d_func()->causedPopup.widget = ((QTornOffMenu*)p)->d_func()->causedPopup.widget;
00072         d_func()->causedPopup.action = ((QTornOffMenu*)p)->d_func()->causedPopup.action;
00073 
00074         setParent(p, Qt::Window | Qt::Tool);
00075   setAttribute(Qt::WA_DeleteOnClose, true);
00076         setWindowTitle(p->windowTitle());
00077         setEnabled(p->isEnabled());
00078         QObject::connect(this, SIGNAL(activated(int)), p, SIGNAL(activated(int)));
00079         QObject::connect(this, SIGNAL(highlighted(int)), p, SIGNAL(highlighted(int)));
00080         QList<QAction*> items = p->actions();
00081         for(int i = 0; i < items.count(); i++)
00082             addAction(items.at(i));
00083     }
00084     void syncWithMenu(QMenu *, QActionEvent *act)
00085     {
00086         if (act->type() == QEvent::ActionAdded) {
00087             insertAction(act->before(), act->action());
00088         } else if (act->type() == QEvent::ActionRemoved)
00089             removeAction(act->action());
00090     }
00091     void actionEvent(QActionEvent *e)
00092     {
00093         QMenu::actionEvent(e);
00094         resize(sizeHint());
00095     }
00096 };
00097 #include "qmenu.moc"
00098 
00099 //Windows and KDE allows menus to cover the taskbar, while GNOME and Mac don't
00100 QRect QMenuPrivate::popupGeometry(int screen) const
00101 {
00102 #ifdef Q_WS_WIN
00103     return QApplication::desktop()->screenGeometry(screen);
00104 #elif defined Q_WS_X11
00105     if (X11->desktopEnvironment == DE_KDE)
00106         return QApplication::desktop()->screenGeometry(screen);
00107     else
00108         return QApplication::desktop()->availableGeometry(screen);
00109 #else
00110         return QApplication::desktop()->availableGeometry(screen);
00111 #endif
00112 }
00113 
00114 void QMenuPrivate::calcActionRects(QMap<QAction*, QRect> &actionRects, QList<QAction*> &actionList) const
00115 {
00116     Q_Q(const QMenu);
00117     if (!itemsDirty) {
00118         actionRects = this->actionRects;
00119         actionList = this->actionList;
00120         return;
00121     }
00122 
00123     actionRects.clear();
00124     actionList.clear();
00125     QList<QAction*> items = filterActions(q->actions());
00126     int max_column_width = 0,
00127         dh = popupGeometry(QApplication::desktop()->screenNumber(q)).height(),
00128         ncols = 1,
00129         y = 0;
00130     const int hmargin = q->style()->pixelMetric(QStyle::PM_MenuHMargin, 0, q),
00131               vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q),
00132               icone = q->style()->pixelMetric(QStyle::PM_SmallIconSize, 0, q);
00133 
00134     //for compatability now - will have to refactor this away..
00135     tabWidth = 0;
00136     maxIconWidth = 0;
00137     hasCheckableItems = false;
00138     for(int i = 0; i < items.count(); i++) {
00139         QAction *action = items.at(i);
00140         if (widgetItems.value(action))
00141             continue;
00142         hasCheckableItems |= action->isCheckable();
00143         QIcon is = action->icon();
00144         if (!is.isNull()) {
00145             uint miw = maxIconWidth;
00146             maxIconWidth = qMax<uint>(miw, icone + 4);
00147         }
00148     }
00149 
00150     //calculate size
00151     QFontMetrics qfm = q->fontMetrics();
00152     for(int i = 0; i < items.count(); i++) {
00153         QAction *action = items.at(i);
00154 
00155         QFontMetrics fm(action->font().resolve(q->font()));
00156         QSize sz;
00157 
00158         if (QWidget *w = widgetItems.value(action)) {
00159             sz = w->sizeHint();
00160         } else {
00161             //calc what I think the size is..
00162             if (action->isSeparator()) {
00163                 sz = QSize(2, 2);
00164             } else {
00165                 QString s = action->text();
00166                 int t = s.indexOf(QLatin1Char('\t'));
00167                 if (t != -1) {
00168                     tabWidth = qMax(int(tabWidth), qfm.width(s.mid(t+1)));
00169                     s = s.left(t);
00170     #ifndef QT_NO_SHORTCUT
00171                 } else {
00172                     QKeySequence seq = action->shortcut();
00173                     if (!seq.isEmpty())
00174                         tabWidth = qMax(int(tabWidth), qfm.width(seq));
00175     #endif
00176                 }
00177                 int w = fm.width(s);
00178                 w -= s.count(QLatin1Char('&')) * fm.width(QLatin1Char('&'));
00179                 w += s.count(QLatin1String("&&")) * fm.width(QLatin1Char('&'));
00180                 sz.setWidth(w);
00181                 sz.setHeight(qMax(fm.height(), qfm.height()));
00182 
00183                 QIcon is = action->icon();
00184                 if (!is.isNull()) {
00185                     QSize is_sz = QSize(icone, icone);
00186                     if (is_sz.height() > sz.height())
00187                         sz.setHeight(is_sz.height());
00188                 }
00189             }
00190         }
00191 
00192         //let the style modify the above size..
00193         QStyleOptionMenuItem opt = getStyleOption(action);
00194         opt.rect = q->rect();
00195         sz = q->style()->sizeFromContents(QStyle::CT_MenuItem, &opt, sz, q);
00196 
00197         if (!sz.isEmpty()) {
00198             max_column_width = qMax(max_column_width, sz.width());
00199             //wrapping
00200             if (!scroll &&
00201                y+sz.height()+vmargin > dh - (q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q) * 2)) {
00202                 ncols++;
00203                 y = vmargin;
00204             }
00205             y += sz.height();
00206             //append item
00207             actionRects.insert(action, QRect(0, 0, sz.width(), sz.height()));
00208             actionList.append(action);
00209         }
00210     }
00211     if (tabWidth)
00212         max_column_width += tabWidth; //finally add in the tab width
00213 
00214     //calculate position
00215     int x = hmargin;
00216     y = vmargin;
00217     for(int i = 0; i < actionList.count(); i++) {
00218         QAction *action = actionList.at(i);
00219         QRect &rect = actionRects[action];
00220         if (rect.isNull())
00221             continue;
00222         if (!scroll &&
00223            y+rect.height() > dh - (q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q) * 2)) {
00224             ncols--;
00225             if (ncols < 0)
00226                 qWarning("QMenu: Column calculation mismatch (%d)", ncols);
00227             x += max_column_width + hmargin;
00228             y = vmargin;
00229         }
00230         rect.translate(x, y);                        //move
00231         rect.setWidth(max_column_width); //uniform width
00232         y += rect.height();
00233     }
00234 }
00235 
00236 void QMenuPrivate::updateActions()
00237 {
00238     Q_Q(const QMenu);
00239     if (!itemsDirty)
00240         return;
00241     sloppyAction = 0;
00242     calcActionRects(actionRects, actionList);
00243     for (QHash<QAction *, QWidget *>::ConstIterator item = widgetItems.constBegin(),
00244          end = widgetItems.constEnd(); item != end; ++item) {
00245         QAction *action = item.key();
00246         QWidget *widget = item.value();
00247         widget->setGeometry(actionRect(action));
00248         widget->setVisible(action->isVisible());
00249     }
00250     ncols = 1;
00251     int last_left = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
00252     if (!scroll) {
00253         for(int i = 0; i < actionList.count(); i++) {
00254             int left = actionRects.value(actionList.at(i)).left();
00255             if (left > last_left) {
00256                 last_left = left;
00257                 ncols++;
00258             }
00259         }
00260     }
00261     itemsDirty = 0;
00262 }
00263 
00264 QList<QAction *> QMenuPrivate::filterActions(const QList<QAction *> &actions) const
00265 {
00266     QList<QAction *> visibleActions;
00267     int i = 0;
00268     while (i < actions.count()) {
00269         QAction *action = actions.at(i);
00270         if (!action->isVisible()) {
00271             ++i;
00272             continue;
00273         }
00274         if (!action->isSeparator() || !collapsibleSeparators) {
00275             visibleActions.append(action);
00276             ++i;
00277             continue;
00278         }
00279 
00280         // no leading separators
00281         if (!visibleActions.isEmpty())
00282             visibleActions.append(action);
00283 
00284         // skip double/tripple/etc. separators
00285         while (i < actions.count()
00286                && (!actions.at(i)->isVisible() || actions.at(i)->isSeparator()))
00287             ++i;
00288     }
00289 
00290     if (collapsibleSeparators) {
00291         // remove trailing separators
00292         while (!visibleActions.isEmpty() && visibleActions.last()->isSeparator())
00293             visibleActions.removeLast();
00294     }
00295 
00296     return visibleActions;
00297 }
00298 
00299 QRect QMenuPrivate::actionRect(QAction *act) const
00300 {
00301     Q_Q(const QMenu);
00302     QRect ret = actionRects.value(act);
00303     if (ret.isNull())
00304         return ret;
00305     if (scroll)
00306         ret.translate(0, scroll->scrollOffset);
00307     if (tearoff)
00308         ret.translate(0, q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q));
00309     const int fw = q->style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, q);
00310     ret.translate(fw+leftmargin, fw+topmargin);
00311     return ret;
00312 }
00313 
00314 void QMenuPrivate::hideUpToMenuBar()
00315 {
00316     Q_Q(QMenu);
00317     if (!tornoff) {
00318         QWidget *caused = causedPopup.widget;
00319         q->hide(); //hide after getting causedPopup
00320         while(caused) {
00321 #ifndef QT_NO_MENUBAR
00322             if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
00323                 mb->d_func()->setCurrentAction(0);
00324                 caused = 0;
00325             } else
00326 #endif
00327             if (QMenu *m = qobject_cast<QMenu*>(caused)) {
00328                 caused = m->d_func()->causedPopup.widget;
00329                 if (!m->d_func()->tornoff)
00330                     m->hide();
00331                 m->d_func()->setCurrentAction(0);
00332             } else {
00333                 qWarning("QMenu: Internal error");
00334                 caused = 0;
00335             }
00336         }
00337     }
00338     setCurrentAction(0);
00339 }
00340 
00341 void QMenuPrivate::popupAction(QAction *action, int delay, bool activateFirst)
00342 {
00343     Q_Q(QMenu);
00344     if (action && action->isEnabled()) {
00345         if (!delay)
00346             q->internalDelayedPopup();
00347         else
00348             QMenuPrivate::menuDelayTimer.start(delay, q);
00349         if (activateFirst && action->menu())
00350             action->menu()->d_func()->setFirstActionActive();
00351     } else if (QMenu *menu = activeMenu) {  //hide the current item
00352         activeMenu = 0;
00353         menu->hide();
00354     }
00355 }
00356 
00357 void QMenuPrivate::setFirstActionActive()
00358 {
00359     Q_Q(QMenu);
00360     const int scrollerHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q);
00361     for(int i = 0, saccum = 0; i < actionList.count(); i++) {
00362         QAction *act = actionList[i];
00363         if (scroll && scroll->scrollFlags & QMenuScroller::ScrollUp) {
00364             saccum -= actionRects.value(act).height();
00365             if (saccum > scroll->scrollOffset-scrollerHeight)
00366                 continue;
00367         }
00368         if (!act->isSeparator() &&
00369            (q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q)
00370             || act->isEnabled())) {
00371             setCurrentAction(act);
00372             break;
00373         }
00374     }
00375 }
00376 
00377 // popup == -1 means do not popup, 0 means immediately, others mean use a timer
00378 void QMenuPrivate::setCurrentAction(QAction *action, int popup, SelectionReason reason, bool activateFirst)
00379 {
00380     Q_Q(QMenu);
00381     tearoffHighlighted = 0;
00382     if (action == currentAction && !(action && action->menu() && action->menu() != activeMenu)) {
00383         if(QMenu *menu = qobject_cast<QMenu*>(causedPopup.widget)) {
00384             if(causedPopup.action && menu->d_func()->activeMenu == q)
00385                 menu->d_func()->setCurrentAction(causedPopup.action, 0, reason, false);
00386         }
00387         return;
00388     }
00389     if (currentAction)
00390         q->update(actionRect(currentAction));
00391 
00392     sloppyAction = 0;
00393     if (!sloppyRegion.isEmpty())
00394         sloppyRegion = QRegion();
00395     QMenu *hideActiveMenu = activeMenu;
00396 #ifndef QT_NO_STATUSTIP
00397     QAction *previousAction = currentAction;
00398 #endif
00399     currentAction = action;
00400     if (action && !action->isSeparator()) {
00401         activateAction(action, QAction::Hover);
00402         if (popup != -1) {
00403             hideActiveMenu = 0; //will be done "later"
00404             popupAction(currentAction, popup, activateFirst);
00405         }
00406         q->update(actionRect(action));
00407         QWidget *widget = widgetItems.value(action);
00408         if (reason == SelectedFromKeyboard
00409             && widget
00410             && widget->focusPolicy() != Qt::NoFocus)
00411                 widget->setFocus(Qt::TabFocusReason);
00412 
00413 #ifndef QT_NO_STATUSTIP
00414     }  else if (previousAction) {
00415         QWidget *w = causedPopup.widget;
00416         while (QMenu *m = qobject_cast<QMenu*>(w))
00417             w = m->d_func()->causedPopup.widget;
00418         if (w) {
00419             QString empty;
00420             QStatusTipEvent tip(empty);
00421             QApplication::sendEvent(w, &tip);
00422         }
00423 #endif
00424     }
00425     if (hideActiveMenu) {
00426         activeMenu = 0;
00427 #ifndef QT_NO_EFFECTS
00428         // kill any running effect
00429         qFadeEffect(0);
00430         qScrollEffect(0);
00431 #endif
00432         hideActiveMenu->hide();
00433     }
00434 }
00435 
00436 QAction *QMenuPrivate::actionAt(QPoint p) const
00437 {
00438     if (!q_func()->rect().contains(p))     //sanity check
00439        return 0;
00440 
00441     for(int i = 0; i < actionList.count(); i++) {
00442         QAction *act = actionList[i];
00443         if (actionRect(act).contains(p))
00444             return act;
00445     }
00446     return 0;
00447 }
00448 
00449 
00453 QAction *QMenu::menuAction() const
00454 {
00455     return d_func()->menuAction;
00456 }
00457 
00465 QString QMenu::title() const
00466 {
00467     return d_func()->menuAction->text();
00468 }
00469 
00470 void QMenu::setTitle(const QString &text)
00471 {
00472     d_func()->menuAction->setText(text);
00473 }
00474 
00482 QIcon QMenu::icon() const
00483 {
00484     return d_func()->menuAction->icon();
00485 }
00486 
00487 void QMenu::setIcon(const QIcon &icon)
00488 {
00489     d_func()->menuAction->setIcon(icon);
00490 }
00491 
00492 
00493 //actually performs the scrolling
00494 void QMenuPrivate::scrollMenu(QAction *action, QMenuScroller::ScrollLocation location, bool active)
00495 {
00496     Q_Q(QMenu);
00497     if (!scroll || !scroll->scrollFlags)
00498         return;
00499     int newOffset = 0;
00500     const int scrollHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q);
00501     const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp)   ? scrollHeight : 0;
00502     const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollHeight : 0;
00503     const int vmargin = q->style()->pixelMetric(QStyle::PM_MenuVMargin, 0, q);
00504 
00505     if (location == QMenuScroller::ScrollTop) {
00506         for(int i = 0, saccum = 0; i < actionList.count(); i++) {
00507             QAction *act = actionList.at(i);
00508             if (act == action) {
00509                 newOffset = topScroll - saccum;
00510                 break;
00511             }
00512             saccum += actionRects.value(act).height();
00513         }
00514     } else {
00515         for(int i = 0, saccum = 0; i < actionList.count(); i++) {
00516             QAction *act = actionList.at(i);
00517             saccum += actionRects.value(act).height();
00518             if (act == action) {
00519                 if (location == QMenuScroller::ScrollCenter)
00520                     newOffset = ((q->height() / 2) - botScroll) - saccum;
00521                 else
00522                     newOffset = (q->height() - botScroll) - saccum;
00523                 break;
00524             }
00525         }
00526         if(newOffset)
00527             newOffset -= vmargin*2;
00528     }
00529 
00530     //figure out which scroll flags
00531     uint newScrollFlags = QMenuScroller::ScrollNone;
00532     if (newOffset < 0) //easy and cheap one
00533         newScrollFlags |= QMenuScroller::ScrollUp;
00534     for(int i = 0, saccum=newOffset+topScroll; i < actionList.count(); i++) {
00535         saccum += actionRects.value(actionList.at(i)).height();
00536         if (saccum > q->height()) {
00537             newScrollFlags |= QMenuScroller::ScrollDown;
00538             break;
00539         }
00540     }
00541 
00542     if (location == QMenuScroller::ScrollTop && ((newScrollFlags & QMenuScroller::ScrollUp)
00543                                                  || !(scroll->scrollFlags & QMenuScroller::ScrollUp)))
00544         newOffset += scrollHeight;
00545     else if (location == QMenuScroller::ScrollBottom && ((newScrollFlags & QMenuScroller::ScrollDown)
00546                                                          || !(scroll->scrollFlags & QMenuScroller::ScrollDown)))
00547         newOffset -= scrollHeight;
00548 
00549     QRect screen = popupGeometry(QApplication::desktop()->screenNumber(q));
00550     const int desktopFrame = q->style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, q);
00551     if (q->height() < screen.height()-(desktopFrame*2)-1) {
00552         QRect geom = q->geometry();
00553         if (newOffset > scroll->scrollOffset && (scroll->scrollFlags & newScrollFlags & QMenuScroller::ScrollUp)) { //scroll up
00554             geom.setHeight(geom.height()-(newOffset-scroll->scrollOffset));
00555         } else if(scroll->scrollFlags & newScrollFlags & QMenuScroller::ScrollDown) {
00556             geom.setTop(geom.top() + (newOffset-scroll->scrollOffset));
00557             if (geom != q->geometry()) {
00558                 newOffset = 0;
00559                 newScrollFlags &= ~QMenuScroller::ScrollUp;
00560             }
00561         }
00562         if (geom.bottom() > screen.bottom() - desktopFrame)
00563             geom.setBottom(screen.bottom() - desktopFrame);
00564         if (geom.top() < desktopFrame+screen.top())
00565             geom.setTop(desktopFrame+screen.top());
00566         if (geom != q->geometry()) {
00567 #if 0
00568             if (newScrollFlags & QMenuScroller::ScrollDown &&
00569                q->geometry().top() - geom.top() >= -newOffset)
00570                 newScrollFlags &= ~QMenuScroller::ScrollDown;
00571 #endif
00572             q->setGeometry(geom);
00573         }
00574     }
00575 
00576     //actually update flags
00577     scroll->scrollOffset = newOffset;
00578     if (scroll->scrollOffset > 0)
00579         scroll->scrollOffset = 0;
00580     scroll->scrollFlags = newScrollFlags;
00581     if (active)
00582         setCurrentAction(action);
00583 
00584     q->update();     //issue an update so we see all the new state..
00585 }
00586 
00587 //only directional
00588 void QMenuPrivate::scrollMenu(QMenuScroller::ScrollDirection direction, bool page, bool active)
00589 {
00590     Q_Q(QMenu);
00591     if (!scroll || !(scroll->scrollFlags & direction)) //not really possible...
00592         return;
00593     const int scrollHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q);
00594     const int topScroll = (scroll->scrollFlags & QMenuScroller::ScrollUp)   ? scrollHeight : 0;
00595     const int botScroll = (scroll->scrollFlags & QMenuScroller::ScrollDown) ? scrollHeight : 0;
00596     if (direction == QMenuScroller::ScrollUp) {
00597         for(int i = 0, saccum = 0; i < actionList.count(); i++) {
00598             QAction *act = actionList.at(i);
00599             const int iHeight = actionRects.value(act).height();
00600             saccum -= iHeight;
00601             if (saccum <= scroll->scrollOffset-topScroll) {
00602                 scrollMenu(act, page ? QMenuScroller::ScrollBottom : QMenuScroller::ScrollTop, active);
00603                 break;
00604             }
00605         }
00606     } else if (direction == QMenuScroller::ScrollDown) {
00607         for(int i = 0, saccum = 0; i < actionList.count(); i++) {
00608             QAction *act = actionList.at(i);
00609             const int iHeight = actionRects.value(act).height();
00610             if (saccum <= scroll->scrollOffset-topScroll) {
00611                 const int scrollerArea = q->height() - botScroll;
00612                 int visible = -(((scroll->scrollOffset-topScroll) - saccum) - iHeight);
00613                 for(i++ ; i < actionList.count(); i++) {
00614                     act = actionList.at(i);
00615                     const int iHeight = actionRects.value(act).height();
00616                     visible += iHeight;
00617                     if (visible > scrollerArea-topScroll) {
00618                         scrollMenu(act, page ? QMenuScroller::ScrollTop : QMenuScroller::ScrollBottom, active);
00619                         break;
00620                     }
00621                 }
00622                 break;
00623             }
00624             saccum -= iHeight;
00625         }
00626     }
00627 }
00628 
00629 /* This is poor-mans eventfilters. This avoids the use of
00630    eventFilter (which can be nasty for users of QMenuBar's). */
00631 bool QMenuPrivate::mouseEventTaken(QMouseEvent *e)
00632 {
00633     Q_Q(QMenu);
00634     QPoint pos = q->mapFromGlobal(e->globalPos());
00635     if (scroll && !activeMenu) { //let the scroller "steal" the event
00636         bool isScroll = false;
00637         if (pos.x() >= 0 && pos.x() < q->width()) {
00638             const int scrollerHeight = q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q);
00639             for(int dir = QMenuScroller::ScrollUp; dir <= QMenuScroller::ScrollDown; dir = dir << 1) {
00640                 if (scroll->scrollFlags & dir) {
00641                     if (dir == QMenuScroller::ScrollUp)
00642                         isScroll = (pos.y() <= scrollerHeight);
00643                     else if (dir == QMenuScroller::ScrollDown)
00644                         isScroll = (pos.y() >= q->height()-scrollerHeight);
00645                     if (isScroll) {
00646                         scroll->scrollDirection = dir;
00647                         break;
00648                     }
00649                 }
00650             }
00651         }
00652         if (isScroll) {
00653             if (!scroll->scrollTimer)
00654                 scroll->scrollTimer = new QBasicTimer;
00655             scroll->scrollTimer->start(50, q);
00656             return true;
00657         } else if (scroll->scrollTimer && scroll->scrollTimer->isActive()) {
00658             scroll->scrollTimer->stop();
00659         }
00660     }
00661 
00662     if (tearoff) { //let the tear off thingie "steal" the event..
00663         QRect tearRect(0, 0, q->width(), q->style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, q));
00664         if (scroll && scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
00665             tearRect.translate(0, q->style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, q));
00666         q->update(tearRect);
00667         if (tearRect.contains(pos)) {
00668             setCurrentAction(0);
00669             tearoffHighlighted = 1;
00670             if (e->type() == QEvent::MouseButtonRelease) {
00671                 if (tornPopup) {
00672                     tornPopup->close();
00673                 } else {
00674                     tornPopup = new QTornOffMenu(q);
00675                     tornPopup->setGeometry(q->geometry());
00676                     tornPopup->show();
00677                 }
00678                 hideUpToMenuBar();
00679             }
00680             return true;
00681         }
00682         tearoffHighlighted = 0;
00683     }
00684 
00685     if (q->frameGeometry().contains(e->globalPos())) //otherwise if the event is in our rect we want it..
00686         return false;
00687 
00688     for(QWidget *caused = causedPopup.widget; caused;) {
00689         bool passOnEvent = false;
00690         QWidget *next_widget = 0;
00691         QPoint cpos = caused->mapFromGlobal(e->globalPos());
00692 #ifndef QT_NO_MENUBAR
00693         if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
00694             passOnEvent = mb->rect().contains(cpos);
00695         } else
00696 #endif
00697         if (QMenu *m = qobject_cast<QMenu*>(caused)) {
00698             passOnEvent = m->d_func()->actionAt(cpos);
00699             next_widget = m->d_func()->causedPopup.widget;
00700         }
00701         if (passOnEvent) {
00702             QMouseEvent new_e(e->type(), cpos, e->button(), e->buttons(), e->modifiers());
00703             QApplication::sendEvent(caused, &new_e);
00704             return true;
00705         }
00706         if (!next_widget)
00707             break;
00708         caused = next_widget;
00709     }
00710     return false;
00711 }
00712 
00713 void QMenuPrivate::activateAction(QAction *action, QAction::ActionEvent action_e)
00714 {
00715     Q_Q(QMenu);
00716 #ifndef QT_NO_WHATSTHIS
00717     bool inWhatsThisMode = QWhatsThis::inWhatsThisMode();
00718 #endif
00719     if (!action || !q->isEnabled()
00720         || (action_e == QAction::Trigger
00721 #ifndef QT_NO_WHATSTHIS
00722             && !inWhatsThisMode
00723 #endif
00724             && (action->isSeparator() ||!action->isEnabled())))
00725         return;
00726 
00727     /* I have to save the caused stack here because it will be undone after popup execution (ie in the hide).
00728        Then I iterate over the list to actually send the events. --Sam
00729     */
00730     QList<QPointer<QWidget> > causedStack;
00731     for(QWidget *widget = causedPopup.widget; widget; ) {
00732         causedStack.append(widget);
00733         if (QMenu *qmenu = ::qobject_cast<QMenu*>(widget))
00734             widget = qmenu->d_func()->causedPopup.widget;
00735         else
00736             break;
00737     }
00738     if (action_e == QAction::Trigger) {
00739         hideUpToMenuBar();
00740 #ifndef QT_NO_WHATSTHIS
00741         if (inWhatsThisMode) {
00742             QString s = action->whatsThis();
00743             if (s.isEmpty())
00744                 s = whatsThis;
00745             QWhatsThis::showText(q->mapToGlobal(actionRect(action).center()), s, q);
00746             return;
00747         }
00748 #endif
00749     }
00750 
00751     action->activate(action_e);
00752 
00753     for(int i = 0; i < causedStack.size(); ++i) {
00754         QPointer<QWidget> widget = causedStack.at(i);
00755         if (!widget)
00756             continue;
00757         //fire
00758         if (QMenu *qmenu = ::qobject_cast<QMenu*>(widget)) {
00759             widget = qmenu->d_func()->causedPopup.widget;
00760             if (action_e == QAction::Trigger) {
00761                 emit qmenu->triggered(action);
00762 #ifdef QT3_SUPPORT
00763                 emit qmenu->activated(qmenu->findIdForAction(action));
00764 #endif
00765             } else if (action_e == QAction::Hover) {
00766                 emit qmenu->hovered(action);
00767 #ifdef QT3_SUPPORT
00768                 emit qmenu->highlighted(qmenu->findIdForAction(action));
00769 #endif
00770             }
00771 #ifndef QT_NO_MENUBAR
00772         } else if (QMenuBar *qmenubar = ::qobject_cast<QMenuBar*>(widget)) {
00773             if (action_e == QAction::Trigger) {
00774                 emit qmenubar->triggered(action);
00775 #ifdef QT3_SUPPORT
00776                 emit qmenubar->activated(qmenubar->findIdForAction(action));
00777 #endif
00778             } else if (action_e == QAction::Hover) {
00779                 emit qmenubar->hovered(action);
00780 #ifdef QT3_SUPPORT
00781                 emit qmenubar->highlighted(qmenubar->findIdForAction(action));
00782 #endif
00783             }
00784             break; //nothing more..
00785 #endif
00786         }
00787     }
00788 
00789     if (action_e == QAction::Hover) {
00790 #ifndef QT_NO_ACCESSIBILITY
00791         int actionID = indexOf(action);
00792         QAccessible::updateAccessibility(q, actionID, QAccessible::Focus);
00793         QAccessible::updateAccessibility(q, actionID, QAccessible::Selection);
00794 #endif
00795         QWidget *w = causedPopup.widget;
00796         while (QMenu *m = qobject_cast<QMenu*>(w))
00797             w = m->d_func()->causedPopup.widget;
00798         action->showStatusText(w);
00799     }
00800 }
00801 
00802 void QMenuPrivate::_q_actionTriggered()
00803 {
00804     Q_Q(QMenu);
00805     if (QAction *action = qobject_cast<QAction *>(q->sender())) {
00806         emit q->triggered(action);
00807 #ifdef QT3_SUPPORT
00808         emit q->activated(q->findIdForAction(action));
00809 #endif
00810     }
00811 }
00812 
00813 void QMenuPrivate::_q_actionHovered()
00814 {
00815     Q_Q(QMenu);
00816     if (QAction *action = qobject_cast<QAction *>(q->sender())) {
00817         emit q->hovered(action);
00818 #ifdef QT3_SUPPORT
00819         emit q->highlighted(q->findIdForAction(action));
00820 #endif
00821     }
00822 }
00823 
00824 QStyleOptionMenuItem QMenuPrivate::getStyleOption(const QAction *action) const
00825 {
00826     Q_Q(const QMenu);
00827     QStyleOptionMenuItem opt;
00828     opt.initFrom(q);
00829     opt.palette = q->palette();
00830     opt.state = QStyle::State_None;
00831 
00832     if (q->window()->isActiveWindow())
00833         opt.state |= QStyle::State_Active;
00834     if (q->isEnabled() && action->isEnabled()
00835             && (!action->menu() || action->menu()->isEnabled()))
00836         opt.state |= QStyle::State_Enabled;
00837     else
00838         opt.palette.setCurrentColorGroup(QPalette::Disabled);
00839 
00840     opt.font = action->font();
00841 
00842     if (currentAction && currentAction == action && !currentAction->isSeparator()) {
00843         opt.state |= QStyle::State_Selected
00844                      | (mouseDown ? QStyle::State_Sunken : QStyle::State_None);
00845     }
00846 
00847     opt.menuHasCheckableItems = hasCheckableItems;
00848     if (!action->isCheckable()) {
00849         opt.checkType = QStyleOptionMenuItem::NotCheckable;
00850     } else {
00851         opt.checkType = (action->actionGroup() && action->actionGroup()->isExclusive())
00852                             ? QStyleOptionMenuItem::Exclusive : QStyleOptionMenuItem::NonExclusive;
00853         opt.checked = action->isChecked();
00854     }
00855     if (action->menu())
00856         opt.menuItemType = QStyleOptionMenuItem::SubMenu;
00857     else if (action->isSeparator())
00858         opt.menuItemType = QStyleOptionMenuItem::Separator;
00859     else if (defaultAction == action)
00860       opt.menuItemType = QStyleOptionMenuItem::DefaultItem;
00861     else
00862         opt.menuItemType = QStyleOptionMenuItem::Normal;
00863     opt.icon = action->icon();
00864     QString textAndAccel = action->text();
00865 #ifndef QT_NO_SHORTCUT
00866     if (textAndAccel.indexOf(QLatin1Char('\t')) == -1) {
00867         QKeySequence seq = action->shortcut();
00868         if (!seq.isEmpty())
00869             textAndAccel += QLatin1Char('\t') + QString(seq);
00870     }
00871 #endif
00872     opt.text = textAndAccel;
00873     opt.tabWidth = tabWidth;
00874     opt.maxIconWidth = maxIconWidth;
00875     opt.menuRect = q->rect();
00876     return opt;
00877 }
00878 
00947 QMenu::QMenu(QWidget *parent)
00948     : QWidget(*new QMenuPrivate, parent, Qt::Popup)
00949 {
00950     Q_D(QMenu);
00951 #ifndef QT_NO_WHATSTHIS
00952     setAttribute(Qt::WA_CustomWhatsThis);
00953 #endif
00954     setMouseTracking(style()->styleHint(QStyle::SH_Menu_MouseTracking, 0, this));
00955     if (style()->styleHint(QStyle::SH_Menu_Scrollable, 0, this)) {
00956         d->scroll = new QMenuPrivate::QMenuScroller;
00957         d->scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
00958     }
00959     d->menuAction = new QAction(this);
00960     d->menuAction->d_func()->menu = this;
00961 }
00962 
00972 QMenu::QMenu(const QString &title, QWidget *parent)
00973     : QWidget(*new QMenuPrivate, parent, Qt::Popup)
00974 {
00975     Q_D(QMenu);
00976 #ifndef QT_NO_WHATSTHIS
00977     setAttribute(Qt::WA_CustomWhatsThis);
00978 #endif
00979     setMouseTracking(style()->styleHint(QStyle::SH_Menu_MouseTracking));
00980     if (style()->styleHint(QStyle::SH_Menu_Scrollable, 0, this)) {
00981         d->scroll = new QMenuPrivate::QMenuScroller;
00982         d->scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
00983     }
00984     d->menuAction = new QAction(title, this);
00985     d->menuAction->d_func()->menu = this;
00986 }
00987 
00991 QMenu::~QMenu()
00992 {
00993     Q_D(QMenu);
00994     if (d->eventLoop)
00995         d->eventLoop->exit();
00996     if (d->tornPopup)
00997         d->tornPopup->close();
00998 }
00999 
01009 QAction *QMenu::addAction(const QString &text)
01010 {
01011     QAction *ret = new QAction(text, this);
01012     addAction(ret);
01013     return ret;
01014 }
01015 
01025 QAction *QMenu::addAction(const QIcon &icon, const QString &text)
01026 {
01027     QAction *ret = new QAction(icon, text, this);
01028     addAction(ret);
01029     return ret;
01030 }
01031 
01043 QAction *QMenu::addAction(const QString &text, const QObject *receiver, const char* member, const QKeySequence &shortcut)
01044 {
01045     QAction *action = new QAction(text, this);
01046 #ifdef QT_NO_SHORTCUT
01047     Q_UNUSED(shortcut);
01048 #else
01049     action->setShortcut(shortcut);
01050 #endif
01051     QObject::connect(action, SIGNAL(triggered()), receiver, member);
01052     addAction(action);
01053     return action;
01054 }
01055 
01067 QAction *QMenu::addAction(const QIcon &icon, const QString &text, const QObject *receiver,
01068                           const char* member, const QKeySequence &shortcut)
01069 {
01070     QAction *action = new QAction(icon, text, this);
01071 #ifdef QT_NO_SHORTCUT
01072     Q_UNUSED(shortcut);
01073 #else
01074     action->setShortcut(shortcut);
01075 #endif
01076     QObject::connect(action, SIGNAL(triggered()), receiver, member);
01077     addAction(action);
01078     return action;
01079 }
01080 
01086 QAction *QMenu::addMenu(QMenu *menu)
01087 {
01088     QAction *action = menu->menuAction();
01089     addAction(action);
01090     return action;
01091 }
01092 
01099 QMenu *QMenu::addMenu(const QString &title)
01100 {
01101     QMenu *menu = new QMenu(title, this);
01102     addAction(menu->menuAction());
01103     return menu;
01104 }
01105 
01112 QMenu *QMenu::addMenu(const QIcon &icon, const QString &title)
01113 {
01114     QMenu *menu = new QMenu(title, this);
01115     menu->setIcon(icon);
01116     addAction(menu->menuAction());
01117     return menu;
01118 }
01119 
01128 QAction *QMenu::addSeparator()
01129 {
01130     QAction *action = new QAction(this);
01131     action->setSeparator(true);
01132     addAction(action);
01133     return action;
01134 }
01135 
01142 QAction *QMenu::insertMenu(QAction *before, QMenu *menu)
01143 {
01144     QAction *action = menu->menuAction();
01145     insertAction(before, action);
01146     return action;
01147 }
01148 
01157 QAction *QMenu::insertSeparator(QAction *before)
01158 {
01159     QAction *action = new QAction(this);
01160     action->setSeparator(true);
01161     insertAction(before, action);
01162     return action;
01163 }
01164 
01173 void QMenu::setDefaultAction(QAction *act)
01174 {
01175     d_func()->defaultAction = act;
01176 }
01177 
01183 QAction *QMenu::defaultAction() const
01184 {
01185     return d_func()->defaultAction;
01186 }
01187 
01198 void QMenu::setTearOffEnabled(bool b)
01199 {
01200     Q_D(QMenu);
01201     if (d->tearoff == b)
01202         return;
01203     if (!b && d->tornPopup)
01204         d->tornPopup->close();
01205     d->tearoff = b;
01206 
01207     d->itemsDirty = true;
01208     if (isVisible())
01209         resize(sizeHint());
01210 }
01211 
01212 bool QMenu::isTearOffEnabled() const
01213 {
01214     return d_func()->tearoff;
01215 }
01216 
01224 bool QMenu::isTearOffMenuVisible() const
01225 {
01226     if (d_func()->tornPopup)
01227         return d_func()->tornPopup->isVisible();
01228     return false;
01229 }
01230 
01237 void QMenu::hideTearOffMenu()
01238 {
01239     if (d_func()->tornPopup)
01240         d_func()->tornPopup->close();
01241 }
01242 
01243 
01247 void QMenu::setActiveAction(QAction *act)
01248 {
01249     Q_D(QMenu);
01250     d->setCurrentAction(act, 0);
01251     if (d->scroll)
01252         d->scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollCenter);
01253 }
01254 
01255 
01260 QAction *QMenu::activeAction() const
01261 {
01262     return d_func()->currentAction;
01263 }
01264 
01274 bool QMenu::isEmpty() const
01275 {
01276     return actions().isEmpty();
01277 }
01278 
01285 void QMenu::clear()
01286 {
01287     QList<QAction*> acts = actions();
01288     for(int i = 0; i < acts.size(); i++) {
01289         removeAction(acts[i]);
01290         if (acts[i]->parent() == this && acts[i]->d_func()->widgets.isEmpty())
01291             delete acts[i];
01292     }
01293 }
01294 
01302 int QMenu::columnCount() const
01303 {
01304     return d_func()->ncols;
01305 }
01306 
01310 QAction *QMenu::actionAt(const QPoint &pt) const
01311 {
01312     if (QAction *ret = d_func()->actionAt(pt))
01313         return ret;
01314     return 0;
01315 }
01316 
01320 QRect QMenu::actionGeometry(QAction *act) const
01321 {
01322     return d_func()->actionRect(act);
01323 }
01324 
01328 QSize QMenu::sizeHint() const
01329 {
01330     Q_D(const QMenu);
01331     ensurePolished();
01332     QMap<QAction*, QRect> actionRects;
01333     QList<QAction*> actionList;
01334     d->calcActionRects(actionRects, actionList);
01335 
01336     QSize s;
01337     QStyleOption opt(0);
01338     opt.rect = rect();
01339     opt.palette = palette();
01340     opt.state = QStyle::State_None;
01341     for (QMap<QAction*, QRect>::const_iterator i = actionRects.constBegin();
01342          i != actionRects.constEnd(); ++i) {
01343         if (i.value().bottom() > s.height())
01344             s.setHeight(i.value().y()+i.value().height());
01345         if (i.value().right() > s.width())
01346             s.setWidth(i.value().right());
01347     }
01348     if (d->tearoff)
01349         s.rheight() += style()->pixelMetric(QStyle::PM_MenuTearoffHeight, &opt, this);
01350     if (const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, &opt, this)) {
01351         s.rwidth() += fw*2;
01352         s.rheight() += fw*2;
01353     }
01354     s.rwidth() += 2 * style()->pixelMetric(QStyle::PM_MenuHMargin, &opt, this);
01355     s.rheight() += 2 * style()->pixelMetric(QStyle::PM_MenuVMargin, &opt, this);
01356 
01357     s += QSize(d->leftmargin + d->rightmargin, d->topmargin + d->bottommargin);
01358 
01359     return style()->sizeFromContents(QStyle::CT_Menu, &opt,
01360                                     s.expandedTo(QApplication::globalStrut()), this);
01361 }
01362 
01377 void QMenu::popup(const QPoint &p, QAction *atAction)
01378 {
01379     Q_D(QMenu);
01380     if (d->scroll) { //reset scroll state from last popup
01381         d->scroll->scrollOffset = 0;
01382         d->scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
01383     }
01384     d->tearoffHighlighted = 0;
01385     d->motions = 0;
01386     d->doChildEffects = true;
01387 
01388     ensurePolished(); // Get the right font
01389     emit aboutToShow();
01390     d->updateActions();
01391     QPoint pos = p;
01392     QSize size = sizeHint();
01393     QRect screen = d->popupGeometry(QApplication::desktop()->screenNumber(p));
01394     const int desktopFrame = style()->pixelMetric(QStyle::PM_MenuDesktopFrameWidth, 0, this);
01395     if (d->ncols > 1) {
01396         pos.setY(screen.top()+desktopFrame);
01397     } else if (atAction) {
01398         for(int i=0, above_height=0; i<(int)d->actionList.count(); i++) {
01399             QAction *action = d->actionList.at(i);
01400             if (action == atAction) {
01401                 int newY = pos.y()-above_height;
01402                 if (d->scroll && newY < desktopFrame) {
01403                     d->scroll->scrollFlags = d->scroll->scrollFlags
01404                                              | QMenuPrivate::QMenuScroller::ScrollUp;
01405                     d->scroll->scrollOffset = newY;
01406                     newY = desktopFrame;
01407                 }
01408                 pos.setY(newY);
01409 
01410                 if (d->scroll && d->scroll->scrollFlags != QMenuPrivate::QMenuScroller::ScrollNone
01411                     && !style()->styleHint(QStyle::SH_Menu_FillScreenWithScroll, 0, this)) {
01412                     int below_height = above_height + d->scroll->scrollOffset;
01413                     for(int i2 = i; i2 < (int)d->actionList.count(); i2++)
01414                         below_height += d->actionRects.value(d->actionList.at(i2)).height();
01415                     size.setHeight(below_height);
01416                 }
01417                 break;
01418             } else {
01419                 above_height += d->actionRects.value(action).height();
01420             }
01421         }
01422     }
01423 
01424     QPoint mouse = QCursor::pos();
01425     const bool snapToMouse = (p == mouse);
01426 
01427     //handle popup falling "off screen"
01428     if (qApp->layoutDirection() == Qt::RightToLeft) {
01429         if(snapToMouse) //position flowing left from the mouse
01430             pos.setX(mouse.x()-size.width());
01431 
01432         if (pos.x() < screen.left()+desktopFrame)
01433             pos.setX(qMax(p.x(), screen.left()+desktopFrame));
01434         if (pos.x()+size.width() > screen.right()-desktopFrame)
01435             pos.setX(qMax(p.x()-size.width(), screen.right()-desktopFrame-size.width()));
01436     } else {
01437         if (pos.x()+size.width() > screen.right()-desktopFrame)
01438             pos.setX(qMin(p.x()-size.width(), screen.right()-desktopFrame-size.width()));
01439         if (pos.x() < screen.left()+desktopFrame)
01440             pos.setX(qMax(p.x(), screen.left() + desktopFrame));
01441     }
01442     if (pos.y() + size.height() > screen.bottom() - desktopFrame) {
01443         if(snapToMouse)
01444             pos.setY(qMin(mouse.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()));
01445         else
01446             pos.setY(qMax(p.y() - (size.height() + desktopFrame), screen.bottom()-desktopFrame-size.height()));
01447     } else if (pos.y() < screen.top()) {
01448         pos.setY(screen.top());
01449     }
01450 
01451     if (pos.y() < screen.top())
01452         pos.setY(screen.top());
01453     if (pos.y()+size.height() > screen.bottom() - desktopFrame) {
01454         if (d->scroll) {
01455             d->scroll->scrollFlags |= uint(QMenuPrivate::QMenuScroller::ScrollDown);
01456             int y = qMax(screen.y(),pos.y());
01457             size.setHeight(screen.height()-desktopFrame*2-y);
01458         } else {
01459             // Too big for screen, bias to see bottom of menu (for some reason)
01460             pos.setY(screen.bottom()-size.height());
01461         }
01462     }
01463     setGeometry(QRect(pos, size));
01464 
01465 #ifndef QT_NO_EFFECTS
01466     int hGuess = qApp->layoutDirection() == Qt::RightToLeft ? QEffects::LeftScroll : QEffects::RightScroll;
01467     int vGuess = QEffects::DownScroll;
01468     if (qApp->layoutDirection() == Qt::RightToLeft) {
01469         if ((snapToMouse && (pos.x() + size.width()/2 > mouse.x())) ||
01470            (qobject_cast<QMenu*>(d->causedPopup.widget) && pos.x() + size.width()/2 > d->causedPopup.widget->x()))
01471             hGuess = QEffects::RightScroll;
01472     } else {
01473         if ((snapToMouse && (pos.x() + size.width()/2 < mouse.x())) ||
01474            (qobject_cast<QMenu*>(d->causedPopup.widget) && pos.x() + size.width()/2 < d->causedPopup.widget->x()))
01475             hGuess = QEffects::LeftScroll;
01476     }
01477 
01478 #ifndef QT_NO_MENUBAR
01479     if ((snapToMouse && (pos.y() + size.height()/2 < mouse.y())) ||
01480        (qobject_cast<QMenuBar*>(d->causedPopup.widget) &&
01481         pos.y() + size.width()/2 < d->causedPopup.widget->mapToGlobal(d->causedPopup.widget->pos()).y()))
01482        vGuess = QEffects::UpScroll;
01483 #endif
01484     if (QApplication::isEffectEnabled(Qt::UI_AnimateMenu)) {
01485         bool doChildEffects = true;
01486 #ifndef QT_NO_MENUBAR
01487         if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->causedPopup.widget)) {
01488             doChildEffects = mb->d_func()->doChildEffects;
01489             mb->d_func()->doChildEffects = false;
01490         } else
01491 #endif
01492         if (QMenu *m = qobject_cast<QMenu*>(d->causedPopup.widget)) {
01493             doChildEffects = m->d_func()->doChildEffects;
01494             m->d_func()->doChildEffects = false;
01495         }
01496 
01497         if (doChildEffects) {
01498             if (QApplication::isEffectEnabled(Qt::UI_FadeMenu))
01499                 qFadeEffect(this);
01500             else if (d->causedPopup.widget)
01501                 qScrollEffect(this, qobject_cast<QMenu*>(d->causedPopup.widget) ? hGuess : vGuess);
01502             else
01503                 qScrollEffect(this, hGuess | vGuess);
01504         } else {
01505             // kill any running effect
01506             qFadeEffect(0);
01507             qScrollEffect(0);
01508 
01509             show();
01510         }
01511     } else
01512 #endif
01513     {
01514         show();
01515     }
01516 
01517 #ifndef QT_NO_ACCESSIBILITY
01518     QAccessible::updateAccessibility(this, 0, QAccessible::PopupMenuStart);
01519 #endif
01520 }
01521 
01545 QAction *QMenu::exec()
01546 {
01547     createWinId();
01548     return exec(pos());
01549 }
01550 
01551 
01593 QAction *QMenu::exec(const QPoint &p, QAction *action)
01594 {
01595     Q_D(QMenu);
01596     QEventLoop eventLoop;
01597     d->eventLoop = &eventLoop;
01598     popup(p, action);
01599 
01600     QPointer<QObject> guard = this;
01601     (void) eventLoop.exec();
01602     if (guard.isNull())
01603         return 0;
01604 
01605     action = d->syncAction;
01606     d->syncAction = 0;
01607     d->eventLoop = 0;
01608     return action;
01609 }
01610 
01636 QAction *QMenu::exec(QList<QAction*> actions, const QPoint &pos, QAction *at)
01637 {
01638     QMenu menu;
01639     for(QList<QAction*>::Iterator it = actions.begin(); it != actions.end(); ++it)
01640         menu.addAction((*it));
01641     return menu.exec(pos, at);
01642 }
01643 
01647 void QMenu::hideEvent(QHideEvent *)
01648 {
01649     Q_D(QMenu);
01650     emit aboutToHide();
01651     if (d->eventLoop)
01652         d->eventLoop->exit();
01653     d->setCurrentAction(0);
01654 #ifndef QT_NO_ACCESSIBILITY
01655     QAccessible::updateAccessibility(this, 0, QAccessible::PopupMenuEnd);
01656 #endif
01657 #ifndef QT_NO_MENUBAR
01658     if (QMenuBar *mb = qobject_cast<QMenuBar*>(d->causedPopup.widget))
01659         mb->d_func()->setCurrentAction(0);
01660 #endif
01661     d->mouseDown = false;
01662     d->hasHadMouse = false;
01663     d->causedPopup.widget = 0;
01664     d->causedPopup.action = 0;
01665 }
01666 
01670 void QMenu::paintEvent(QPaintEvent *e)
01671 {
01672     Q_D(QMenu);
01673     QPainter p(this);
01674     QRegion emptyArea = QRegion(rect());
01675 
01676     //draw the items that need updating..
01677     for (int i = 0; i < d->actionList.count(); ++i) {
01678         QAction *action = d->actionList.at(i);
01679         QRect adjustedActionRect = d->actionRect(action);
01680         if (!e->rect().intersects(adjustedActionRect)
01681             || d->widgetItems.value(action))
01682            continue;
01683         //set the clip region to be extra safe (and adjust for the scrollers)
01684         QRegion adjustedActionReg(adjustedActionRect);
01685         emptyArea -= adjustedActionReg;
01686         p.setClipRegion(adjustedActionReg);
01687 
01688         QStyleOptionMenuItem opt = d->getStyleOption(action);
01689         opt.rect = adjustedActionRect;
01690         style()->drawControl(QStyle::CE_MenuItem, &opt, &p, this);
01691     }
01692 
01693     const int fw = style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, this);
01694     QStyleOptionMenuItem menuOpt;
01695     menuOpt.initFrom(this);
01696     menuOpt.palette = palette();
01697     menuOpt.state = QStyle::State_None;
01698     menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
01699     menuOpt.menuRect = rect();
01700     menuOpt.maxIconWidth = 0;
01701     menuOpt.tabWidth = 0;
01702     //draw the scroller regions..
01703     if (d->scroll) {
01704         const int scrollerHeight = style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this);
01705         menuOpt.menuItemType = QStyleOptionMenuItem::Scroller;
01706         menuOpt.state |= QStyle::State_Enabled;
01707         if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp) {
01708             menuOpt.rect.setRect(fw, fw, width() - (fw * 2), scrollerHeight);
01709             emptyArea -= QRegion(menuOpt.rect);
01710             p.setClipRect(menuOpt.rect);
01711             style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this);
01712         }
01713         if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown) {
01714             menuOpt.rect.setRect(fw, height() - scrollerHeight - fw, width() - (fw * 2),
01715                                      scrollerHeight);
01716             emptyArea -= QRegion(menuOpt.rect);
01717             menuOpt.state |= QStyle::State_DownArrow;
01718             p.setClipRect(menuOpt.rect);
01719             style()->drawControl(QStyle::CE_MenuScroller, &menuOpt, &p, this);
01720         }
01721     }
01722     //paint the tear off..
01723     if (d->tearoff) {
01724         menuOpt.menuItemType = QStyleOptionMenuItem::TearOff;
01725         menuOpt.rect.setRect(fw, fw, width() - (fw * 2),
01726                              style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this));
01727         if (d->scroll && d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
01728             menuOpt.rect.translate(0, style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this));
01729         emptyArea -= QRegion(menuOpt.rect);
01730         p.setClipRect(menuOpt.rect);
01731         menuOpt.state = QStyle::State_None;
01732         if (d->tearoffHighlighted)
01733             menuOpt.state |= QStyle::State_Selected;
01734         style()->drawControl(QStyle::CE_MenuTearoff, &menuOpt, &p, this);
01735     }
01736     //draw border
01737     if (fw) {
01738         QRegion borderReg;
01739         borderReg += QRect(0, 0, fw, height()); //left
01740         borderReg += QRect(width()-fw, 0, fw, height()); //right
01741         borderReg += QRect(0, 0, width(), fw); //top
01742         borderReg += QRect(0, height()-fw, width(), fw); //bottom
01743         p.setClipRegion(borderReg);
01744         emptyArea -= borderReg;
01745         QStyleOptionFrame frame;
01746         frame.rect = rect();
01747         frame.palette = palette();
01748         frame.state = QStyle::State_None;
01749         frame.lineWidth = style()->pixelMetric(QStyle::PM_MenuPanelWidth);
01750         frame.midLineWidth = 0;
01751         style()->drawPrimitive(QStyle::PE_FrameMenu, &frame, &p, this);
01752     }
01753 
01754     //finally the rest of the space
01755     p.setClipRegion(emptyArea);
01756     menuOpt.state = QStyle::State_None;
01757     menuOpt.menuItemType = QStyleOptionMenuItem::EmptyArea;
01758     menuOpt.checkType = QStyleOptionMenuItem::NotCheckable;
01759     menuOpt.rect = rect();
01760     menuOpt.menuRect = rect();
01761     style()->drawControl(QStyle::CE_MenuEmptyArea, &menuOpt, &p, this);
01762 }
01763 
01764 #ifndef QT_NO_WHEELEVENT
01765 
01768 void QMenu::wheelEvent(QWheelEvent *e)
01769 {
01770     Q_D(QMenu);
01771     if (d->scroll && rect().contains(e->pos()))
01772         d->scrollMenu(e->delta() > 0 ?
01773                       QMenuPrivate::QMenuScroller::ScrollUp : QMenuPrivate::QMenuScroller::ScrollDown);
01774 }
01775 #endif
01776 
01780 void QMenu::mousePressEvent(QMouseEvent *e)
01781 {
01782     Q_D(QMenu);
01783     if (d->mouseEventTaken(e))
01784         return;
01785     if (!rect().contains(e->pos())) {
01786          if (d->noReplayFor
01787              && QRect(d->noReplayFor->mapToGlobal(QPoint()), d->noReplayFor->size()).contains(e->globalPos()))
01788              setAttribute(Qt::WA_NoMouseReplay);
01789          if (d->eventLoop) // synchronous operation
01790              d->syncAction = 0;
01791         d->hideUpToMenuBar();
01792         return;
01793     }
01794     d->mouseDown = true;
01795 
01796     QAction *action = d->actionAt(e->pos());
01797     d->setCurrentAction(action, 20);
01798     update();
01799 }
01800 
01804 void QMenu::mouseReleaseEvent(QMouseEvent *e)
01805 {
01806     Q_D(QMenu);
01807     if (d->mouseEventTaken(e))
01808         return;
01809 
01810     d->mouseDown = false;
01811     QAction *action = d->actionAt(e->pos());
01812     for(QWidget *caused = this; caused;) {
01813         if (QMenu *m = qobject_cast<QMenu*>(caused)) {
01814             QAction *currentAction = d->currentAction;
01815             if(currentAction && (!currentAction->isEnabled() || currentAction->menu() || currentAction->isSeparator()))
01816                 currentAction = 0;
01817             caused = m->d_func()->causedPopup.widget;
01818             if (m->d_func()->eventLoop)
01819                 m->d_func()->syncAction = currentAction; // synchronous operation
01820         } else {
01821             break;
01822         }
01823     }
01824     if (action && action == d->currentAction) {
01825         if (action->menu())
01826             action->menu()->d_func()->setFirstActionActive();
01827         else
01828             d->activateAction(action, QAction::Trigger);
01829     } else if (d->motions > 6) {
01830         d->hideUpToMenuBar();
01831     }
01832 }
01833 
01837 void QMenu::changeEvent(QEvent *e)
01838 {
01839     Q_D(QMenu);
01840     if (e->type() == QEvent::StyleChange || e->type() == QEvent::FontChange ||
01841         e->type() == QEvent::LayoutDirectionChange) {
01842         d->itemsDirty = 1;
01843         setMouseTracking(style()->styleHint(QStyle::SH_Menu_MouseTracking, 0, this));
01844         if (isVisible())
01845             resize(sizeHint());
01846         if (!style()->styleHint(QStyle::SH_Menu_Scrollable, 0, this)) {
01847             delete d->scroll;
01848             d->scroll = 0;
01849         } else if (!d->scroll) {
01850             d->scroll = new QMenuPrivate::QMenuScroller;
01851             d->scroll->scrollFlags = QMenuPrivate::QMenuScroller::ScrollNone;
01852         }
01853     } else if (e->type() == QEvent::EnabledChange) {
01854         if (d->tornPopup) // torn-off menu
01855             d->tornPopup->setEnabled(isEnabled());
01856         d->menuAction->setEnabled(isEnabled());
01857     }
01858     QWidget::changeEvent(e);
01859 }
01860 
01861 
01865 bool
01866 QMenu::event(QEvent *e)
01867 {
01868     Q_D(QMenu);
01869     switch (e->type()) {
01870     case QEvent::KeyPress: {
01871         QKeyEvent *ke = (QKeyEvent*)e;
01872         if (ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab) {
01873             keyPressEvent(ke);
01874             return true;
01875         }
01876     } break;
01877     case QEvent::Resize:
01878         d->itemsDirty = 1;
01879         d->updateActions();
01880         break;
01881     case QEvent::Show:
01882         d->updateActions();
01883         break;
01884 #ifndef QT_NO_WHATSTHIS
01885     case QEvent::QueryWhatsThis:
01886         e->setAccepted(d->whatsThis.size());
01887         if (QAction *action = d->actionAt(static_cast<QHelpEvent*>(e)->pos())) {
01888             if (action->whatsThis().size() || action->menu())
01889                 e->accept();
01890         }
01891         return true;
01892 #endif
01893     default:
01894         break;
01895     }
01896     return QWidget::event(e);
01897 }
01898 
01902 bool QMenu::focusNextPrevChild(bool next)
01903 {
01904     setFocus();
01905     QKeyEvent ev(QEvent::KeyPress, next ? Qt::Key_Tab : Qt::Key_Backtab, Qt::NoModifier);
01906     keyPressEvent(&ev);
01907     return true;
01908 }
01909 
01913 void QMenu::keyPressEvent(QKeyEvent *e)
01914 {
01915     Q_D(QMenu);
01916     int key = e->key();
01917     if (isRightToLeft()) {  // in reverse mode open/close key for submenues are reversed
01918         if (key == Qt::Key_Left)
01919             key = Qt::Key_Right;
01920         else if (key == Qt::Key_Right)
01921             key = Qt::Key_Left;
01922     }
01923     if (key == Qt::Key_Tab) //means down
01924         key = Qt::Key_Down;
01925 
01926     bool key_consumed = false;
01927     switch(key) {
01928 #ifdef Q_WS_MAC
01929     case Qt::Key_PageUp:
01930 #endif
01931     case Qt::Key_Home:
01932         key_consumed = true;
01933         if (d->scroll) {
01934             for(int i = 0; i < d->actionList.size(); ++i) {
01935                 QAction *act = d->actionList.at(i);
01936                 if (!act->isSeparator() &&
01937                     (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
01938                      || act->isEnabled())) {
01939                     if(d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
01940                         d->scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollTop, true);
01941                     else
01942                         d->setCurrentAction(act, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
01943                     break;
01944                 }
01945             }
01946         }
01947         break;
01948 #ifdef Q_WS_MAC
01949     case Qt::Key_PageDown:
01950 #endif
01951     case Qt::Key_End:
01952         key_consumed = true;
01953         if (d->scroll) {
01954             for(int i = d->actionList.size()-1; i >= 0; --i) {
01955                 QAction *act = d->actionList.at(i);
01956                 if (!act->isSeparator() &&
01957                     (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
01958                      || act->isEnabled())) {
01959                     if(d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)
01960                         d->scrollMenu(act, QMenuPrivate::QMenuScroller::ScrollBottom, true);
01961                     else
01962                         d->setCurrentAction(act, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
01963                     break;
01964                 }
01965             }
01966         }
01967         break;
01968 #ifndef Q_WS_MAC
01969     case Qt::Key_PageUp:
01970         key_consumed = true;
01971         if (d->currentAction && d->scroll)
01972             d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollUp, true, true);
01973         break;
01974     case Qt::Key_PageDown:
01975         key_consumed = true;
01976         if (d->currentAction && d->scroll)
01977             d->scrollMenu(QMenuPrivate::QMenuScroller::ScrollDown, true, true);
01978         break;
01979 #endif
01980     case Qt::Key_Up:
01981     case Qt::Key_Down: {
01982         key_consumed = true;
01983         QAction *nextAction = 0;
01984         QMenuPrivate::QMenuScroller::ScrollLocation scroll_loc = QMenuPrivate::QMenuScroller::ScrollStay;
01985         if (!d->currentAction) {
01986             if(key == Qt::Key_Down) {
01987                 for(int i = 0; i < d->actionList.size(); ++i) {
01988                     QAction *act = d->actionList.at(i);
01989                     if (!act->isSeparator() &&
01990                         (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
01991                          || act->isEnabled())) {
01992                         nextAction = act;
01993                         break;
01994                     }
01995                 }
01996             } else {
01997                 for(int i = d->actionList.size()-1; i >= 0; --i) {
01998                     QAction *act = d->actionList.at(i);
01999                     if (!act->isSeparator() &&
02000                         (style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)
02001                          || act->isEnabled())) {
02002                         nextAction = act;
02003                         break;
02004                     }
02005                 }
02006             }
02007         } else {
02008             for(int i=0, y=0; !nextAction && i < (int)d->actionList.count(); i++) {
02009                 QAction *act = d->actionList.at(i);
02010                 if (act == d->currentAction) {
02011                     if (key == Qt::Key_Up) {
02012                         for(int next_i = i-1; true; next_i--) {
02013                             if (next_i == -1) {
02014                                 if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this))
02015                                     break;
02016                                 if (d->scroll)
02017                                     scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
02018                                 next_i = d->actionList.count()-1;
02019                             }
02020                             QAction *next = d->actionList.at(next_i);
02021                             if (next == d->currentAction)
02022                                 break;
02023                             if (next->isSeparator() ||
02024                                (!next->isEnabled() &&
02025                                 !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)))
02026                                 continue;
02027                             nextAction = next;
02028                             if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)) {
02029                                 int topVisible = style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this);
02030                                 if (d->tearoff)
02031                                     topVisible += style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);
02032                                 if (((y + d->scroll->scrollOffset) - topVisible) <= d->actionRects.value(nextAction).height())
02033                                     scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
02034                             }
02035                             break;
02036                         }
02037                         if (!nextAction && d->tearoff)
02038                             d->tearoffHighlighted = 1;
02039                     } else {
02040                         y += d->actionRects.value(act).height();
02041                         for(int next_i = i+1; true; next_i++) {
02042                             if (next_i == d->actionList.count()) {
02043                                 if(!style()->styleHint(QStyle::SH_Menu_SelectionWrap, 0, this))
02044                                     break;
02045                                 if (d->scroll)
02046                                     scroll_loc = QMenuPrivate::QMenuScroller::ScrollTop;
02047                                 next_i = 0;
02048                             }
02049                             QAction *next = d->actionList.at(next_i);
02050                             if (next == d->currentAction)
02051                                 break;
02052                             if (next->isSeparator() ||
02053                                (!next->isEnabled() &&
02054                                 !style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, this)))
02055                                 continue;
02056                             nextAction = next;
02057                             if (d->scroll && (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollDown)) {
02058                                 const int scrollerHeight = style()->pixelMetric(QStyle::PM_MenuScrollerHeight, 0, this);
02059                                 int bottomVisible = height()-scrollerHeight;
02060                                 if (d->scroll->scrollFlags & QMenuPrivate::QMenuScroller::ScrollUp)
02061                                     bottomVisible -= scrollerHeight;
02062                                 if (d->tearoff)
02063                                     bottomVisible -= style()->pixelMetric(QStyle::PM_MenuTearoffHeight, 0, this);
02064                                 if ((y + d->scroll->scrollOffset + d->actionRects.value(nextAction).height()) > bottomVisible)
02065                                     scroll_loc = QMenuPrivate::QMenuScroller::ScrollBottom;
02066                             }
02067                             break;
02068                         }
02069                     }
02070                     break;
02071                 }
02072                 y += d->actionRects.value(act).height();
02073             }
02074         }
02075         if (nextAction) {
02076             if (d->scroll && scroll_loc != QMenuPrivate::QMenuScroller::ScrollStay) {
02077                 if (d->scroll->scrollTimer)
02078                     d->scroll->scrollTimer->stop();
02079                 d->scrollMenu(nextAction, scroll_loc);
02080             }
02081             d->setCurrentAction(nextAction, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
02082         }
02083         break; }
02084 
02085     case Qt::Key_Right:
02086         if (!d->currentAction) {
02087             d->setFirstActionActive();
02088             key_consumed = true;
02089             break;
02090         }
02091         if (d->currentAction && d->currentAction->isEnabled() && d->currentAction->menu()) {
02092             d->popupAction(d->currentAction, 0, true);
02093             key_consumed = true;
02094             break;
02095         }
02096         //FALL THROUGH
02097     case Qt::Key_Left: {
02098         if (d->currentAction && !d->scroll) {
02099             QAction *nextAction = 0;
02100             if (key == Qt::Key_Left) {
02101                 QRect actionR = d->actionRect(d->currentAction);
02102                 for(int x = actionR.left()-1; !nextAction && x >= 0; x--)
02103                     nextAction = d->actionAt(QPoint(x, actionR.center().y()));
02104             } else {
02105                 QRect actionR = d->actionRect(d->currentAction);
02106                 for(int x = actionR.right()+1; !nextAction && x < width(); x++)
02107                     nextAction = d->actionAt(QPoint(x, actionR.center().y()));
02108             }
02109             if (nextAction) {
02110                 d->setCurrentAction(nextAction, /*popup*/-1, QMenuPrivate::SelectedFromKeyboard);
02111                 key_consumed = true;
02112             }
02113         }
02114         if (!key_consumed && key == Qt::Key_Left && d->causedPopup.widget &&
02115             qobject_cast<QMenu*>(d->causedPopup.widget)) {
02116             QPointer<QWidget> caused = d->causedPopup.widget;
02117             hide();
02118             if (caused)
02119                 caused->setFocus();
02120             key_consumed = true;
02121         }
02122         break; }
02123 
02124     case Qt::Key_Alt:
02125         key_consumed = true;
02126         if ( style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, 0, this))
02127         {
02128             hide();
02129 #ifndef QT_NO_MENUBAR
02130             if (QMenuBar *mb = qobject_cast<QMenuBar*>(qApp->focusWidget())) {
02131                 mb->d_func()->setKeyboardMode(false);
02132             }
02133 #endif
02134         }
02135         break;
02136 
02137     case Qt::Key_Escape:
02138 #ifdef QT_KEYPAD_NAVIGATION
02139     case Qt::Key_Back:
02140 #endif
02141         key_consumed = true;
02142         if (d->tornoff) {
02143             close();
02144             return;
02145         }
02146         {
02147             QPointer<QWidget> caused = d->causedPopup.widget;
02148             hide(); //hide after getting causedPopup
02149 #ifndef QT_NO_MENUBAR
02150             if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
02151                 mb->d_func()->setCurrentAction(d->menuAction);
02152                 mb->d_func()->setKeyboardMode(true);
02153             }
02154 #endif
02155         }
02156         break;
02157 
02158     case Qt::Key_Space:
02159         if (!style()->styleHint(QStyle::SH_Menu_SpaceActivatesItem, 0, this))
02160             break;
02161         // for motif, fall through
02162 #ifdef QT_KEYPAD_NAVIGATION
02163     case Qt::Key_Select:
02164 #endif
02165     case Qt::Key_Return:
02166     case Qt::Key_Enter: {
02167         if (!d->currentAction) {
02168             d->setFirstActionActive();
02169             key_consumed = true;
02170             break;
02171         }
02172 
02173         for(QWidget *caused = this; caused;) {
02174             if (QMenu *m = qobject_cast<QMenu*>(caused)) {
02175                 QAction *currentAction = d->currentAction;
02176                 if(currentAction && (!currentAction->isEnabled() || currentAction->menu() || currentAction->isSeparator()))
02177                     currentAction = 0;
02178                 caused = m->d_func()->causedPopup.widget;
02179                 if (m->d_func()->eventLoop)
02180                     m->d_func()->syncAction = currentAction; // synchronous operation
02181             } else {
02182                 break;
02183             }
02184         }
02185 
02186         if (d->currentAction->menu())
02187             d->popupAction(d->currentAction, 0, true);
02188         else
02189             d->activateAction(d->currentAction, QAction::Trigger);
02190         key_consumed = true;
02191         break; }
02192 
02193 #ifndef QT_NO_WHATSTHIS
02194     case Qt::Key_F1:
02195         if (!d->currentAction || d->currentAction->whatsThis().isNull())
02196             break;
02197         QWhatsThis::enterWhatsThisMode();
02198         d->activateAction(d->currentAction, QAction::Trigger);
02199         return;
02200 #endif
02201     default:
02202         key_consumed = false;
02203     }
02204 
02205     if (!key_consumed) {                                // send to menu bar
02206         if ((!e->modifiers() || e->modifiers() == Qt::AltModifier || e->modifiers() == Qt::ShiftModifier) &&
02207            e->text().length()==1) {
02208             bool activateAction = false;
02209             QAction *nextAction = 0;
02210             if (style()->styleHint(QStyle::SH_Menu_KeyboardSearch, 0, this) && !e->modifiers()) {
02211                 int best_match_count = 0;
02212                 d->searchBufferTimer.start(2000, this);
02213                 d->searchBuffer += e->text();
02214                 for(int i = 0; i < d->actionList.size(); ++i) {
02215                     int match_count = 0;
02216                     register QAction *act = d->actionList.at(i);
02217                     const QString act_text = act->text();
02218                     for(int c = 0; c < d->searchBuffer.size(); ++c) {
02219                         if(act_text.indexOf(d->searchBuffer.at(c), 0, Qt::CaseInsensitive) != -1)
02220                             ++match_count;
02221                     }
02222                     if(match_count > best_match_count) {
02223                         best_match_count = match_count;
02224                         nextAction = act;
02225                     }
02226                 }
02227             }
02228 #ifndef QT_NO_SHORTCUT
02229             else {
02230                 int clashCount = 0;
02231                 QAction *first = 0, *currentSelected = 0, *firstAfterCurrent = 0;
02232                 QChar c = e->text().at(0).toUpper();
02233                 for(int i = 0; i < d->actionList.size(); ++i) {
02234                     register QAction *act = d->actionList.at(i);
02235                     QKeySequence sequence = QKeySequence::mnemonic(act->text());
02236                     int key = sequence[0] & 0xffff;
02237                     if (key == c.unicode()) {
02238                         clashCount++;
02239                         if (!first)
02240                             first = act;
02241                         if (act == d->currentAction)
02242                             currentSelected = act;
02243                         else if (!firstAfterCurrent && currentSelected)
02244                             firstAfterCurrent = act;
02245                     }
02246                 }
02247                 if (clashCount == 1)
02248                     activateAction = true;
02249                 if (clashCount >= 1) {
02250                     if (clashCount == 1 || !currentSelected || !firstAfterCurrent)
02251                         nextAction = first;
02252                     else
02253                         nextAction = firstAfterCurrent;
02254                 }
02255             }
02256 #endif
02257             if (nextAction) {
02258                 key_consumed = true;
02259                 d->setCurrentAction(nextAction, 20, QMenuPrivate::SelectedFromElsewhere, true);
02260                 if (!nextAction->menu() && activateAction)
02261                     d->activateAction(nextAction, QAction::Trigger);
02262             }
02263         }
02264         if (!key_consumed) {
02265             if (QWidget *caused = d->causedPopup.widget) {
02266                 while(QMenu *m = qobject_cast<QMenu*>(caused))
02267                     caused = m->d_func()->causedPopup.widget;
02268 #ifndef QT_NO_MENUBAR
02269                 if (QMenuBar *mb = qobject_cast<QMenuBar*>(caused)) {
02270                     QAction *oldAct = mb->d_func()->currentAction;
02271                     QApplication::sendEvent(mb, e);
02272                     if (mb->d_func()->currentAction != oldAct)
02273                         key_consumed = true;
02274                 }
02275 #endif
02276             }
02277         }
02278 
02279 #ifdef Q_OS_WIN32
02280         if (key_consumed && (e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta))
02281             qApp->beep();
02282 #endif // Q_OS_WIN32
02283     }
02284     if (key_consumed)
02285         e->accept();
02286     else
02287         e->ignore();
02288 }
02289 
02293 void QMenu::mouseMoveEvent(QMouseEvent *e)
02294 {
02295     Q_D(QMenu);
02296     if (!isVisible() || d->mouseEventTaken(e))
02297         return;
02298     d->motions++;
02299     if (d->motions == 0) // ignore first mouse move event (see enterEvent())
02300         return;
02301     d->hasHadMouse |= rect().contains(e->pos());
02302 
02303     QAction *action = d->actionAt(e->pos());
02304     if (!action) {
02305         if (d->hasHadMouse && !rect().contains(e->pos()))
02306             d->setCurrentAction(0);
02307         return;
02308     } else {
02309         d->mouseDown = e->buttons() & Qt::LeftButton;
02310     }
02311     if (d->sloppyRegion.contains(e->pos())) {
02312         d->sloppyAction = action;
02313         QMenuPrivate::sloppyDelayTimer.start(style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this)*6, this);
02314     } else {
02315         d->setCurrentAction(action, style()->styleHint(QStyle::SH_Menu_SubMenuPopupDelay, 0, this));
02316     }
02317 }
02318 
02322 void QMenu::enterEvent(QEvent *)
02323 {
02324     d_func()->motions = -1; // force us to ignore the generate mouse move in mouseMoveEvent()
02325 }
02326 
02330 void QMenu::leaveEvent(QEvent *)
02331 {
02332     Q_D(QMenu);
02333     d->sloppyAction = 0;
02334     if (!d->sloppyRegion.isEmpty())
02335         d->sloppyRegion = QRegion();
02336 }
02337 
02341 void
02342 QMenu::timerEvent(QTimerEvent *e)
02343 {
02344     Q_D(QMenu);
02345     if (d->scroll && d->scroll->scrollTimer && d->scroll->scrollTimer->timerId() == e->timerId()) {
02346         d->scrollMenu((QMenuPrivate::QMenuScroller::ScrollDirection)d->scroll->scrollDirection);
02347         if (d->scroll->scrollFlags == QMenuPrivate::QMenuScroller::ScrollNone)
02348             d->scroll->scrollTimer->stop();
02349     } else if(QMenuPrivate::menuDelayTimer.timerId() == e->timerId()) {
02350         QMenuPrivate::menuDelayTimer.stop();
02351         internalDelayedPopup();
02352     } else if(QMenuPrivate::sloppyDelayTimer.timerId() == e->timerId()) {
02353         QMenuPrivate::sloppyDelayTimer.stop();
02354         internalSetSloppyAction();
02355     } else if(d->searchBufferTimer.timerId() == e->timerId()) {
02356         d->searchBuffer.clear();
02357     }
02358 }
02359 
02363 void QMenu::actionEvent(QActionEvent *e)
02364 {
02365     Q_D(QMenu);
02366     d->itemsDirty = 1;
02367     setAttribute(Qt::WA_Resized, false);
02368     if (d->tornPopup)
02369         d->tornPopup->syncWithMenu(this, e);
02370     if (e->type() == QEvent::ActionAdded) {
02371         connect(e->action(), SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
02372         connect(e->action(), SIGNAL(hovered()), this, SLOT(_q_actionHovered()));
02373 
02374         if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
02375             QWidget *widget = wa->requestWidget(this);
02376             if (widget)
02377                 d->widgetItems.insert(wa, widget);
02378         }
02379     } else if (e->type() == QEvent::ActionRemoved) {
02380         d->actionRects.clear();
02381         d->actionList.clear();
02382         e->action()->disconnect(this);
02383         if (e->action() == d->currentAction)
02384             d->currentAction = 0;
02385         if (QWidgetAction *wa = qobject_cast<QWidgetAction *>(e->action())) {
02386             QWidget *widget = d->widgetItems.take(wa);
02387             if (widget)
02388                 wa->releaseWidget(widget);
02389         }
02390     }
02391 
02392 #ifdef Q_WS_MAC
02393     if (d->mac_menu) {
02394         if (e->type() == QEvent::ActionAdded)
02395             d->mac_menu->addAction(e->action(), d->mac_menu->findAction(e->before()), d);
02396         else if (e->type() == QEvent::ActionRemoved)
02397             d->mac_menu->removeAction(e->action());
02398         else if (e->type() == QEvent::ActionChanged)
02399             d->mac_menu->syncAction(e->action());
02400     }
02401 #endif
02402 
02403     if (isVisible()) {
02404         d->updateActions();
02405   resize(sizeHint());
02406         update();
02407     }
02408 }
02409 
02413 void QMenu::internalSetSloppyAction()
02414 {
02415     if (d_func()->sloppyAction)
02416         d_func()->setCurrentAction(d_func()->sloppyAction, 0);
02417 }
02418 
02422 void QMenu::internalDelayedPopup()
02423 {
02424     Q_D(QMenu);
02425 
02426     //hide the current item
02427     if (QMenu *menu = d->activeMenu) {
02428         d->activeMenu = 0;
02429         menu->hide();
02430     }
02431 
02432     if (!d->currentAction || !d->currentAction->isEnabled() || !d->currentAction->menu() ||
02433         !d->currentAction->menu()->isEnabled() || d->currentAction->menu()->isVisible())
02434         return;
02435 
02436     //setup
02437     d->activeMenu = d->currentAction->menu();
02438     d->activeMenu->d_func()->causedPopup.widget = this;
02439     d->activeMenu->d_func()->causedPopup.action = d->currentAction;
02440 
02441     const QRect actionRect(d->actionRect(d->currentAction));
02442     const QSize menuSize(d->activeMenu->sizeHint());
02443     const QPoint rightPos(mapToGlobal(QPoint(actionRect.right(), actionRect.top())));
02444     const QPoint leftPos(mapToGlobal(QPoint(actionRect.left() - menuSize.width(), actionRect.top())));
02445 
02446     QPoint pos(rightPos);
02447     QMenu *caused = qobject_cast<QMenu*>(d->activeMenu->d_func()->causedPopup.widget);
02448     const QRect availGeometry(d->popupGeometry(QApplication::desktop()->screenNumber(caused)));
02449     if (isRightToLeft()) {
02450         pos = leftPos;
02451         if (caused && caused->x() < x() || pos.x() < availGeometry.left()) {
02452             if(rightPos.x() + menuSize.width() < availGeometry.right())
02453                 pos = rightPos;
02454             else
02455                 pos.rx() = availGeometry.left();
02456         }
02457     } else {
02458         if (caused && caused->x() > x() || pos.x() + menuSize.width() > availGeometry.right()) {
02459             if(leftPos.x() < availGeometry.left())
02460                 pos.rx() = availGeometry.right() - menuSize.width();
02461             else
02462                 pos = leftPos;
02463         }
02464     }
02465 
02466     //calc sloppy focus buffer
02467     if (style()->styleHint(QStyle::SH_Menu_SloppySubMenus, 0, this)) {
02468         QPoint cur = QCursor::pos();
02469         if (actionRect.contains(mapFromGlobal(cur))) {
02470             QPoint pts[4];
02471             pts[0] = QPoint(cur.x(), cur.y() - 2);
02472             pts[3] = QPoint(cur.x(), cur.y() + 2);
02473             if (pos.x() >= cur.x())        {
02474                 pts[1] = QPoint(geometry().right(), pos.y());
02475                 pts[2] = QPoint(geometry().right(), pos.y() + menuSize.height());
02476             } else {
02477                 pts[1] = QPoint(pos.x() + menuSize.width(), pos.y());
02478                 pts[2] = QPoint(pos.x() + menuSize.width(), pos.y() + menuSize.height());
02479             }
02480             QPolygon points(4);
02481             for(int i = 0; i < 4; i++)
02482                 points.setPoint(i, mapFromGlobal(pts[i]));
02483             d->sloppyRegion = QRegion(points);
02484         }
02485     }
02486 
02487     //do the popup
02488     d->activeMenu->popup(pos);
02489 }
02490 
02537 void QMenu::setNoReplayFor(QWidget *noReplayFor)
02538 {
02539     d_func()->noReplayFor = noReplayFor;
02540 }
02541 
02552 bool QMenu::separatorsCollapsible() const
02553 {
02554     Q_D(const QMenu);
02555     return d->collapsibleSeparators;
02556 }
02557 
02558 void QMenu::setSeparatorsCollapsible(bool collapse)
02559 {
02560     Q_D(QMenu);
02561     d->collapsibleSeparators = collapse;
02562     d->itemsDirty = 1;
02563     if (isVisible()) {
02564         d->updateActions();
02565         update();
02566     }
02567 }
02568 
02569 #ifdef QT3_SUPPORT
02570 
02571 int QMenu::insertAny(const QIcon *icon, const QString *text, const QObject *receiver, const char *member,
02572                           const QKeySequence *shortcut, const QMenu *popup, int id, int index)
02573 {
02574     QAction *act = popup ? popup->menuAction() : new QAction(this);
02575     if (id != -1)
02576         static_cast<QMenuItem*>(act)->setId(id);
02577     if (icon)
02578         act->setIcon(*icon);
02579     if (text)
02580         act->setText(*text);
02581     if (shortcut)
02582         act->setShortcut(*shortcut);
02583     if (receiver && member)
02584         QObject::connect(act, SIGNAL(activated(int)), receiver, member);
02585     if (index == -1 || index >= actions().count())
02586         addAction(act);
02587     else
02588         insertAction(actions().value(index), act);
02589     return findIdForAction(act);
02590 }
02591 
02595 int QMenu::insertItem(QMenuItem *item, int id, int index)
02596 {
02597     if (index == -1 || index >= actions().count())
02598         addAction(item);
02599     else
02600         insertAction(actions().value(index), item);
02601     if (id > -1)
02602         item->d_func()->id = id;
02603     return findIdForAction(item);
02604 }
02605 
02610 int QMenu::insertSeparator(int index)
02611 {
02612     QAction *act = new QAction(this);
02613     act->setSeparator(true);
02614     if (index == -1 || index >= actions().count())
02615         addAction(act);
02616     else
02617         insertAction(actions().value(index), act);
02618     return findIdForAction(act);
02619 }
02620 
02621 QAction *QMenu::findActionForId(int id) const
02622 {
02623     QList<QAction *> list = actions();
02624     for (int i = 0; i < list.size(); ++i) {
02625         QAction *act = list.at(i);
02626         if (findIdForAction(act)== id)
02627             return act;
02628     }
02629     return 0;
02630 }
02631 
02635 QMenuItem *QMenu::findPopup( QMenu *popup, int *index )
02636 {
02637    QList<QAction *> list = actions();
02638     for (int i = 0; i < list.size(); ++i) {
02639         QAction *act = list.at(i);
02640         if (act->menu() == popup) {
02641             QMenuItem *item = static_cast<QMenuItem *>(act);
02642             if (index)
02643                 *index = act->d_func()->id;
02644             return item;
02645         }
02646     }
02647     return 0;
02648 }
02649 
02650 
02654 bool QMenu::setItemParameter(int id, int param)
02655 {
02656     if (QAction *act = findActionForId(id)) {
02657         act->d_func()->param = param;
02658         return true;
02659     }
02660     return false;
02661 }
02662 
02666 int QMenu::itemParameter(int id) const
02667 {
02668     if (QAction *act = findActionForId(id))
02669         return act->d_func()->param;
02670     return id;
02671 }
02672 
02676 void QMenu::setId(int index, int id)
02677 {
02678     if(QAction *act = actions().value(index))
02679         act->d_func()->id = id;
02680 }
02681 
02685 int QMenu::frameWidth() const
02686 {
02687     return style()->pixelMetric(QStyle::PM_MenuPanelWidth, 0, this);
02688 }
02689 
02690 int QMenu::findIdForAction(QAction *act) const
02691 {
02692     if (!act)
02693         return -1;
02694     return act->d_func()->id;
02695 }
02696 #endif // QT3_SUPPORT
02697 
02992 // for private slots
02993 
02994 #include "moc_qmenu.cpp"
02995 #endif // QT_NO_MENU
02996 

Generated on Thu Mar 15 11:57:03 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1