| 1 | /****************************************************************************
|
|---|
| 2 | **
|
|---|
| 3 | ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
|
|---|
| 4 | ** All rights reserved.
|
|---|
| 5 | ** Contact: Nokia Corporation ([email protected])
|
|---|
| 6 | **
|
|---|
| 7 | ** This file is part of the QtGui module of the Qt Toolkit.
|
|---|
| 8 | **
|
|---|
| 9 | ** $QT_BEGIN_LICENSE:LGPL$
|
|---|
| 10 | ** Commercial Usage
|
|---|
| 11 | ** Licensees holding valid Qt Commercial licenses may use this file in
|
|---|
| 12 | ** accordance with the Qt Commercial License Agreement provided with the
|
|---|
| 13 | ** Software or, alternatively, in accordance with the terms contained in
|
|---|
| 14 | ** a written agreement between you and Nokia.
|
|---|
| 15 | **
|
|---|
| 16 | ** GNU Lesser General Public License Usage
|
|---|
| 17 | ** Alternatively, this file may be used under the terms of the GNU Lesser
|
|---|
| 18 | ** General Public License version 2.1 as published by the Free Software
|
|---|
| 19 | ** Foundation and appearing in the file LICENSE.LGPL included in the
|
|---|
| 20 | ** packaging of this file. Please review the following information to
|
|---|
| 21 | ** ensure the GNU Lesser General Public License version 2.1 requirements
|
|---|
| 22 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
|---|
| 23 | **
|
|---|
| 24 | ** In addition, as a special exception, Nokia gives you certain additional
|
|---|
| 25 | ** rights. These rights are described in the Nokia Qt LGPL Exception
|
|---|
| 26 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
|---|
| 27 | **
|
|---|
| 28 | ** GNU General Public License Usage
|
|---|
| 29 | ** Alternatively, this file may be used under the terms of the GNU
|
|---|
| 30 | ** General Public License version 3.0 as published by the Free Software
|
|---|
| 31 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
|---|
| 32 | ** packaging of this file. Please review the following information to
|
|---|
| 33 | ** ensure the GNU General Public License version 3.0 requirements will be
|
|---|
| 34 | ** met: http://www.gnu.org/copyleft/gpl.html.
|
|---|
| 35 | **
|
|---|
| 36 | ** If you have questions regarding the use of this file, please contact
|
|---|
| 37 | ** Nokia at [email protected].
|
|---|
| 38 | ** $QT_END_LICENSE$
|
|---|
| 39 | **
|
|---|
| 40 | ****************************************************************************/
|
|---|
| 41 |
|
|---|
| 42 | #include <qmenubar.h>
|
|---|
| 43 |
|
|---|
| 44 | #include <qstyle.h>
|
|---|
| 45 | #include <qlayout.h>
|
|---|
| 46 | #include <qapplication.h>
|
|---|
| 47 | #include <qdesktopwidget.h>
|
|---|
| 48 | #ifndef QT_NO_ACCESSIBILITY
|
|---|
| 49 | # include <qaccessible.h>
|
|---|
| 50 | #endif
|
|---|
| 51 | #include <qpainter.h>
|
|---|
| 52 | #include <qstylepainter.h>
|
|---|
| 53 | #include <qevent.h>
|
|---|
| 54 | #include <qmainwindow.h>
|
|---|
| 55 | #include <qtoolbar.h>
|
|---|
| 56 | #include <qtoolbutton.h>
|
|---|
| 57 | #include <qwhatsthis.h>
|
|---|
| 58 |
|
|---|
| 59 | #ifndef QT_NO_MENUBAR
|
|---|
| 60 |
|
|---|
| 61 | #ifdef QT3_SUPPORT
|
|---|
| 62 | #include <private/qaction_p.h>
|
|---|
| 63 | #include <qmenudata.h>
|
|---|
| 64 | #endif
|
|---|
| 65 |
|
|---|
| 66 | #include "qmenu_p.h"
|
|---|
| 67 | #include "qmenubar_p.h"
|
|---|
| 68 | #include "qdebug.h"
|
|---|
| 69 |
|
|---|
| 70 | #ifdef Q_WS_WINCE
|
|---|
| 71 | extern bool qt_wince_is_mobile(); //defined in qguifunctions_wce.cpp
|
|---|
| 72 | #endif
|
|---|
| 73 |
|
|---|
| 74 | #ifdef QT_SOFTKEYS_ENABLED
|
|---|
| 75 | #include <private/qsoftkeymanager_p.h>
|
|---|
| 76 | #endif
|
|---|
| 77 |
|
|---|
| 78 | QT_BEGIN_NAMESPACE
|
|---|
| 79 |
|
|---|
| 80 | class QMenuBarExtension : public QToolButton
|
|---|
| 81 | {
|
|---|
| 82 | public:
|
|---|
| 83 | explicit QMenuBarExtension(QWidget *parent);
|
|---|
| 84 |
|
|---|
| 85 | QSize sizeHint() const;
|
|---|
| 86 | void paintEvent(QPaintEvent *);
|
|---|
| 87 | };
|
|---|
| 88 |
|
|---|
| 89 | QMenuBarExtension::QMenuBarExtension(QWidget *parent)
|
|---|
| 90 | : QToolButton(parent)
|
|---|
| 91 | {
|
|---|
| 92 | setObjectName(QLatin1String("qt_menubar_ext_button"));
|
|---|
| 93 | setAutoRaise(true);
|
|---|
| 94 | #ifndef QT_NO_MENU
|
|---|
| 95 | setPopupMode(QToolButton::InstantPopup);
|
|---|
| 96 | #endif
|
|---|
| 97 | setIcon(style()->standardIcon(QStyle::SP_ToolBarHorizontalExtensionButton, 0, parentWidget()));
|
|---|
| 98 | }
|
|---|
| 99 |
|
|---|
| 100 | void QMenuBarExtension::paintEvent(QPaintEvent *)
|
|---|
| 101 | {
|
|---|
| 102 | QStylePainter p(this);
|
|---|
| 103 | QStyleOptionToolButton opt;
|
|---|
| 104 | initStyleOption(&opt);
|
|---|
| 105 | // We do not need to draw both extension arrows
|
|---|
| 106 | opt.features &= ~QStyleOptionToolButton::HasMenu;
|
|---|
| 107 | p.drawComplexControl(QStyle::CC_ToolButton, opt);
|
|---|
| 108 | }
|
|---|
| 109 |
|
|---|
| 110 |
|
|---|
| 111 | QSize QMenuBarExtension::sizeHint() const
|
|---|
| 112 | {
|
|---|
| 113 | int ext = style()->pixelMetric(QStyle::PM_ToolBarExtensionExtent, 0, parentWidget());
|
|---|
| 114 | return QSize(ext, ext);
|
|---|
| 115 | }
|
|---|
| 116 |
|
|---|
| 117 |
|
|---|
| 118 | /*!
|
|---|
| 119 | \internal
|
|---|
| 120 | */
|
|---|
| 121 | QAction *QMenuBarPrivate::actionAt(QPoint p) const
|
|---|
| 122 | {
|
|---|
| 123 | for(int i = 0; i < actions.size(); ++i) {
|
|---|
| 124 | if(actionRect(actions.at(i)).contains(p))
|
|---|
| 125 | return actions.at(i);
|
|---|
| 126 | }
|
|---|
| 127 | return 0;
|
|---|
| 128 | }
|
|---|
| 129 |
|
|---|
| 130 | QRect QMenuBarPrivate::menuRect(bool extVisible) const
|
|---|
| 131 | {
|
|---|
| 132 | Q_Q(const QMenuBar);
|
|---|
| 133 |
|
|---|
| 134 | int hmargin = q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, q);
|
|---|
| 135 | QRect result = q->rect();
|
|---|
| 136 | result.adjust(hmargin, 0, -hmargin, 0);
|
|---|
| 137 |
|
|---|
| 138 | if (extVisible) {
|
|---|
| 139 | if (q->isRightToLeft())
|
|---|
| 140 | result.setLeft(result.left() + extension->sizeHint().width());
|
|---|
| 141 | else
|
|---|
| 142 | result.setWidth(result.width() - extension->sizeHint().width());
|
|---|
| 143 | }
|
|---|
| 144 |
|
|---|
| 145 | if (leftWidget && leftWidget->isVisible()) {
|
|---|
| 146 | QSize sz = leftWidget->sizeHint();
|
|---|
| 147 | if (q->isRightToLeft())
|
|---|
| 148 | result.setRight(result.right() - sz.width());
|
|---|
| 149 | else
|
|---|
| 150 | result.setLeft(result.left() + sz.width());
|
|---|
| 151 | }
|
|---|
| 152 |
|
|---|
| 153 | if (rightWidget && rightWidget->isVisible()) {
|
|---|
| 154 | QSize sz = rightWidget->sizeHint();
|
|---|
| 155 | if (q->isRightToLeft())
|
|---|
| 156 | result.setLeft(result.left() + sz.width());
|
|---|
| 157 | else
|
|---|
| 158 | result.setRight(result.right() - sz.width());
|
|---|
| 159 | }
|
|---|
| 160 |
|
|---|
| 161 | return result;
|
|---|
| 162 | }
|
|---|
| 163 |
|
|---|
| 164 | bool QMenuBarPrivate::isVisible(QAction *action)
|
|---|
| 165 | {
|
|---|
| 166 | return !hiddenActions.contains(action);
|
|---|
| 167 | }
|
|---|
| 168 |
|
|---|
| 169 | void QMenuBarPrivate::updateGeometries()
|
|---|
| 170 | {
|
|---|
| 171 | Q_Q(QMenuBar);
|
|---|
| 172 | if(!itemsDirty)
|
|---|
| 173 | return;
|
|---|
| 174 | int q_width = q->width()-(q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, q)*2);
|
|---|
| 175 | int q_start = -1;
|
|---|
| 176 | if(leftWidget || rightWidget) {
|
|---|
| 177 | int vmargin = q->style()->pixelMetric(QStyle::PM_MenuBarVMargin, 0, q)
|
|---|
| 178 | + q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, q);
|
|---|
| 179 | int hmargin = q->style()->pixelMetric(QStyle::PM_MenuBarHMargin, 0, q)
|
|---|
| 180 | + q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, q);
|
|---|
| 181 | if (leftWidget && leftWidget->isVisible()) {
|
|---|
| 182 | QSize sz = leftWidget->sizeHint();
|
|---|
| 183 | q_width -= sz.width();
|
|---|
| 184 | q_start = sz.width();
|
|---|
| 185 | QPoint pos(hmargin, (q->height() - leftWidget->height()) / 2);
|
|---|
| 186 | QRect vRect = QStyle::visualRect(q->layoutDirection(), q->rect(), QRect(pos, sz));
|
|---|
| 187 | leftWidget->setGeometry(vRect);
|
|---|
| 188 | }
|
|---|
| 189 | if (rightWidget && rightWidget->isVisible()) {
|
|---|
| 190 | QSize sz = rightWidget->sizeHint();
|
|---|
| 191 | q_width -= sz.width();
|
|---|
| 192 | QPoint pos(q->width() - sz.width() - hmargin, vmargin);
|
|---|
| 193 | QRect vRect = QStyle::visualRect(q->layoutDirection(), q->rect(), QRect(pos, sz));
|
|---|
| 194 | rightWidget->setGeometry(vRect);
|
|---|
| 195 | }
|
|---|
| 196 | }
|
|---|
| 197 |
|
|---|
| 198 | #ifdef Q_WS_MAC
|
|---|
| 199 | if(q->isNativeMenuBar()) {//nothing to see here folks, move along..
|
|---|
| 200 | itemsDirty = false;
|
|---|
| 201 | return;
|
|---|
| 202 | }
|
|---|
| 203 | #endif
|
|---|
| 204 | calcActionRects(q_width, q_start);
|
|---|
| 205 | currentAction = 0;
|
|---|
| 206 | #ifndef QT_NO_SHORTCUT
|
|---|
| 207 | if(itemsDirty) {
|
|---|
| 208 | for(int j = 0; j < shortcutIndexMap.size(); ++j)
|
|---|
| 209 | q->releaseShortcut(shortcutIndexMap.value(j));
|
|---|
| 210 | shortcutIndexMap.resize(0); // faster than clear
|
|---|
| 211 | for(int i = 0; i < actions.count(); i++)
|
|---|
| 212 | shortcutIndexMap.append(q->grabShortcut(QKeySequence::mnemonic(actions.at(i)->text())));
|
|---|
| 213 | }
|
|---|
| 214 | #endif
|
|---|
| 215 | itemsDirty = false;
|
|---|
| 216 |
|
|---|
| 217 | hiddenActions.clear();
|
|---|
| 218 | //this is the menu rectangle without any extension
|
|---|
| 219 | QRect menuRect = this->menuRect(false);
|
|---|
| 220 |
|
|---|
| 221 | //we try to see if the actions will fit there
|
|---|
| 222 | bool hasHiddenActions = false;
|
|---|
| 223 | for (int i = 0; i < actions.count(); ++i) {
|
|---|
| 224 | const QRect &rect = actionRects.at(i);
|
|---|
| 225 | if (rect.isValid() && !menuRect.contains(rect)) {
|
|---|
| 226 | hasHiddenActions = true;
|
|---|
| 227 | break;
|
|---|
| 228 | }
|
|---|
| 229 | }
|
|---|
| 230 |
|
|---|
| 231 | //...and if not, determine the ones that fit on the menu with the extension visible
|
|---|
| 232 | if (hasHiddenActions) {
|
|---|
| 233 | menuRect = this->menuRect(true);
|
|---|
| 234 | for (int i = 0; i < actions.count(); ++i) {
|
|---|
| 235 | const QRect &rect = actionRects.at(i);
|
|---|
| 236 | if (rect.isValid() && !menuRect.contains(rect)) {
|
|---|
| 237 | hiddenActions.append(actions.at(i));
|
|---|
| 238 | }
|
|---|
| 239 | }
|
|---|
| 240 | }
|
|---|
| 241 |
|
|---|
| 242 | if (hiddenActions.count() > 0) {
|
|---|
| 243 | QMenu *pop = extension->menu();
|
|---|
| 244 | if (!pop) {
|
|---|
| 245 | pop = new QMenu(q);
|
|---|
| 246 | extension->setMenu(pop);
|
|---|
| 247 | }
|
|---|
| 248 | pop->clear();
|
|---|
| 249 | pop->addActions(hiddenActions);
|
|---|
| 250 |
|
|---|
| 251 | int vmargin = q->style()->pixelMetric(QStyle::PM_MenuBarVMargin, 0, q);
|
|---|
| 252 | int x = q->isRightToLeft()
|
|---|
| 253 | ? menuRect.left() - extension->sizeHint().width() + 1
|
|---|
| 254 | : menuRect.right();
|
|---|
| 255 | extension->setGeometry(x, vmargin, extension->sizeHint().width(), menuRect.height() - vmargin*2);
|
|---|
| 256 | extension->show();
|
|---|
| 257 | } else {
|
|---|
| 258 | extension->hide();
|
|---|
| 259 | }
|
|---|
| 260 | q->updateGeometry();
|
|---|
| 261 | #ifdef QT3_SUPPORT
|
|---|
| 262 | if (parent) {
|
|---|
| 263 | QMenubarUpdatedEvent menubarUpdated(q);
|
|---|
| 264 | QApplication::sendEvent(parent, &menubarUpdated);
|
|---|
| 265 | }
|
|---|
| 266 | #endif
|
|---|
| 267 | }
|
|---|
| 268 |
|
|---|
| 269 | QRect QMenuBarPrivate::actionRect(QAction *act) const
|
|---|
| 270 | {
|
|---|
| 271 | const int index = actions.indexOf(act);
|
|---|
| 272 |
|
|---|
| 273 | //makes sure the geometries are up-to-date
|
|---|
| 274 | const_cast<QMenuBarPrivate*>(this)->updateGeometries();
|
|---|
| 275 |
|
|---|
| 276 | if (index < 0 || index >= actionRects.count())
|
|---|
| 277 | return QRect(); // that can happen in case of native menubar
|
|---|
| 278 |
|
|---|
| 279 | return actionRects.at(index);
|
|---|
| 280 | }
|
|---|
| 281 |
|
|---|
| 282 | void QMenuBarPrivate::focusFirstAction()
|
|---|
| 283 | {
|
|---|
| 284 | if(!currentAction) {
|
|---|
| 285 | updateGeometries();
|
|---|
| 286 | int index = 0;
|
|---|
| 287 | while (index < actions.count() && actionRects.at(index).isNull()) ++index;
|
|---|
| 288 | if (index < actions.count())
|
|---|
| 289 | setCurrentAction(actions.at(index));
|
|---|
| 290 | }
|
|---|
| 291 | }
|
|---|
| 292 |
|
|---|
| 293 | void QMenuBarPrivate::setKeyboardMode(bool b)
|
|---|
| 294 | {
|
|---|
| 295 | Q_Q(QMenuBar);
|
|---|
| 296 | if (b && !q->style()->styleHint(QStyle::SH_MenuBar_AltKeyNavigation, 0, q)) {
|
|---|
| 297 | setCurrentAction(0);
|
|---|
| 298 | return;
|
|---|
| 299 | }
|
|---|
| 300 | keyboardState = b;
|
|---|
| 301 | if(b) {
|
|---|
| 302 | QWidget *fw = QApplication::focusWidget();
|
|---|
| 303 | if (fw != q)
|
|---|
| 304 | keyboardFocusWidget = fw;
|
|---|
| 305 | focusFirstAction();
|
|---|
| 306 | q->setFocus(Qt::MenuBarFocusReason);
|
|---|
| 307 | } else {
|
|---|
| 308 | if(!popupState)
|
|---|
| 309 | setCurrentAction(0);
|
|---|
| 310 | if(keyboardFocusWidget) {
|
|---|
| 311 | if (QApplication::focusWidget() == q)
|
|---|
| 312 | keyboardFocusWidget->setFocus(Qt::MenuBarFocusReason);
|
|---|
| 313 | keyboardFocusWidget = 0;
|
|---|
| 314 | }
|
|---|
| 315 | }
|
|---|
| 316 | q->update();
|
|---|
| 317 | }
|
|---|
| 318 |
|
|---|
| 319 | void QMenuBarPrivate::popupAction(QAction *action, bool activateFirst)
|
|---|
| 320 | {
|
|---|
| 321 | Q_Q(QMenuBar);
|
|---|
| 322 | if(!action || !action->menu() || closePopupMode)
|
|---|
| 323 | return;
|
|---|
| 324 | popupState = true;
|
|---|
| 325 | if (action->isEnabled() && action->menu()->isEnabled()) {
|
|---|
| 326 | closePopupMode = 0;
|
|---|
| 327 | activeMenu = action->menu();
|
|---|
| 328 | activeMenu->d_func()->causedPopup.widget = q;
|
|---|
| 329 | activeMenu->d_func()->causedPopup.action = action;
|
|---|
| 330 |
|
|---|
| 331 | QRect adjustedActionRect = actionRect(action);
|
|---|
| 332 | QPoint pos(q->mapToGlobal(QPoint(adjustedActionRect.left(), adjustedActionRect.bottom() + 1)));
|
|---|
| 333 | QSize popup_size = activeMenu->sizeHint();
|
|---|
| 334 |
|
|---|
| 335 | //we put the popup menu on the screen containing the bottom-center of the action rect
|
|---|
| 336 | QRect screenRect = QApplication::desktop()->screenGeometry(pos + QPoint(adjustedActionRect.width() / 2, 0));
|
|---|
| 337 | pos = QPoint(qMax(pos.x(), screenRect.x()), qMax(pos.y(), screenRect.y()));
|
|---|
| 338 |
|
|---|
| 339 | const bool fitUp = (q->mapToGlobal(adjustedActionRect.topLeft()).y() >= popup_size.height());
|
|---|
| 340 | const bool fitDown = (pos.y() + popup_size.height() <= screenRect.bottom());
|
|---|
| 341 | const bool rtl = q->isRightToLeft();
|
|---|
| 342 | const int actionWidth = adjustedActionRect.width();
|
|---|
| 343 |
|
|---|
| 344 | if (!fitUp && !fitDown) { //we should shift the menu
|
|---|
| 345 | bool shouldShiftToRight = !rtl;
|
|---|
| 346 | if (rtl && popup_size.width() > pos.x())
|
|---|
| 347 | shouldShiftToRight = true;
|
|---|
| 348 | else if (actionWidth + popup_size.width() + pos.x() > screenRect.right())
|
|---|
| 349 | shouldShiftToRight = false;
|
|---|
| 350 |
|
|---|
| 351 | if (shouldShiftToRight) {
|
|---|
| 352 | pos.rx() += actionWidth + (rtl ? popup_size.width() : 0);
|
|---|
| 353 | } else {
|
|---|
| 354 | //shift to left
|
|---|
| 355 | if (!rtl)
|
|---|
| 356 | pos.rx() -= popup_size.width();
|
|---|
| 357 | }
|
|---|
| 358 | } else if (rtl) {
|
|---|
| 359 | pos.rx() += actionWidth;
|
|---|
| 360 | }
|
|---|
| 361 |
|
|---|
| 362 | if(!defaultPopDown || (fitUp && !fitDown))
|
|---|
| 363 | pos.setY(qMax(screenRect.y(), q->mapToGlobal(QPoint(0, adjustedActionRect.top()-popup_size.height())).y()));
|
|---|
| 364 | activeMenu->popup(pos);
|
|---|
| 365 | if(activateFirst)
|
|---|
| 366 | activeMenu->d_func()->setFirstActionActive();
|
|---|
| 367 | }
|
|---|
| 368 | q->update(actionRect(action));
|
|---|
| 369 | }
|
|---|
| 370 |
|
|---|
| 371 | void QMenuBarPrivate::setCurrentAction(QAction *action, bool popup, bool activateFirst)
|
|---|
| 372 | {
|
|---|
| 373 | if(currentAction == action && popup == popupState)
|
|---|
| 374 | return;
|
|---|
| 375 |
|
|---|
| 376 | autoReleaseTimer.stop();
|
|---|
| 377 |
|
|---|
| 378 | doChildEffects = (popup && !activeMenu);
|
|---|
| 379 | Q_Q(QMenuBar);
|
|---|
| 380 | QWidget *fw = 0;
|
|---|
| 381 | if(QMenu *menu = activeMenu) {
|
|---|
| 382 | activeMenu = 0;
|
|---|
| 383 | if (popup) {
|
|---|
| 384 | fw = q->window()->focusWidget();
|
|---|
| 385 | q->setFocus(Qt::NoFocusReason);
|
|---|
| 386 | }
|
|---|
| 387 | menu->hide();
|
|---|
| 388 | }
|
|---|
| 389 |
|
|---|
| 390 | if(currentAction)
|
|---|
| 391 | q->update(actionRect(currentAction));
|
|---|
| 392 |
|
|---|
| 393 | popupState = popup;
|
|---|
| 394 | #ifndef QT_NO_STATUSTIP
|
|---|
| 395 | QAction *previousAction = currentAction;
|
|---|
| 396 | #endif
|
|---|
| 397 | currentAction = action;
|
|---|
| 398 | if (action) {
|
|---|
| 399 | activateAction(action, QAction::Hover);
|
|---|
| 400 | if(popup)
|
|---|
| 401 | popupAction(action, activateFirst);
|
|---|
| 402 | q->update(actionRect(action));
|
|---|
| 403 | #ifndef QT_NO_STATUSTIP
|
|---|
| 404 | } else if (previousAction) {
|
|---|
| 405 | QString empty;
|
|---|
| 406 | QStatusTipEvent tip(empty);
|
|---|
| 407 | QApplication::sendEvent(q, &tip);
|
|---|
| 408 | #endif
|
|---|
| 409 | }
|
|---|
| 410 | if (fw)
|
|---|
| 411 | fw->setFocus(Qt::NoFocusReason);
|
|---|
| 412 | }
|
|---|
| 413 |
|
|---|
| 414 | void QMenuBarPrivate::calcActionRects(int max_width, int start) const
|
|---|
| 415 | {
|
|---|
| 416 | Q_Q(const QMenuBar);
|
|---|
| 417 |
|
|---|
| 418 | if(!itemsDirty)
|
|---|
| 419 | return;
|
|---|
| 420 |
|
|---|
| 421 | //let's reinitialize the buffer
|
|---|
| 422 | actionRects.resize(actions.count());
|
|---|
| 423 | actionRects.fill(QRect());
|
|---|
| 424 |
|
|---|
| 425 | const QStyle *style = q->style();
|
|---|
| 426 |
|
|---|
| 427 | const int itemSpacing = style->pixelMetric(QStyle::PM_MenuBarItemSpacing, 0, q);
|
|---|
| 428 | int max_item_height = 0, separator = -1, separator_start = 0, separator_len = 0;
|
|---|
| 429 |
|
|---|
| 430 | //calculate size
|
|---|
| 431 | const QFontMetrics fm = q->fontMetrics();
|
|---|
| 432 | const int hmargin = style->pixelMetric(QStyle::PM_MenuBarHMargin, 0, q),
|
|---|
| 433 | vmargin = style->pixelMetric(QStyle::PM_MenuBarVMargin, 0, q),
|
|---|
| 434 | icone = style->pixelMetric(QStyle::PM_SmallIconSize, 0, q);
|
|---|
| 435 | for(int i = 0; i < actions.count(); i++) {
|
|---|
| 436 | QAction *action = actions.at(i);
|
|---|
| 437 | if(!action->isVisible())
|
|---|
| 438 | continue;
|
|---|
| 439 |
|
|---|
| 440 | QSize sz;
|
|---|
| 441 |
|
|---|
| 442 | //calc what I think the size is..
|
|---|
| 443 | if(action->isSeparator()) {
|
|---|
| 444 | if (style->styleHint(QStyle::SH_DrawMenuBarSeparator, 0, q))
|
|---|
| 445 | separator = i;
|
|---|
| 446 | continue; //we don't really position these!
|
|---|
| 447 | } else {
|
|---|
| 448 | const QString s = action->text();
|
|---|
| 449 | QIcon is = action->icon();
|
|---|
| 450 | // If an icon is set, only the icon is visible
|
|---|
| 451 | if (!is.isNull())
|
|---|
| 452 | sz = sz.expandedTo(QSize(icone, icone));
|
|---|
| 453 | else if (!s.isEmpty())
|
|---|
| 454 | sz = fm.size(Qt::TextShowMnemonic, s);
|
|---|
| 455 | }
|
|---|
| 456 |
|
|---|
| 457 | //let the style modify the above size..
|
|---|
| 458 | QStyleOptionMenuItem opt;
|
|---|
| 459 | q->initStyleOption(&opt, action);
|
|---|
| 460 | sz = q->style()->sizeFromContents(QStyle::CT_MenuBarItem, &opt, sz, q);
|
|---|
| 461 |
|
|---|
| 462 | if(!sz.isEmpty()) {
|
|---|
| 463 | { //update the separator state
|
|---|
| 464 | int iWidth = sz.width() + itemSpacing;
|
|---|
| 465 | if(separator == -1)
|
|---|
| 466 | separator_start += iWidth;
|
|---|
| 467 | else
|
|---|
| 468 | separator_len += iWidth;
|
|---|
| 469 | }
|
|---|
| 470 | //maximum height
|
|---|
| 471 | max_item_height = qMax(max_item_height, sz.height());
|
|---|
| 472 | //append
|
|---|
| 473 | actionRects[i] = QRect(0, 0, sz.width(), sz.height());
|
|---|
| 474 | }
|
|---|
| 475 | }
|
|---|
| 476 |
|
|---|
| 477 | //calculate position
|
|---|
| 478 | const int fw = q->style()->pixelMetric(QStyle::PM_MenuBarPanelWidth, 0, q);
|
|---|
| 479 | int x = fw + ((start == -1) ? hmargin : start) + itemSpacing;
|
|---|
| 480 | int y = fw + vmargin;
|
|---|
| 481 | for(int i = 0; i < actions.count(); i++) {
|
|---|
| 482 | QRect &rect = actionRects[i];
|
|---|
| 483 | if (rect.isNull())
|
|---|
| 484 | continue;
|
|---|
| 485 |
|
|---|
| 486 | //resize
|
|---|
| 487 | rect.setHeight(max_item_height);
|
|---|
| 488 |
|
|---|
| 489 | //move
|
|---|
| 490 | if(separator != -1 && i >= separator) { //after the separator
|
|---|
| 491 | int left = (max_width - separator_len - hmargin - itemSpacing) + (x - separator_start - hmargin);
|
|---|
| 492 | if(left < separator_start) { //wrap
|
|---|
| 493 | separator_start = x = hmargin;
|
|---|
| 494 | y += max_item_height;
|
|---|
| 495 | }
|
|---|
| 496 | rect.moveLeft(left);
|
|---|
| 497 | } else {
|
|---|
| 498 | rect.moveLeft(x);
|
|---|
| 499 | }
|
|---|
| 500 | rect.moveTop(y);
|
|---|
| 501 |
|
|---|
| 502 | //keep moving along..
|
|---|
| 503 | x += rect.width() + itemSpacing;
|
|---|
| 504 |
|
|---|
| 505 | //make sure we follow the layout direction
|
|---|
| 506 | rect = QStyle::visualRect(q->layoutDirection(), q->rect(), rect);
|
|---|
| 507 | }
|
|---|
| 508 | }
|
|---|
| 509 |
|
|---|
| 510 | void QMenuBarPrivate::activateAction(QAction *action, QAction::ActionEvent action_e)
|
|---|
| 511 | {
|
|---|
| 512 | Q_Q(QMenuBar);
|
|---|
| 513 | if (!action || !action->isEnabled())
|
|---|
| 514 | return;
|
|---|
| 515 | action->activate(action_e);
|
|---|
| 516 | if (action_e == QAction::Hover)
|
|---|
| 517 | action->showStatusText(q);
|
|---|
| 518 |
|
|---|
| 519 | // if(action_e == QAction::Trigger)
|
|---|
| 520 | // emit q->activated(action);
|
|---|
| 521 | // else if(action_e == QAction::Hover)
|
|---|
| 522 | // emit q->highlighted(action);
|
|---|
| 523 | }
|
|---|
| 524 |
|
|---|
| 525 |
|
|---|
| 526 | void QMenuBarPrivate::_q_actionTriggered()
|
|---|
| 527 | {
|
|---|
| 528 | Q_Q(QMenuBar);
|
|---|
| 529 | if (QAction *action = qobject_cast<QAction *>(q->sender())) {
|
|---|
| 530 | emit q->triggered(action);
|
|---|
| 531 | #ifdef QT3_SUPPORT
|
|---|
| 532 | emit q->activated(q->findIdForAction(action));
|
|---|
| 533 | #endif
|
|---|
| 534 | }
|
|---|
| 535 | }
|
|---|
| 536 |
|
|---|
| 537 | void QMenuBarPrivate::_q_actionHovered()
|
|---|
| 538 | {
|
|---|
| 539 | Q_Q(QMenuBar);
|
|---|
| 540 | if (QAction *action = qobject_cast<QAction *>(q->sender())) {
|
|---|
| 541 | emit q->hovered(action);
|
|---|
| 542 | #ifndef QT_NO_ACCESSIBILITY
|
|---|
| 543 | if (QAccessible::isActive()) {
|
|---|
| 544 | int actionIndex = actions.indexOf(action);
|
|---|
| 545 | ++actionIndex;
|
|---|
| 546 | QAccessible::updateAccessibility(q, actionIndex, QAccessible::Focus);
|
|---|
| 547 | QAccessible::updateAccessibility(q, actionIndex, QAccessible::Selection);
|
|---|
| 548 | }
|
|---|
| 549 | #endif //QT_NO_ACCESSIBILITY
|
|---|
| 550 | #ifdef QT3_SUPPORT
|
|---|
| 551 | emit q->highlighted(q->findIdForAction(action));
|
|---|
| 552 | #endif
|
|---|
| 553 | }
|
|---|
| 554 | }
|
|---|
| 555 |
|
|---|
| 556 | /*!
|
|---|
| 557 | Initialize \a option with the values from the menu bar and information from \a action. This method
|
|---|
| 558 | is useful for subclasses when they need a QStyleOptionMenuItem, but don't want
|
|---|
| 559 | to fill in all the information themselves.
|
|---|
| 560 |
|
|---|
| 561 | \sa QStyleOption::initFrom() QMenu::initStyleOption()
|
|---|
| 562 | */
|
|---|
| 563 | void QMenuBar::initStyleOption(QStyleOptionMenuItem *option, const QAction *action) const
|
|---|
| 564 | {
|
|---|
| 565 | if (!option || !action)
|
|---|
| 566 | return;
|
|---|
| 567 | Q_D(const QMenuBar);
|
|---|
| 568 | option->palette = palette();
|
|---|
| 569 | option->state = QStyle::State_None;
|
|---|
| 570 | if (isEnabled() && action->isEnabled())
|
|---|
| 571 | option->state |= QStyle::State_Enabled;
|
|---|
| 572 | else
|
|---|
| 573 | option->palette.setCurrentColorGroup(QPalette::Disabled);
|
|---|
| 574 | option->fontMetrics = fontMetrics();
|
|---|
| 575 | if (d->currentAction && d->currentAction == action) {
|
|---|
| 576 | option->state |= QStyle::State_Selected;
|
|---|
| 577 | if (d->popupState && !d->closePopupMode)
|
|---|
| 578 | option->state |= QStyle::State_Sunken;
|
|---|
| 579 | }
|
|---|
| 580 | if (hasFocus() || d->currentAction)
|
|---|
| 581 | option->state |= QStyle::State_HasFocus;
|
|---|
| 582 | option->menuRect = rect();
|
|---|
| 583 | option->menuItemType = QStyleOptionMenuItem::Normal;
|
|---|
| 584 | option->checkType = QStyleOptionMenuItem::NotCheckable;
|
|---|
| 585 | option->text = action->text();
|
|---|
| 586 | option->icon = action->icon();
|
|---|
| 587 | }
|
|---|
| 588 |
|
|---|
| 589 | /*!
|
|---|
| 590 | \class QMenuBar
|
|---|
| 591 | \brief The QMenuBar class provides a horizontal menu bar.
|
|---|
| 592 |
|
|---|
| 593 | \ingroup mainwindow-classes
|
|---|
| 594 |
|
|---|
| 595 | A menu bar consists of a list of pull-down menu items. You add
|
|---|
| 596 | menu items with addMenu(). For example, asuming that \c menubar
|
|---|
| 597 | is a pointer to a QMenuBar and \c fileMenu is a pointer to a
|
|---|
| 598 | QMenu, the following statement inserts the menu into the menu bar:
|
|---|
| 599 | \snippet doc/src/snippets/code/src_gui_widgets_qmenubar.cpp 0
|
|---|
| 600 |
|
|---|
| 601 | The ampersand in the menu item's text sets Alt+F as a shortcut for
|
|---|
| 602 | this menu. (You can use "\&\&" to get a real ampersand in the menu
|
|---|
| 603 | bar.)
|
|---|
| 604 |
|
|---|
| 605 | There is no need to lay out a menu bar. It automatically sets its
|
|---|
| 606 | own geometry to the top of the parent widget and changes it
|
|---|
| 607 | appropriately whenever the parent is resized.
|
|---|
| 608 |
|
|---|
| 609 | \section1 Usage
|
|---|
| 610 |
|
|---|
| 611 | In most main window style applications you would use the
|
|---|
| 612 | \l{QMainWindow::}{menuBar()} function provided in QMainWindow,
|
|---|
| 613 | adding \l{QMenu}s to the menu bar and adding \l{QAction}s to the
|
|---|
| 614 | pop-up menus.
|
|---|
| 615 |
|
|---|
| 616 | Example (from the \l{mainwindows/menus}{Menus} example):
|
|---|
| 617 |
|
|---|
| 618 | \snippet examples/mainwindows/menus/mainwindow.cpp 9
|
|---|
| 619 |
|
|---|
| 620 | Menu items may be removed with removeAction().
|
|---|
| 621 |
|
|---|
| 622 | Widgets can be added to menus by using instances of the QWidgetAction
|
|---|
| 623 | class to hold them. These actions can then be inserted into menus
|
|---|
| 624 | in the usual way; see the QMenu documentation for more details.
|
|---|
| 625 |
|
|---|
| 626 | \section1 Platform Dependent Look and Feel
|
|---|
| 627 |
|
|---|
| 628 | Different platforms have different requirements for the appearance
|
|---|
| 629 | of menu bars and their behavior when the user interacts with them.
|
|---|
| 630 | For example, Windows systems are often configured so that the
|
|---|
| 631 | underlined character mnemonics that indicate keyboard shortcuts
|
|---|
| 632 | for items in the menu bar are only shown when the \gui{Alt} key is
|
|---|
| 633 | pressed.
|
|---|
| 634 |
|
|---|
| 635 | \table
|
|---|
| 636 |
|
|---|
| 637 | \row \o \inlineimage plastique-menubar.png A menu bar shown in the
|
|---|
| 638 | Plastique widget style.
|
|---|
| 639 |
|
|---|
| 640 | \o The \l{QPlastiqueStyle}{Plastique widget style}, like most
|
|---|
| 641 | other styles, handles the \gui{Help} menu in the same way as it
|
|---|
| 642 | handles any other menu.
|
|---|
| 643 |
|
|---|
| 644 | \row \o \inlineimage motif-menubar.png A menu bar shown in the
|
|---|
| 645 | Motif widget style.
|
|---|
| 646 |
|
|---|
| 647 | \o The \l{QMotifStyle}{Motif widget style} treats \gui{Help} menus
|
|---|
| 648 | in a special way, placing them at right-hand end of the menu bar.
|
|---|
| 649 |
|
|---|
| 650 | \endtable
|
|---|
| 651 |
|
|---|
| 652 | \section1 QMenuBar on Mac OS X
|
|---|
| 653 |
|
|---|
| 654 | QMenuBar on Mac OS X is a wrapper for using the system-wide menu bar.
|
|---|
| 655 | If you have multiple menu bars in one dialog the outermost menu bar
|
|---|
| 656 | (normally inside a widget with widget flag Qt::Window) will
|
|---|
| 657 | be used for the system-wide menu bar.
|
|---|
| 658 |
|
|---|
| 659 | Qt for Mac OS X also provides a menu bar merging feature to make
|
|---|
| 660 | QMenuBar conform more closely to accepted Mac OS X menu bar layout.
|
|---|
| 661 | The merging functionality is based on string matching the title of
|
|---|
| 662 | a QMenu entry. These strings are translated (using QObject::tr())
|
|---|
| 663 | in the "QMenuBar" context. If an entry is moved its slots will still
|
|---|
| 664 | fire as if it was in the original place. The table below outlines
|
|---|
| 665 | the strings looked for and where the entry is placed if matched:
|
|---|
| 666 |
|
|---|
| 667 | \table
|
|---|
| 668 | \header \i String matches \i Placement \i Notes
|
|---|
| 669 | \row \i about.*
|
|---|
| 670 | \i Application Menu | About <application name>
|
|---|
| 671 | \i The application name is fetched from the \c {Info.plist} file
|
|---|
| 672 | (see note below). If this entry is not found no About item
|
|---|
| 673 | will appear in the Application Menu.
|
|---|
| 674 | \row \i config, options, setup, settings or preferences
|
|---|
| 675 | \i Application Menu | Preferences
|
|---|
| 676 | \i If this entry is not found the Settings item will be disabled
|
|---|
| 677 | \row \i quit or exit
|
|---|
| 678 | \i Application Menu | Quit <application name>
|
|---|
| 679 | \i If this entry is not found a default Quit item will be
|
|---|
| 680 | created to call QApplication::quit()
|
|---|
| 681 | \endtable
|
|---|
| 682 |
|
|---|
| 683 | You can override this behavior by using the QAction::menuRole()
|
|---|
| 684 | property.
|
|---|
| 685 |
|
|---|
| 686 | If you want all windows in a Mac application to share one menu
|
|---|
| 687 | bar, you must create a menu bar that does not have a parent.
|
|---|
| 688 | Create a parent-less menu bar this way:
|
|---|
| 689 |
|
|---|
| 690 | \snippet doc/src/snippets/code/src_gui_widgets_qmenubar.cpp 1
|
|---|
| 691 |
|
|---|
| 692 | \bold{Note:} Do \e{not} call QMainWindow::menuBar() to create the
|
|---|
| 693 | shared menu bar, because that menu bar will have the QMainWindow
|
|---|
| 694 | as its parent. That menu bar would only be displayed for the
|
|---|
| 695 | parent QMainWindow.
|
|---|
| 696 |
|
|---|
| 697 | \bold{Note:} The text used for the application name in the menu
|
|---|
| 698 | bar is obtained from the value set in the \c{Info.plist} file in
|
|---|
| 699 | the application's bundle. See \l{Deploying an Application on
|
|---|
| 700 | Mac OS X} for more information.
|
|---|
| 701 |
|
|---|
| 702 | \section1 QMenuBar on Windows CE
|
|---|
| 703 |
|
|---|
| 704 | QMenuBar on Windows CE is a wrapper for using the system-wide menu bar,
|
|---|
| 705 | similar to the Mac. This feature is activated for Windows Mobile
|
|---|
| 706 | and integrates QMenuBar with the native soft keys. The left soft
|
|---|
| 707 | key can be controlled with QMenuBar::setDefaultAction() and the
|
|---|
| 708 | right soft key can be used to access the menu bar.
|
|---|
| 709 |
|
|---|
| 710 | The hovered() signal is not supported for the native menu
|
|---|
| 711 | integration. Also, it is not possible to display an icon in a
|
|---|
| 712 | native menu on Windows Mobile.
|
|---|
| 713 |
|
|---|
| 714 | \section1 Examples
|
|---|
| 715 |
|
|---|
| 716 | The \l{mainwindows/menus}{Menus} example shows how to use QMenuBar
|
|---|
| 717 | and QMenu. The other \l{Main Window Examples}{main window
|
|---|
| 718 | application examples} also provide menus using these classes.
|
|---|
| 719 |
|
|---|
| 720 | \sa QMenu, QShortcut, QAction,
|
|---|
| 721 | {http://developer.apple.com/documentation/UserExperience/Conceptual/AppleHIGuidelines/XHIGIntro/XHIGIntro.html}{Introduction to Apple Human Interface Guidelines},
|
|---|
| 722 | {fowler}{GUI Design Handbook: Menu Bar}, {Menus Example}
|
|---|
| 723 | */
|
|---|
| 724 |
|
|---|
| 725 |
|
|---|
| 726 | void QMenuBarPrivate::init()
|
|---|
| 727 | {
|
|---|
| 728 | Q_Q(QMenuBar);
|
|---|
| 729 | q->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Minimum);
|
|---|
| 730 | q->setAttribute(Qt::WA_CustomWhatsThis);
|
|---|
| 731 | #ifdef Q_WS_MAC
|
|---|
| 732 | macCreateMenuBar(q->parentWidget());
|
|---|
| 733 | if(mac_menubar)
|
|---|
| 734 | q->hide();
|
|---|
| 735 | #endif
|
|---|
| 736 | #ifdef Q_WS_WINCE
|
|---|
| 737 | if (qt_wince_is_mobile()) {
|
|---|
| 738 | wceCreateMenuBar(q->parentWidget());
|
|---|
| 739 | if(wce_menubar)
|
|---|
| 740 | q->hide();
|
|---|
| 741 | }
|
|---|
| 742 | else {
|
|---|
| 743 | QApplication::setAttribute(Qt::AA_DontUseNativeMenuBar, true);
|
|---|
| 744 | }
|
|---|
| 745 | #endif
|
|---|
| 746 | q->setBackgroundRole(QPalette::Button);
|
|---|
| 747 | oldWindow = oldParent = 0;
|
|---|
| 748 | #ifdef QT3_SUPPORT
|
|---|
| 749 | doAutoResize = false;
|
|---|
| 750 | #endif
|
|---|
| 751 | #ifdef QT_SOFTKEYS_ENABLED
|
|---|
| 752 | menuBarAction = 0;
|
|---|
| 753 | #endif
|
|---|
| 754 | handleReparent();
|
|---|
| 755 | q->setMouseTracking(q->style()->styleHint(QStyle::SH_MenuBar_MouseTracking, 0, q));
|
|---|
| 756 |
|
|---|
| 757 | extension = new QMenuBarExtension(q);
|
|---|
| 758 | extension->setFocusPolicy(Qt::NoFocus);
|
|---|
| 759 | extension->hide();
|
|---|
| 760 | }
|
|---|
| 761 |
|
|---|
| 762 | //Gets the next action for keyboard navigation
|
|---|
| 763 | QAction *QMenuBarPrivate::getNextAction(const int _start, const int increment) const
|
|---|
| 764 | {
|
|---|
| 765 | Q_Q(const QMenuBar);
|
|---|
| 766 | const_cast<QMenuBarPrivate*>(this)->updateGeometries();
|
|---|
| 767 | bool allowActiveAndDisabled = q->style()->styleHint(QStyle::SH_Menu_AllowActiveAndDisabled, 0, q);
|
|---|
| 768 | const int start = (_start == -1 && increment == -1) ? actions.count() : _start;
|
|---|
| 769 | const int end = increment == -1 ? 0 : actions.count() - 1;
|
|---|
| 770 |
|
|---|
| 771 | for (int i = start; i != end;) {
|
|---|
| 772 | i += increment;
|
|---|
| 773 | QAction *current = actions.at(i);
|
|---|
| 774 | if (!actionRects.at(i).isNull() && (allowActiveAndDisabled || current->isEnabled()))
|
|---|
| 775 | return current;
|
|---|
| 776 | }
|
|---|
| 777 |
|
|---|
| 778 | if (_start != -1) //let's try from the beginning or the end
|
|---|
| 779 | return getNextAction(-1, increment);
|
|---|
| 780 |
|
|---|
| 781 | return 0;
|
|---|
| 782 | }
|
|---|
| 783 |
|
|---|
| 784 | /*!
|
|---|
| 785 | Constructs a menu bar with parent \a parent.
|
|---|
| 786 | */
|
|---|
| 787 | QMenuBar::QMenuBar(QWidget *parent) : QWidget(*new QMenuBarPrivate, parent, 0)
|
|---|
| 788 | {
|
|---|
| 789 | Q_D(QMenuBar);
|
|---|
| 790 | d->init();
|
|---|
| 791 | }
|
|---|
| 792 |
|
|---|
| 793 | #ifdef QT3_SUPPORT
|
|---|
| 794 | /*!
|
|---|
| 795 | Use one of the constructors that doesn't take the \a name
|
|---|
| 796 | argument and then use setObjectName() instead.
|
|---|
| 797 | */
|
|---|
| 798 | QMenuBar::QMenuBar(QWidget *parent, const char *name) : QWidget(*new QMenuBarPrivate, parent, 0)
|
|---|
| 799 | {
|
|---|
| 800 | Q_D(QMenuBar);
|
|---|
| 801 | d->init();
|
|---|
| 802 | setObjectName(QString::fromAscii(name));
|
|---|
| 803 | }
|
|---|
| 804 | #endif
|
|---|
| 805 |
|
|---|
| 806 | /*!
|
|---|
| 807 | Destroys the menu bar.
|
|---|
| 808 | */
|
|---|
| 809 | QMenuBar::~QMenuBar()
|
|---|
| 810 | {
|
|---|
| 811 | #ifdef Q_WS_MAC
|
|---|
| 812 | Q_D(QMenuBar);
|
|---|
|
|---|