00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
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
00063
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
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
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
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
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
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
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
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;
00213
00214
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);
00231 rect.setWidth(max_column_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
00281 if (!visibleActions.isEmpty())
00282 visibleActions.append(action);
00283
00284
00285 while (i < actions.count()
00286 && (!actions.at(i)->isVisible() || actions.at(i)->isSeparator()))
00287 ++i;
00288 }
00289
00290 if (collapsibleSeparators) {
00291
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();
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) {
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
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;
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
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))
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
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
00531 uint newScrollFlags = QMenuScroller::ScrollNone;
00532 if (newOffset < 0)
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)) {
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
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();
00585 }
00586
00587
00588 void QMenuPrivate::scrollMenu(QMenuScroller::ScrollDirection direction, bool page, bool active)
00589 {
00590 Q_Q(QMenu);
00591 if (!scroll || !(scroll->scrollFlags & direction))
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
00630
00631 bool QMenuPrivate::mouseEventTaken(QMouseEvent *e)
00632 {
00633 Q_Q(QMenu);
00634 QPoint pos = q->mapFromGlobal(e->globalPos());
00635 if (scroll && !activeMenu) {
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) {
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()))
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
00728
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
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;
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) {
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();
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
01428 if (qApp->layoutDirection() == Qt::RightToLeft) {
01429 if(snapToMouse)
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
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
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
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
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
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
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
01737 if (fw) {
01738 QRegion borderReg;
01739 borderReg += QRect(0, 0, fw, height());
01740 borderReg += QRect(width()-fw, 0, fw, height());
01741 borderReg += QRect(0, 0, width(), fw);
01742 borderReg += QRect(0, height()-fw, width(), fw);
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
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)
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;
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)
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()) {
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)
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, -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, -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, -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
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, -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();
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
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;
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) {
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)
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;
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
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
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
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
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
02993
02994 #include "moc_qmenu.cpp"
02995 #endif // QT_NO_MENU
02996