[2] | 1 | /****************************************************************************
|
---|
| 2 | ** $Id: qpopupmenu.cpp 8 2005-11-16 19:36:46Z dmik $
|
---|
| 3 | **
|
---|
| 4 | ** Implementation of QPopupMenu class
|
---|
| 5 | **
|
---|
| 6 | ** Created : 941128
|
---|
| 7 | **
|
---|
| 8 | ** Copyright (C) 1992-2002 Trolltech AS. All rights reserved.
|
---|
| 9 | **
|
---|
| 10 | ** This file is part of the widgets module of the Qt GUI Toolkit.
|
---|
| 11 | **
|
---|
| 12 | ** This file may be distributed under the terms of the Q Public License
|
---|
| 13 | ** as defined by Trolltech AS of Norway and appearing in the file
|
---|
| 14 | ** LICENSE.QPL included in the packaging of this file.
|
---|
| 15 | **
|
---|
| 16 | ** This file may be distributed and/or modified under the terms of the
|
---|
| 17 | ** GNU General Public License version 2 as published by the Free Software
|
---|
| 18 | ** Foundation and appearing in the file LICENSE.GPL included in the
|
---|
| 19 | ** packaging of this file.
|
---|
| 20 | **
|
---|
| 21 | ** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
|
---|
| 22 | ** licenses may use this file in accordance with the Qt Commercial License
|
---|
| 23 | ** Agreement provided with the Software.
|
---|
| 24 | **
|
---|
| 25 | ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
|
---|
| 26 | ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
|
---|
| 27 | **
|
---|
| 28 | ** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
|
---|
| 29 | ** information about Qt Commercial License Agreements.
|
---|
| 30 | ** See http://www.trolltech.com/qpl/ for QPL licensing information.
|
---|
| 31 | ** See http://www.trolltech.com/gpl/ for GPL licensing information.
|
---|
| 32 | **
|
---|
| 33 | ** Contact info@trolltech.com if any conditions of this licensing are
|
---|
| 34 | ** not clear to you.
|
---|
| 35 | **
|
---|
| 36 | **********************************************************************/
|
---|
| 37 |
|
---|
| 38 | #include "qpopupmenu.h"
|
---|
| 39 | #ifndef QT_NO_POPUPMENU
|
---|
| 40 | #include "qmenubar.h"
|
---|
| 41 | #include "qaccel.h"
|
---|
| 42 | #include "qpainter.h"
|
---|
| 43 | #include "qdrawutil.h"
|
---|
| 44 | #include "qapplication.h"
|
---|
| 45 | #include "qpixmap.h"
|
---|
| 46 | #include "qpixmapcache.h"
|
---|
| 47 | #include "qtimer.h"
|
---|
| 48 | #include "qwhatsthis.h"
|
---|
| 49 | #include "qobjectlist.h"
|
---|
| 50 | #include "qguardedptr.h"
|
---|
| 51 | #include "qeffects_p.h"
|
---|
| 52 | #include "qcursor.h"
|
---|
| 53 | #include "qstyle.h"
|
---|
| 54 | #include "qtimer.h"
|
---|
| 55 | #include "qdatetime.h"
|
---|
| 56 | #if defined(QT_ACCESSIBILITY_SUPPORT)
|
---|
| 57 | #include "qaccessible.h"
|
---|
| 58 | #endif
|
---|
| 59 |
|
---|
| 60 | //#define ANIMATED_POPUP
|
---|
| 61 | //#define BLEND_POPUP
|
---|
| 62 |
|
---|
| 63 | // Motif style parameters
|
---|
| 64 |
|
---|
| 65 | static const int motifArrowHMargin = 6; // arrow horizontal margin
|
---|
| 66 | static const int motifArrowVMargin = 2; // arrow vertical margin
|
---|
| 67 |
|
---|
| 68 |
|
---|
| 69 | /*
|
---|
| 70 |
|
---|
| 71 | +-----------------------------
|
---|
| 72 | | PopupFrame
|
---|
| 73 | | +-------------------------
|
---|
| 74 | | | ItemFrame
|
---|
| 75 | | | +---------------------
|
---|
| 76 | | | |
|
---|
| 77 | | | | \
|
---|
| 78 | | | | ^ T E X T ^ | ItemVMargin
|
---|
| 79 | | | | | | /
|
---|
| 80 | | | ItemHMargin
|
---|
| 81 | |
|
---|
| 82 |
|
---|
| 83 | */
|
---|
| 84 |
|
---|
| 85 | #if 0
|
---|
| 86 | # define DEBUG_SLOPPY_SUBMENU
|
---|
| 87 | #endif
|
---|
| 88 |
|
---|
| 89 | // used for internal communication
|
---|
| 90 | static QPopupMenu * syncMenu = 0;
|
---|
| 91 | static int syncMenuId = 0;
|
---|
| 92 |
|
---|
| 93 | // Used to detect motion prior to mouse-release
|
---|
| 94 | static int motion;
|
---|
| 95 |
|
---|
| 96 | // used to provide ONE single-shot timer
|
---|
| 97 | static QTimer * singleSingleShot = 0;
|
---|
| 98 |
|
---|
| 99 | static bool supressAboutToShow = FALSE;
|
---|
| 100 |
|
---|
| 101 | static void cleanup()
|
---|
| 102 | {
|
---|
| 103 | delete singleSingleShot;
|
---|
| 104 | singleSingleShot = 0;
|
---|
| 105 | }
|
---|
| 106 |
|
---|
| 107 | static void popupSubMenuLater( int msec, QPopupMenu * receiver ) {
|
---|
| 108 | if ( !singleSingleShot ) {
|
---|
| 109 | singleSingleShot = new QTimer( qApp, "popup submenu timer" );
|
---|
| 110 | qAddPostRoutine( cleanup );
|
---|
| 111 | }
|
---|
| 112 |
|
---|
| 113 | singleSingleShot->disconnect( SIGNAL(timeout()) );
|
---|
| 114 | QObject::connect( singleSingleShot, SIGNAL(timeout()),
|
---|
| 115 | receiver, SLOT(subMenuTimer()) );
|
---|
| 116 | singleSingleShot->start( msec, TRUE );
|
---|
| 117 | }
|
---|
| 118 |
|
---|
| 119 | static bool preventAnimation = FALSE;
|
---|
| 120 |
|
---|
| 121 | #ifndef QT_NO_WHATSTHIS
|
---|
| 122 | extern void qWhatsThisBDH();
|
---|
| 123 | static QMenuItem* whatsThisItem = 0;
|
---|
| 124 | #endif
|
---|
| 125 |
|
---|
| 126 | /*!
|
---|
| 127 | \class QPopupMenu qpopupmenu.h
|
---|
| 128 | \brief The QPopupMenu class provides a popup menu widget.
|
---|
| 129 |
|
---|
| 130 | \ingroup application
|
---|
| 131 | \ingroup basic
|
---|
| 132 | \mainclass
|
---|
| 133 |
|
---|
| 134 | A popup menu widget is a selection menu. It can be either a
|
---|
| 135 | pull-down menu in a menu bar or a standalone context (popup) menu.
|
---|
| 136 | Pull-down menus are shown by the menu bar when the user clicks on
|
---|
| 137 | the respective item or presses the specified shortcut key. Use
|
---|
| 138 | QMenuBar::insertItem() to insert a popup menu into a menu bar.
|
---|
| 139 | Show a context menu either asynchronously with popup() or
|
---|
| 140 | synchronously with exec().
|
---|
| 141 |
|
---|
| 142 | Technically, a popup menu consists of a list of menu items. You
|
---|
| 143 | add items with insertItem(). An item is either a string, a pixmap
|
---|
| 144 | or a custom item that provides its own drawing function (see
|
---|
| 145 | QCustomMenuItem). In addition, items can have an optional icon
|
---|
| 146 | drawn on the very left side and an accelerator key such as
|
---|
| 147 | "Ctrl+X".
|
---|
| 148 |
|
---|
| 149 | There are three kinds of menu items: separators, menu items that
|
---|
| 150 | perform an action and menu items that show a submenu. Separators
|
---|
| 151 | are inserted with insertSeparator(). For submenus, you pass a
|
---|
| 152 | pointer to a QPopupMenu in your call to insertItem(). All other
|
---|
| 153 | items are considered action items.
|
---|
| 154 |
|
---|
| 155 | When inserting action items you usually specify a receiver and a
|
---|
| 156 | slot. The receiver will be notifed whenever the item is selected.
|
---|
| 157 | In addition, QPopupMenu provides two signals, activated() and
|
---|
| 158 | highlighted(), which signal the identifier of the respective menu
|
---|
| 159 | item. It is sometimes practical to connect several items to one
|
---|
| 160 | slot. To distinguish between them, specify a slot that takes an
|
---|
| 161 | integer argument and use setItemParameter() to associate a unique
|
---|
| 162 | value with each item.
|
---|
| 163 |
|
---|
| 164 | You clear a popup menu with clear() and remove single items with
|
---|
| 165 | removeItem() or removeItemAt().
|
---|
| 166 |
|
---|
| 167 | A popup menu can display check marks for certain items when
|
---|
| 168 | enabled with setCheckable(TRUE). You check or uncheck items with
|
---|
| 169 | setItemChecked().
|
---|
| 170 |
|
---|
| 171 | Items are either enabled or disabled. You toggle their state with
|
---|
| 172 | setItemEnabled(). Just before a popup menu becomes visible, it
|
---|
| 173 | emits the aboutToShow() signal. You can use this signal to set the
|
---|
| 174 | correct enabled/disabled states of all menu items before the user
|
---|
| 175 | sees it. The corresponding aboutToHide() signal is emitted when
|
---|
| 176 | the menu hides again.
|
---|
| 177 |
|
---|
| 178 | You can provide What's This? help for single menu items with
|
---|
| 179 | setWhatsThis(). See QWhatsThis for general information about this
|
---|
| 180 | kind of lightweight online help.
|
---|
| 181 |
|
---|
| 182 | For ultimate flexibility, you can also add entire widgets as items
|
---|
| 183 | into a popup menu (for example, a color selector).
|
---|
| 184 |
|
---|
| 185 | A QPopupMenu can also provide a tear-off menu. A tear-off menu is
|
---|
| 186 | a top-level window that contains a copy of the menu. This makes it
|
---|
| 187 | possible for the user to "tear off" frequently used menus and
|
---|
| 188 | position them in a convenient place on the screen. If you want
|
---|
| 189 | that functionality for a certain menu, insert a tear-off handle
|
---|
| 190 | with insertTearOffHandle(). When using tear-off menus, bear in
|
---|
| 191 | mind that the concept isn't typically used on Microsoft Windows so
|
---|
| 192 | users may not be familiar with it. Consider using a QToolBar
|
---|
| 193 | instead. Tear-off menus cannot contain custom widgets; if the
|
---|
| 194 | original menu contains a custom widget item, this item is omitted.
|
---|
| 195 |
|
---|
| 196 | \link menu-example.html menu/menu.cpp\endlink is an example of
|
---|
| 197 | QMenuBar and QPopupMenu use.
|
---|
| 198 |
|
---|
| 199 | \important insertItem removeItem removeItemAt clear text pixmap iconSet insertSeparator changeItem whatsThis setWhatsThis accel setAccel setItemEnabled isItemEnabled setItemVisible isItemVisible setItemChecked isItemChecked connectItem disconnectItem setItemParameter itemParameter
|
---|
| 200 |
|
---|
| 201 | <img src=qpopmenu-m.png> <img src=qpopmenu-w.png>
|
---|
| 202 |
|
---|
| 203 | \sa QMenuBar
|
---|
| 204 | \link guibooks.html#fowler GUI Design Handbook: Menu, Drop-Down and
|
---|
| 205 | Pop-Up\endlink
|
---|
| 206 | */
|
---|
| 207 |
|
---|
| 208 |
|
---|
| 209 | /*!
|
---|
| 210 | \fn void QPopupMenu::aboutToShow()
|
---|
| 211 |
|
---|
| 212 | This signal is emitted just before the popup menu is displayed.
|
---|
| 213 | You can connect it to any slot that sets up the menu contents
|
---|
| 214 | (e.g. to ensure that the right items are enabled).
|
---|
| 215 |
|
---|
| 216 | \sa aboutToHide(), setItemEnabled(), setItemChecked(), insertItem(), removeItem()
|
---|
| 217 | */
|
---|
| 218 |
|
---|
| 219 | /*!
|
---|
| 220 | \fn void QPopupMenu::aboutToHide()
|
---|
| 221 |
|
---|
| 222 | This signal is emitted just before the popup menu is hidden after
|
---|
| 223 | it has been displayed.
|
---|
| 224 |
|
---|
| 225 | \warning Do not open a widget in a slot connected to this signal.
|
---|
| 226 |
|
---|
| 227 | \sa aboutToShow(), setItemEnabled(), setItemChecked(), insertItem(), removeItem()
|
---|
| 228 | */
|
---|
| 229 |
|
---|
| 230 |
|
---|
| 231 |
|
---|
| 232 | /*****************************************************************************
|
---|
| 233 | QPopupMenu member functions
|
---|
| 234 | *****************************************************************************/
|
---|
| 235 |
|
---|
| 236 | class QMenuDataData {
|
---|
| 237 | // attention: also defined in qmenudata.cpp
|
---|
| 238 | public:
|
---|
| 239 | QMenuDataData();
|
---|
| 240 | QGuardedPtr<QWidget> aWidget;
|
---|
| 241 | int aInt;
|
---|
| 242 | };
|
---|
| 243 |
|
---|
| 244 | class QPopupMenuPrivate {
|
---|
| 245 | public:
|
---|
| 246 | struct Scroll {
|
---|
| 247 | enum { ScrollNone=0, ScrollUp=0x01, ScrollDown=0x02 };
|
---|
| 248 | uint scrollable : 2;
|
---|
| 249 | uint direction : 1;
|
---|
| 250 | int topScrollableIndex, scrollableSize;
|
---|
| 251 | QTime lastScroll;
|
---|
| 252 | QTimer *scrolltimer;
|
---|
| 253 | } scroll;
|
---|
| 254 | QSize calcSize;
|
---|
| 255 | QRegion mouseMoveBuffer;
|
---|
| 256 | };
|
---|
| 257 |
|
---|
| 258 | static QPopupMenu* active_popup_menu = 0;
|
---|
| 259 |
|
---|
| 260 | /*!
|
---|
| 261 | Constructs a popup menu called \a name with parent \a parent.
|
---|
| 262 |
|
---|
| 263 | Although a popup menu is always a top-level widget, if a parent is
|
---|
| 264 | passed the popup menu will be deleted when that parent is
|
---|
| 265 | destroyed (as with any other QObject).
|
---|
| 266 | */
|
---|
| 267 |
|
---|
| 268 | QPopupMenu::QPopupMenu( QWidget *parent, const char *name )
|
---|
| 269 | : QFrame( parent, name, WType_Popup | WNoAutoErase )
|
---|
| 270 | {
|
---|
| 271 | d = new QPopupMenuPrivate;
|
---|
| 272 | d->scroll.scrollableSize = d->scroll.topScrollableIndex = 0;
|
---|
| 273 | d->scroll.scrollable = QPopupMenuPrivate::Scroll::ScrollNone;
|
---|
| 274 | d->scroll.scrolltimer = 0;
|
---|
| 275 | isPopupMenu = TRUE;
|
---|
| 276 | #ifndef QT_NO_ACCEL
|
---|
| 277 | autoaccel = 0;
|
---|
| 278 | accelDisabled = FALSE;
|
---|
| 279 | #endif
|
---|
| 280 | popupActive = -1;
|
---|
| 281 | snapToMouse = TRUE;
|
---|
| 282 | tab = 0;
|
---|
| 283 | checkable = 0;
|
---|
| 284 | tornOff = 0;
|
---|
| 285 | pendingDelayedContentsChanges = 0;
|
---|
| 286 | pendingDelayedStateChanges = 0;
|
---|
| 287 | maxPMWidth = 0;
|
---|
| 288 |
|
---|
| 289 | tab = 0;
|
---|
| 290 | ncols = 1;
|
---|
| 291 | setFrameStyle( QFrame::PopupPanel | QFrame::Raised );
|
---|
| 292 | setMouseTracking(style().styleHint(QStyle::SH_PopupMenu_MouseTracking, this));
|
---|
| 293 | style().polishPopupMenu( this );
|
---|
| 294 | setBackgroundMode( PaletteButton );
|
---|
| 295 | connectModalRecursionSafety = 0;
|
---|
| 296 |
|
---|
| 297 | setFocusPolicy( StrongFocus );
|
---|
| 298 | }
|
---|
| 299 |
|
---|
| 300 | /*!
|
---|
| 301 | Destroys the popup menu.
|
---|
| 302 | */
|
---|
| 303 |
|
---|
| 304 | QPopupMenu::~QPopupMenu()
|
---|
| 305 | {
|
---|
| 306 | if ( syncMenu == this && qApp ) {
|
---|
| 307 | qApp->exit_loop();
|
---|
| 308 | syncMenu = 0;
|
---|
| 309 | }
|
---|
| 310 |
|
---|
| 311 | if(d->scroll.scrolltimer)
|
---|
| 312 | delete d->scroll.scrolltimer;
|
---|
| 313 |
|
---|
| 314 | if ( isVisible() ) {
|
---|
| 315 | parentMenu = 0;
|
---|
| 316 | hidePopups();
|
---|
| 317 | }
|
---|
| 318 |
|
---|
| 319 | delete (QWidget*) QMenuData::d->aWidget; // tear-off menu
|
---|
| 320 |
|
---|
| 321 | preventAnimation = FALSE;
|
---|
| 322 | delete d;
|
---|
| 323 | }
|
---|
| 324 |
|
---|
| 325 |
|
---|
| 326 | /*!
|
---|
| 327 | Updates the item with identity \a id.
|
---|
| 328 | */
|
---|
| 329 | void QPopupMenu::updateItem( int id ) // update popup menu item
|
---|
| 330 | {
|
---|
| 331 | updateRow( indexOf(id) );
|
---|
| 332 | }
|
---|
| 333 |
|
---|
| 334 |
|
---|
| 335 | void QPopupMenu::setCheckable( bool enable )
|
---|
| 336 | {
|
---|
| 337 | if ( isCheckable() != enable ) {
|
---|
| 338 | checkable = enable;
|
---|
| 339 | badSize = TRUE;
|
---|
| 340 | if ( QMenuData::d->aWidget )
|
---|
| 341 | ( (QPopupMenu*)(QWidget*)QMenuData::d->aWidget)->setCheckable( enable );
|
---|
| 342 | }
|
---|
| 343 | }
|
---|
| 344 |
|
---|
| 345 | /*!
|
---|
| 346 | \property QPopupMenu::checkable
|
---|
| 347 | \brief whether the display of check marks on menu items is enabled
|
---|
| 348 |
|
---|
| 349 | When TRUE, the display of check marks on menu items is enabled.
|
---|
| 350 | Checking is always enabled when in Windows-style.
|
---|
| 351 |
|
---|
| 352 | \sa QMenuData::setItemChecked()
|
---|
| 353 | */
|
---|
| 354 |
|
---|
| 355 | bool QPopupMenu::isCheckable() const
|
---|
| 356 | {
|
---|
| 357 | return checkable;
|
---|
| 358 | }
|
---|
| 359 |
|
---|
| 360 | void QPopupMenu::menuContentsChanged()
|
---|
| 361 | {
|
---|
| 362 | // here the part that can't be delayed
|
---|
| 363 | QMenuData::menuContentsChanged();
|
---|
| 364 | badSize = TRUE; // might change the size
|
---|
| 365 | #if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE)
|
---|
| 366 | mac_dirty_popup = 1;
|
---|
| 367 | #endif
|
---|
| 368 | if( pendingDelayedContentsChanges )
|
---|
| 369 | return;
|
---|
| 370 | pendingDelayedContentsChanges = 1;
|
---|
| 371 | if( !pendingDelayedStateChanges ) // if the timer hasn't been started yet
|
---|
| 372 | QTimer::singleShot( 0, this, SLOT(performDelayedChanges()));
|
---|
| 373 | }
|
---|
| 374 |
|
---|
| 375 | void QPopupMenu::performDelayedContentsChanged()
|
---|
| 376 | {
|
---|
| 377 | pendingDelayedContentsChanges = 0;
|
---|
| 378 | // here the part the can be delayed
|
---|
| 379 | #ifndef QT_NO_ACCEL
|
---|
| 380 | // if performDelayedStateChanged() will be called too,
|
---|
| 381 | // it will call updateAccel() too, no need to do it twice
|
---|
| 382 | if( !pendingDelayedStateChanges )
|
---|
| 383 | updateAccel( 0 );
|
---|
| 384 | #endif
|
---|
| 385 | if ( isVisible() ) {
|
---|
| 386 | if ( tornOff )
|
---|
| 387 | return;
|
---|
| 388 | updateSize(TRUE);
|
---|
| 389 | update();
|
---|
| 390 | }
|
---|
| 391 | QPopupMenu* p = (QPopupMenu*)(QWidget*)QMenuData::d->aWidget;
|
---|
| 392 | if ( p && p->isVisible() ) {
|
---|
| 393 | p->mitems->clear();
|
---|
| 394 | for ( QMenuItemListIt it( *mitems ); it.current(); ++it ) {
|
---|
| 395 | if ( it.current()->id() != QMenuData::d->aInt && !it.current()->widget() )
|
---|
| 396 | p->mitems->append( it.current() );
|
---|
| 397 | }
|
---|
| 398 | p->updateSize(TRUE);
|
---|
| 399 | p->update();
|
---|
| 400 | }
|
---|
| 401 | #if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE)
|
---|
| 402 | mac_dirty_popup = 1;
|
---|
| 403 | #endif
|
---|
| 404 | }
|
---|
| 405 |
|
---|
| 406 |
|
---|
| 407 | void QPopupMenu::menuStateChanged()
|
---|
| 408 | {
|
---|
| 409 | // here the part that can't be delayed
|
---|
| 410 | if( pendingDelayedStateChanges )
|
---|
| 411 | return;
|
---|
| 412 | pendingDelayedStateChanges = 1;
|
---|
| 413 | if( !pendingDelayedContentsChanges ) // if the timer hasn't been started yet
|
---|
| 414 | QTimer::singleShot( 0, this, SLOT(performDelayedChanges()));
|
---|
| 415 | }
|
---|
| 416 |
|
---|
| 417 | void QPopupMenu::performDelayedStateChanged()
|
---|
| 418 | {
|
---|
| 419 | pendingDelayedStateChanges = 0;
|
---|
| 420 | // here the part that can be delayed
|
---|
| 421 | #ifndef QT_NO_ACCEL
|
---|
| 422 | updateAccel( 0 ); // ### when we have a good solution for the accel vs. focus widget problem, remove that. That is only a workaround
|
---|
| 423 | // if you remove this, see performDelayedContentsChanged()
|
---|
| 424 | #endif
|
---|
| 425 | update();
|
---|
| 426 | if ( QMenuData::d->aWidget )
|
---|
| 427 | QMenuData::d->aWidget->update();
|
---|
| 428 | }
|
---|
| 429 |
|
---|
| 430 | void QPopupMenu::performDelayedChanges()
|
---|
| 431 | {
|
---|
| 432 | if( pendingDelayedContentsChanges )
|
---|
| 433 | performDelayedContentsChanged();
|
---|
| 434 | if( pendingDelayedStateChanges )
|
---|
| 435 | performDelayedStateChanged();
|
---|
| 436 | }
|
---|
| 437 |
|
---|
| 438 | void QPopupMenu::menuInsPopup( QPopupMenu *popup )
|
---|
| 439 | {
|
---|
| 440 | connect( popup, SIGNAL(activatedRedirect(int)),
|
---|
| 441 | SLOT(subActivated(int)) );
|
---|
| 442 | connect( popup, SIGNAL(highlightedRedirect(int)),
|
---|
| 443 | SLOT(subHighlighted(int)) );
|
---|
| 444 | connect( popup, SIGNAL(destroyed(QObject*)),
|
---|
| 445 | this, SLOT(popupDestroyed(QObject*)) );
|
---|
| 446 | }
|
---|
| 447 |
|
---|
| 448 | void QPopupMenu::menuDelPopup( QPopupMenu *popup )
|
---|
| 449 | {
|
---|
| 450 | popup->disconnect( SIGNAL(activatedRedirect(int)) );
|
---|
| 451 | popup->disconnect( SIGNAL(highlightedRedirect(int)) );
|
---|
| 452 | disconnect( popup, SIGNAL(destroyed(QObject*)),
|
---|
| 453 | this, SLOT(popupDestroyed(QObject*)) );
|
---|
| 454 | }
|
---|
| 455 |
|
---|
| 456 |
|
---|
| 457 | void QPopupMenu::frameChanged()
|
---|
| 458 | {
|
---|
| 459 | menuContentsChanged();
|
---|
| 460 | }
|
---|
| 461 |
|
---|
| 462 | /*!
|
---|
| 463 | Displays the popup menu so that the item number \a indexAtPoint
|
---|
| 464 | will be at the specified \e global position \a pos. To translate a
|
---|
| 465 | widget's local coordinates into global coordinates, use
|
---|
| 466 | QWidget::mapToGlobal().
|
---|
| 467 |
|
---|
| 468 | When positioning a popup with exec() or popup(), bear in mind that
|
---|
| 469 | you cannot rely on the popup menu's current size(). For
|
---|
| 470 | performance reasons, the popup adapts its size only when
|
---|
| 471 | necessary, so in many cases, the size before and after the show is
|
---|
| 472 | different. Instead, use sizeHint(). It calculates the proper size
|
---|
| 473 | depending on the menu's current contents.
|
---|
| 474 | */
|
---|
| 475 |
|
---|
| 476 | void QPopupMenu::popup( const QPoint &pos, int indexAtPoint )
|
---|
| 477 | {
|
---|
| 478 | if ( !isPopup() && isVisible() )
|
---|
| 479 | hide();
|
---|
| 480 |
|
---|
| 481 | //avoid circularity
|
---|
| 482 | if ( isVisible() || !isEnabled() )
|
---|
| 483 | return;
|
---|
| 484 |
|
---|
| 485 | #if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE)
|
---|
| 486 | if( macPopupMenu(pos, indexAtPoint ))
|
---|
| 487 | return;
|
---|
| 488 | #endif
|
---|
| 489 |
|
---|
| 490 | #if (QT_VERSION-0 >= 0x040000)
|
---|
| 491 | #error "Fix this now"
|
---|
| 492 | // #### should move to QWidget - anything might need this functionality,
|
---|
| 493 | // #### since anything can have WType_Popup window flag.
|
---|
| 494 | // #### This includes stuff in QPushButton and some stuff for setting
|
---|
| 495 | // #### the geometry of QDialog.
|
---|
| 496 | // QPopupMenu
|
---|
| 497 | // ::exec()
|
---|
| 498 | // ::popup()
|
---|
| 499 | // QPushButton (shouldn't require QMenuPopup)
|
---|
| 500 | // ::popupPressed
|
---|
| 501 | // Some stuff in qwidget.cpp for dialogs... can't remember exactly.
|
---|
| 502 | // Also the code here indicatets the parameter should be a rect, not a
|
---|
| 503 | // point.
|
---|
| 504 | #endif
|
---|
| 505 |
|
---|
| 506 | if(d->scroll.scrollable) {
|
---|
| 507 | d->scroll.scrollable = QPopupMenuPrivate::Scroll::ScrollNone;
|
---|
| 508 | d->scroll.topScrollableIndex = d->scroll.scrollableSize = 0;
|
---|
| 509 | badSize = TRUE;
|
---|
| 510 | }
|
---|
| 511 | updateSize();
|
---|
| 512 |
|
---|
| 513 | QPoint mouse = QCursor::pos();
|
---|
| 514 | snapToMouse = pos == mouse;
|
---|
| 515 |
|
---|
| 516 | // have to emit here as a menu might be setup in a slot connected
|
---|
| 517 | // to aboutToShow which will change the size of the menu
|
---|
| 518 | bool s = supressAboutToShow;
|
---|
| 519 | supressAboutToShow = TRUE;
|
---|
| 520 | if ( !s) {
|
---|
| 521 | emit aboutToShow();
|
---|
| 522 | updateSize(TRUE);
|
---|
| 523 | }
|
---|
| 524 |
|
---|
| 525 | int screen_num;
|
---|
| 526 | if (QApplication::desktop()->isVirtualDesktop())
|
---|
| 527 | screen_num =
|
---|
| 528 | QApplication::desktop()->screenNumber( QApplication::reverseLayout() ?
|
---|
| 529 | pos+QPoint(width(),0) : pos );
|
---|
| 530 | else
|
---|
| 531 | screen_num = QApplication::desktop()->screenNumber( this );
|
---|
| 532 | #ifdef Q_WS_MAC
|
---|
| 533 | QRect screen = QApplication::desktop()->availableGeometry( screen_num );
|
---|
| 534 | #else
|
---|
| 535 | QRect screen = QApplication::desktop()->screenGeometry( screen_num );
|
---|
| 536 | #endif
|
---|
| 537 | int sw = screen.width(); // screen width
|
---|
| 538 | int sh = screen.height(); // screen height
|
---|
| 539 | int sx = screen.x(); // screen pos
|
---|
| 540 | int sy = screen.y();
|
---|
| 541 | int x = pos.x();
|
---|
| 542 | int y = pos.y();
|
---|
| 543 | if ( indexAtPoint >= 0 ) // don't subtract when < 0
|
---|
| 544 | y -= itemGeometry( indexAtPoint ).y(); // (would subtract 2 pixels!)
|
---|
| 545 | int w = width();
|
---|
| 546 | int h = height();
|
---|
| 547 |
|
---|
| 548 | if ( snapToMouse ) {
|
---|
| 549 | if ( qApp->reverseLayout() )
|
---|
| 550 | x -= w;
|
---|
| 551 | if ( x+w > sx+sw )
|
---|
| 552 | x = mouse.x()-w;
|
---|
| 553 | if ( y+h > sy+sh )
|
---|
| 554 | y = mouse.y()-h;
|
---|
| 555 | if ( x < sx )
|
---|
| 556 | x = mouse.x();
|
---|
| 557 | if ( y < sy )
|
---|
| 558 | y = sy;
|
---|
| 559 | }
|
---|
| 560 |
|
---|
| 561 | if(style().styleHint(QStyle::SH_PopupMenu_Scrollable, this)) {
|
---|
| 562 | int off_top = 0, off_bottom = 0;
|
---|
| 563 | if(y+h > sy+sh)
|
---|
| 564 | off_bottom = (y+h) - (sy+sh);
|
---|
| 565 | if(y < sy)
|
---|
| 566 | off_top = sy - y;
|
---|
| 567 | if(off_bottom || off_top) {
|
---|
| 568 | int ch = updateSize().height(); //store the old height, before setting scrollable --Sam
|
---|
| 569 | const int vextra = style().pixelMetric(QStyle::PM_PopupMenuFrameVerticalExtra, this);
|
---|
| 570 | d->scroll.scrollableSize = h - off_top - off_bottom - 2*vextra;
|
---|
| 571 | if(off_top) {
|
---|
| 572 | move( x, y = sy );
|
---|
| 573 | d->scroll.scrollable = d->scroll.scrollable | QPopupMenuPrivate::Scroll::ScrollUp;
|
---|
| 574 | }
|
---|
| 575 | if( off_bottom )
|
---|
| 576 | d->scroll.scrollable = d->scroll.scrollable | QPopupMenuPrivate::Scroll::ScrollDown;
|
---|
| 577 | if( off_top != off_bottom && indexAtPoint >= 0 ) {
|
---|
| 578 | ch -= (vextra * 2);
|
---|
| 579 | if(ch > sh) //no bigger than the screen!
|
---|
| 580 | ch = sh;
|
---|
| 581 | if( ch > d->scroll.scrollableSize )
|
---|
| 582 | d->scroll.scrollableSize = ch;
|
---|
| 583 | }
|
---|
| 584 |
|
---|
| 585 | updateSize(TRUE); //now set the size using the scrollable/scrollableSize as above
|
---|
| 586 | if(indexAtPoint >= 0) {
|
---|
| 587 | if(off_top) { //scroll to it
|
---|
| 588 | register QMenuItem *mi = NULL;
|
---|
| 589 | QMenuItemListIt it(*mitems);
|
---|
| 590 | for(int tmp_y = 0; tmp_y < off_top && (mi=it.current()); ) {
|
---|
| 591 | QSize sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
---|
| 592 | QSize(0, itemHeight( mi )),
|
---|
| 593 | QStyleOption(mi,maxPMWidth,0));
|
---|
| 594 | tmp_y += sz.height();
|
---|
| 595 | d->scroll.topScrollableIndex++;
|
---|
| 596 | }
|
---|
| 597 | }
|
---|
| 598 | }
|
---|
| 599 | }
|
---|
| 600 | }
|
---|
| 601 | if ( x+w > sx+sw ) // the complete widget must
|
---|
| 602 | x = sx+sw - w; // be visible
|
---|
| 603 | if ( y+h > sy+sh )
|
---|
| 604 | y = sy+sh - h;
|
---|
| 605 | if ( x < sx )
|
---|
| 606 | x = sx;
|
---|
| 607 | if ( y < sy )
|
---|
| 608 | y = sy;
|
---|
| 609 | move( x, y );
|
---|
| 610 | motion=0;
|
---|
| 611 | actItem = -1;
|
---|
| 612 |
|
---|
| 613 | #ifndef QT_NO_EFFECTS
|
---|
| 614 | int hGuess = qApp->reverseLayout() ? QEffects::LeftScroll : QEffects::RightScroll;
|
---|
| 615 | int vGuess = QEffects::DownScroll;
|
---|
| 616 | if ( qApp->reverseLayout() ) {
|
---|
| 617 | if ( snapToMouse && ( x + w/2 > mouse.x() ) ||
|
---|
| 618 | ( parentMenu && parentMenu->isPopupMenu &&
|
---|
| 619 | ( x + w/2 > ((QPopupMenu*)parentMenu)->x() ) ) )
|
---|
| 620 | hGuess = QEffects::RightScroll;
|
---|
| 621 | } else {
|
---|
| 622 | if ( snapToMouse && ( x + w/2 < mouse.x() ) ||
|
---|
| 623 | ( parentMenu && parentMenu->isPopupMenu &&
|
---|
| 624 | ( x + w/2 < ((QPopupMenu*)parentMenu)->x() ) ) )
|
---|
| 625 | hGuess = QEffects::LeftScroll;
|
---|
| 626 | }
|
---|
| 627 |
|
---|
| 628 | #ifndef QT_NO_MENUBAR
|
---|
| 629 | if ( snapToMouse && ( y + h/2 < mouse.y() ) ||
|
---|
| 630 | ( parentMenu && parentMenu->isMenuBar &&
|
---|
| 631 | ( y + h/2 < ((QMenuBar*)parentMenu)->mapToGlobal( ((QMenuBar*)parentMenu)->pos() ).y() ) ) )
|
---|
| 632 | vGuess = QEffects::UpScroll;
|
---|
| 633 | #endif
|
---|
| 634 |
|
---|
| 635 | if ( QApplication::isEffectEnabled( UI_AnimateMenu ) &&
|
---|
| 636 | preventAnimation == FALSE ) {
|
---|
| 637 | if ( QApplication::isEffectEnabled( UI_FadeMenu ) )
|
---|
| 638 | qFadeEffect( this );
|
---|
| 639 | else if ( parentMenu )
|
---|
| 640 | qScrollEffect( this, parentMenu->isPopupMenu ? hGuess : vGuess );
|
---|
| 641 | else
|
---|
| 642 | qScrollEffect( this, hGuess | vGuess );
|
---|
| 643 | } else
|
---|
| 644 | #endif
|
---|
| 645 | {
|
---|
| 646 | show();
|
---|
| 647 | }
|
---|
| 648 | #if defined(QT_ACCESSIBILITY_SUPPORT)
|
---|
| 649 | QAccessible::updateAccessibility( this, 0, QAccessible::PopupMenuStart );
|
---|
| 650 | #endif
|
---|
| 651 | }
|
---|
| 652 |
|
---|
| 653 | /*!
|
---|
| 654 | \fn void QPopupMenu::activated( int id )
|
---|
| 655 |
|
---|
| 656 | This signal is emitted when a menu item is selected; \a id is the
|
---|
| 657 | id of the selected item.
|
---|
| 658 |
|
---|
| 659 | Normally, you connect each menu item to a single slot using
|
---|
| 660 | QMenuData::insertItem(), but sometimes you will want to connect
|
---|
| 661 | several items to a single slot (most often if the user selects
|
---|
| 662 | from an array). This signal is useful in such cases.
|
---|
| 663 |
|
---|
| 664 | \sa highlighted(), QMenuData::insertItem()
|
---|
| 665 | */
|
---|
| 666 |
|
---|
| 667 | /*!
|
---|
| 668 | \fn void QPopupMenu::highlighted( int id )
|
---|
| 669 |
|
---|
| 670 | This signal is emitted when a menu item is highlighted; \a id is
|
---|
| 671 | the id of the highlighted item.
|
---|
| 672 |
|
---|
| 673 | \sa activated(), QMenuData::insertItem()
|
---|
| 674 | */
|
---|
| 675 |
|
---|
| 676 | /*! \fn void QPopupMenu::highlightedRedirect( int id )
|
---|
| 677 | \internal
|
---|
| 678 | Used internally to connect submenus to their parents.
|
---|
| 679 | */
|
---|
| 680 |
|
---|
| 681 | /*! \fn void QPopupMenu::activatedRedirect( int id )
|
---|
| 682 | \internal
|
---|
| 683 | Used internally to connect submenus to their parents.
|
---|
| 684 | */
|
---|
| 685 |
|
---|
| 686 | void QPopupMenu::subActivated( int id )
|
---|
| 687 | {
|
---|
| 688 | emit activatedRedirect( id );
|
---|
| 689 | }
|
---|
| 690 |
|
---|
| 691 | void QPopupMenu::subHighlighted( int id )
|
---|
| 692 | {
|
---|
| 693 | emit highlightedRedirect( id );
|
---|
| 694 | }
|
---|
| 695 |
|
---|
| 696 | static bool fromAccel = FALSE;
|
---|
| 697 |
|
---|
| 698 | #ifndef QT_NO_ACCEL
|
---|
| 699 | void QPopupMenu::accelActivated( int id )
|
---|
| 700 | {
|
---|
| 701 | QMenuItem *mi = findItem( id );
|
---|
| 702 | if ( mi && mi->isEnabledAndVisible() ) {
|
---|
| 703 | QGuardedPtr<QSignal> signal = mi->signal();
|
---|
| 704 | fromAccel = TRUE;
|
---|
| 705 | actSig( mi->id() );
|
---|
| 706 | fromAccel = FALSE;
|
---|
| 707 | if ( signal )
|
---|
| 708 | signal->activate();
|
---|
| 709 | }
|
---|
| 710 | }
|
---|
| 711 |
|
---|
| 712 | void QPopupMenu::accelDestroyed() // accel about to be deleted
|
---|
| 713 | {
|
---|
| 714 | autoaccel = 0; // don't delete it twice!
|
---|
| 715 | }
|
---|
| 716 | #endif //QT_NO_ACCEL
|
---|
| 717 |
|
---|
| 718 | void QPopupMenu::popupDestroyed( QObject *o )
|
---|
| 719 | {
|
---|
| 720 | removePopup( (QPopupMenu*)o );
|
---|
| 721 | }
|
---|
| 722 |
|
---|
| 723 | void QPopupMenu::actSig( int id, bool inwhatsthis )
|
---|
| 724 | {
|
---|
| 725 | if ( !inwhatsthis ) {
|
---|
| 726 | emit activated( id );
|
---|
| 727 | #if defined(QT_ACCESSIBILITY_SUPPORT)
|
---|
| 728 | if ( !fromAccel )
|
---|
| 729 | QAccessible::updateAccessibility( this, indexOf(id)+1, QAccessible::MenuCommand );
|
---|
| 730 | #endif
|
---|
| 731 | } else {
|
---|
| 732 | #ifndef QT_NO_WHATSTHIS
|
---|
| 733 | QRect r( itemGeometry( indexOf( id ) ) );
|
---|
| 734 | QPoint p( r.center().x(), r.bottom() );
|
---|
| 735 | QString whatsThis = findItem( id )->whatsThis();
|
---|
| 736 | if ( whatsThis.isNull() )
|
---|
| 737 | whatsThis = QWhatsThis::textFor( this, p );
|
---|
| 738 | QWhatsThis::leaveWhatsThisMode( whatsThis, mapToGlobal( p ), this );
|
---|
| 739 | #endif
|
---|
| 740 | }
|
---|
| 741 |
|
---|
| 742 | emit activatedRedirect( id );
|
---|
| 743 | }
|
---|
| 744 |
|
---|
| 745 | void QPopupMenu::hilitSig( int id )
|
---|
| 746 | {
|
---|
| 747 | emit highlighted( id );
|
---|
| 748 | emit highlightedRedirect( id );
|
---|
| 749 |
|
---|
| 750 | #if defined(QT_ACCESSIBILITY_SUPPORT)
|
---|
| 751 | QAccessible::updateAccessibility( this, indexOf(id)+1, QAccessible::Focus );
|
---|
| 752 | QAccessible::updateAccessibility( this, indexOf(id)+1, QAccessible::Selection );
|
---|
| 753 | #endif
|
---|
| 754 | }
|
---|
| 755 |
|
---|
| 756 | void QPopupMenu::setFirstItemActive()
|
---|
| 757 | {
|
---|
| 758 | QMenuItemListIt it(*mitems);
|
---|
| 759 | register QMenuItem *mi;
|
---|
| 760 | int ai = 0;
|
---|
| 761 | if(d->scroll.scrollable)
|
---|
| 762 | ai = d->scroll.topScrollableIndex;
|
---|
| 763 | while ( (mi=it.current()) ) {
|
---|
| 764 | ++it;
|
---|
| 765 | if ( !mi->isSeparator() && mi->id() != QMenuData::d->aInt &&
|
---|
| 766 | ( style().styleHint( QStyle::SH_PopupMenu_AllowActiveAndDisabled, this ) || mi->isEnabledAndVisible() )) {
|
---|
| 767 | setActiveItem( ai );
|
---|
| 768 | return;
|
---|
| 769 | }
|
---|
| 770 | ai++;
|
---|
| 771 | }
|
---|
| 772 | actItem = -1;
|
---|
| 773 | }
|
---|
| 774 |
|
---|
| 775 | /*!
|
---|
| 776 | \internal
|
---|
| 777 | Hides all popup menus (in this menu tree) that are currently open.
|
---|
| 778 | */
|
---|
| 779 |
|
---|
| 780 | void QPopupMenu::hideAllPopups()
|
---|
| 781 | {
|
---|
| 782 | register QMenuData *top = this; // find top level popup
|
---|
| 783 | if ( !preventAnimation )
|
---|
| 784 | QTimer::singleShot( 10, this, SLOT(allowAnimation()) );
|
---|
| 785 | preventAnimation = TRUE;
|
---|
| 786 |
|
---|
| 787 | if ( !isPopup() )
|
---|
| 788 | return; // nothing to do
|
---|
| 789 |
|
---|
| 790 | while ( top->parentMenu && top->parentMenu->isPopupMenu
|
---|
| 791 | && ((QPopupMenu*)top->parentMenu)->isPopup() )
|
---|
| 792 | top = top->parentMenu;
|
---|
| 793 | ((QPopupMenu*)top)->hide(); // cascade from top level
|
---|
| 794 |
|
---|
| 795 | #ifndef QT_NO_WHATSTHIS
|
---|
| 796 | if (whatsThisItem) {
|
---|
| 797 | qWhatsThisBDH();
|
---|
| 798 | whatsThisItem = 0;
|
---|
| 799 | }
|
---|
| 800 | #endif
|
---|
| 801 |
|
---|
| 802 | }
|
---|
| 803 |
|
---|
| 804 | /*!
|
---|
| 805 | \internal
|
---|
| 806 | Hides all popup sub-menus.
|
---|
| 807 | */
|
---|
| 808 |
|
---|
| 809 | void QPopupMenu::hidePopups()
|
---|
| 810 | {
|
---|
| 811 | if ( !preventAnimation )
|
---|
| 812 | QTimer::singleShot( 10, this, SLOT(allowAnimation()) );
|
---|
| 813 | preventAnimation = TRUE;
|
---|
| 814 |
|
---|
| 815 | QMenuItemListIt it(*mitems);
|
---|
| 816 | register QMenuItem *mi;
|
---|
| 817 | while ( (mi=it.current()) ) {
|
---|
| 818 | ++it;
|
---|
| 819 | if ( mi->popup() && mi->popup()->parentMenu == this ) //avoid circularity
|
---|
| 820 | mi->popup()->hide();
|
---|
| 821 | }
|
---|
| 822 | popupActive = -1; // no active sub menu
|
---|
| 823 | if(style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this))
|
---|
| 824 | d->mouseMoveBuffer = QRegion();
|
---|
| 825 |
|
---|
| 826 | QRect mfrect = itemGeometry( actItem );
|
---|
| 827 | setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE );
|
---|
| 828 | }
|
---|
| 829 |
|
---|
| 830 |
|
---|
| 831 | /*!
|
---|
| 832 | \internal
|
---|
| 833 | Sends the event to the menu bar.
|
---|
| 834 | */
|
---|
| 835 |
|
---|
| 836 | bool QPopupMenu::tryMenuBar( QMouseEvent *e )
|
---|
| 837 | {
|
---|
| 838 | register QMenuData *top = this; // find top level
|
---|
| 839 | while ( top->parentMenu )
|
---|
| 840 | top = top->parentMenu;
|
---|
| 841 | #ifndef QT_NO_MENUBAR
|
---|
| 842 | return top->isMenuBar ?
|
---|
| 843 | ((QMenuBar *)top)->tryMouseEvent( this, e ) :
|
---|
| 844 | ((QPopupMenu*)top)->tryMouseEvent(this, e );
|
---|
| 845 | #else
|
---|
| 846 | return ((QPopupMenu*)top)->tryMouseEvent(this, e );
|
---|
| 847 | #endif
|
---|
| 848 | }
|
---|
| 849 |
|
---|
| 850 |
|
---|
| 851 | /*!
|
---|
| 852 | \internal
|
---|
| 853 | */
|
---|
| 854 | bool QPopupMenu::tryMouseEvent( QPopupMenu *p, QMouseEvent * e)
|
---|
| 855 | {
|
---|
| 856 | if ( p == this )
|
---|
| 857 | return FALSE;
|
---|
| 858 | QPoint pos = mapFromGlobal( e->globalPos() );
|
---|
| 859 | if ( !rect().contains( pos ) ) // outside
|
---|
| 860 | return FALSE;
|
---|
| 861 | QMouseEvent ee( e->type(), pos, e->globalPos(), e->button(), e->state() );
|
---|
| 862 | event( &ee );
|
---|
| 863 | return TRUE;
|
---|
| 864 | }
|
---|
| 865 |
|
---|
| 866 | /*!
|
---|
| 867 | \internal
|
---|
| 868 | Tells the menu bar to go back to idle state.
|
---|
| 869 | */
|
---|
| 870 |
|
---|
| 871 | void QPopupMenu::byeMenuBar()
|
---|
| 872 | {
|
---|
| 873 | #ifndef QT_NO_MENUBAR
|
---|
| 874 | register QMenuData *top = this; // find top level
|
---|
| 875 | while ( top->parentMenu )
|
---|
| 876 | top = top->parentMenu;
|
---|
| 877 | #endif
|
---|
| 878 | hideAllPopups();
|
---|
| 879 | #ifndef QT_NO_MENUBAR
|
---|
| 880 | if ( top->isMenuBar )
|
---|
| 881 | ((QMenuBar *)top)->goodbye();
|
---|
| 882 | #endif
|
---|
| 883 | }
|
---|
| 884 |
|
---|
| 885 |
|
---|
| 886 | /*!
|
---|
| 887 | \internal
|
---|
| 888 | Return the item at \a pos, or -1 if there is no item there or if
|
---|
| 889 | it is a separator item.
|
---|
| 890 | */
|
---|
| 891 |
|
---|
| 892 | int QPopupMenu::itemAtPos( const QPoint &pos, bool ignoreSeparator ) const
|
---|
| 893 | {
|
---|
| 894 | if ( !contentsRect().contains(pos) )
|
---|
| 895 | return -1;
|
---|
| 896 |
|
---|
| 897 | int row = 0;
|
---|
| 898 | int x = contentsRect().x();
|
---|
| 899 | int y = contentsRect().y();
|
---|
| 900 | QMenuItem *mi;
|
---|
| 901 | QMenuItemListIt it( *mitems );
|
---|
| 902 | if(d->scroll.scrollable) {
|
---|
| 903 | if(d->scroll.topScrollableIndex) {
|
---|
| 904 | for( ; (mi = it.current()) && row < d->scroll.topScrollableIndex; row++)
|
---|
| 905 | ++it;
|
---|
| 906 | if(!mi) {
|
---|
| 907 | row = 0;
|
---|
| 908 | it.toFirst();
|
---|
| 909 | }
|
---|
| 910 | y += style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
---|
| 911 | }
|
---|
| 912 | }
|
---|
| 913 | int itemw = contentsRect().width() / ncols;
|
---|
| 914 | QSize sz;
|
---|
| 915 | while ( (mi=it.current()) ) {
|
---|
| 916 | if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown &&
|
---|
| 917 | y >= contentsRect().height() - style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this))
|
---|
| 918 | return -1;
|
---|
| 919 | ++it;
|
---|
| 920 | if ( !mi->isVisible() ) {
|
---|
| 921 | ++row;
|
---|
| 922 | continue;
|
---|
| 923 | }
|
---|
| 924 | int itemh = itemHeight( mi );
|
---|
| 925 |
|
---|
| 926 | sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
---|
| 927 | QSize(0, itemh),
|
---|
| 928 | QStyleOption(mi,maxPMWidth));
|
---|
| 929 | sz = sz.expandedTo(QSize(itemw, sz.height()));
|
---|
| 930 | itemw = sz.width();
|
---|
| 931 | itemh = sz.height();
|
---|
| 932 |
|
---|
| 933 | if ( ncols > 1 && y + itemh > contentsRect().bottom() ) {
|
---|
| 934 | y = contentsRect().y();
|
---|
| 935 | x +=itemw;
|
---|
| 936 | }
|
---|
| 937 | if ( QRect( x, y, itemw, itemh ).contains( pos ) )
|
---|
| 938 | break;
|
---|
| 939 | y += itemh;
|
---|
| 940 | ++row;
|
---|
| 941 | }
|
---|
| 942 |
|
---|
| 943 | if ( mi && ( !ignoreSeparator || !mi->isSeparator() ) )
|
---|
| 944 | return row;
|
---|
| 945 | return -1;
|
---|
| 946 | }
|
---|
| 947 |
|
---|
| 948 | /*!
|
---|
| 949 | \internal
|
---|
| 950 | Returns the geometry of item number \a index.
|
---|
| 951 | */
|
---|
| 952 |
|
---|
| 953 | QRect QPopupMenu::itemGeometry( int index )
|
---|
| 954 | {
|
---|
| 955 | QMenuItem *mi;
|
---|
| 956 | QSize sz;
|
---|
| 957 | int row = 0, scrollh = 0;
|
---|
| 958 | int x = contentsRect().x();
|
---|
| 959 | int y = contentsRect().y();
|
---|
| 960 | QMenuItemListIt it( *mitems );
|
---|
| 961 | if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp) {
|
---|
| 962 | scrollh = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
---|
| 963 | y += scrollh;
|
---|
| 964 | if(d->scroll.topScrollableIndex) {
|
---|
| 965 | for( ; (mi = it.current()) && row < d->scroll.topScrollableIndex; row++)
|
---|
| 966 | ++it;
|
---|
| 967 | if(!mi) {
|
---|
| 968 | row = 0;
|
---|
| 969 | it.toFirst();
|
---|
| 970 | }
|
---|
| 971 | }
|
---|
| 972 | }
|
---|
| 973 | int itemw = contentsRect().width() / ncols;
|
---|
| 974 | while ( (mi=it.current()) ) {
|
---|
| 975 | if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown &&
|
---|
| 976 | y >= contentsRect().height() - scrollh)
|
---|
| 977 | break;
|
---|
| 978 | ++it;
|
---|
| 979 | if ( !mi->isVisible() ) {
|
---|
| 980 | ++row;
|
---|
| 981 | continue;
|
---|
| 982 | }
|
---|
| 983 | int itemh = itemHeight( mi );
|
---|
| 984 |
|
---|
| 985 | sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
---|
| 986 | QSize(0, itemh),
|
---|
| 987 | QStyleOption(mi,maxPMWidth));
|
---|
| 988 | sz = sz.expandedTo(QSize(itemw, sz.height()));
|
---|
| 989 | itemw = sz.width();
|
---|
| 990 | itemh = sz.height();
|
---|
| 991 | if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown &&
|
---|
| 992 | (y + itemh > contentsRect().height() - scrollh))
|
---|
| 993 | itemh -= (y + itemh) - (contentsRect().height() - scrollh);
|
---|
| 994 | if ( ncols > 1 && y + itemh > contentsRect().bottom() ) {
|
---|
| 995 | y = contentsRect().y();
|
---|
| 996 | x +=itemw;
|
---|
| 997 | }
|
---|
| 998 | if ( row == index )
|
---|
| 999 | return QRect( x,y,itemw,itemh );
|
---|
| 1000 | y += itemh;
|
---|
| 1001 | ++row;
|
---|
| 1002 | }
|
---|
| 1003 |
|
---|
| 1004 | return QRect(0,0,0,0);
|
---|
| 1005 | }
|
---|
| 1006 |
|
---|
| 1007 |
|
---|
| 1008 | /*!
|
---|
| 1009 | \internal
|
---|
| 1010 | Calculates and sets the size of the popup menu, based on the size
|
---|
| 1011 | of the items.
|
---|
| 1012 | */
|
---|
| 1013 |
|
---|
| 1014 | QSize QPopupMenu::updateSize(bool force_update, bool do_resize)
|
---|
| 1015 | {
|
---|
| 1016 | polish();
|
---|
| 1017 | if ( count() == 0 ) {
|
---|
| 1018 | QSize ret = QSize( 50, 8 );
|
---|
| 1019 | if(do_resize)
|
---|
| 1020 | setFixedSize( ret );
|
---|
| 1021 | badSize = TRUE;
|
---|
| 1022 | return ret;
|
---|
| 1023 | }
|
---|
| 1024 |
|
---|
| 1025 | int scrheight = 0;
|
---|
| 1026 | if(d->scroll.scrollableSize) {
|
---|
| 1027 | if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp)
|
---|
| 1028 | scrheight += style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
---|
| 1029 | if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown)
|
---|
| 1030 | scrheight += style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
---|
| 1031 | }
|
---|
| 1032 |
|
---|
| 1033 | if(badSize || force_update) {
|
---|
| 1034 | #ifndef QT_NO_ACCEL
|
---|
| 1035 | updateAccel( 0 );
|
---|
| 1036 | #endif
|
---|
| 1037 | int height = 0;
|
---|
| 1038 | int max_width = 0, max_height = 0;
|
---|
| 1039 | QFontMetrics fm = fontMetrics();
|
---|
| 1040 | register QMenuItem *mi;
|
---|
| 1041 | maxPMWidth = 0;
|
---|
| 1042 | int maxWidgetWidth = 0;
|
---|
| 1043 | tab = 0;
|
---|
| 1044 |
|
---|
| 1045 | for ( QMenuItemListIt it( *mitems ); it.current(); ++it ) {
|
---|
| 1046 | mi = it.current();
|
---|
| 1047 | QWidget *miw = mi->widget();
|
---|
| 1048 | if (miw) {
|
---|
| 1049 | if ( miw->parentWidget() != this )
|
---|
| 1050 | miw->reparent( this, QPoint(0,0), TRUE );
|
---|
| 1051 | // widget items musn't propgate mouse events
|
---|
| 1052 | ((QPopupMenu*)miw)->setWFlags(WNoMousePropagation);
|
---|
| 1053 | }
|
---|
| 1054 | if ( mi->custom() )
|
---|
| 1055 | mi->custom()->setFont( font() );
|
---|
| 1056 | if ( mi->iconSet() != 0)
|
---|
| 1057 | maxPMWidth = QMAX( maxPMWidth,
|
---|
| 1058 | mi->iconSet()->pixmap( QIconSet::Small, QIconSet::Normal ).width() + 4 );
|
---|
| 1059 | }
|
---|
| 1060 |
|
---|
| 1061 | int dh = QApplication::desktop()->height();
|
---|
| 1062 | ncols = 1;
|
---|
| 1063 |
|
---|
| 1064 | for ( QMenuItemListIt it2( *mitems ); it2.current(); ++it2 ) {
|
---|
| 1065 | mi = it2.current();
|
---|
| 1066 | if ( !mi->isVisible() )
|
---|
| 1067 | continue;
|
---|
| 1068 | int w = 0;
|
---|
| 1069 | int itemHeight = QPopupMenu::itemHeight( mi );
|
---|
| 1070 |
|
---|
| 1071 | if ( mi->widget() ) {
|
---|
| 1072 | QSize s( mi->widget()->sizeHint() );
|
---|
| 1073 | s = s.expandedTo( mi->widget()->minimumSize() );
|
---|
| 1074 | mi->widget()->resize( s );
|
---|
| 1075 | if ( s.width() > maxWidgetWidth )
|
---|
| 1076 | maxWidgetWidth = s.width();
|
---|
| 1077 | itemHeight = s.height();
|
---|
| 1078 | } else {
|
---|
| 1079 | if( ! mi->isSeparator() ) {
|
---|
| 1080 | if ( mi->custom() ) {
|
---|
| 1081 | if ( mi->custom()->fullSpan() ) {
|
---|
| 1082 | maxWidgetWidth = QMAX( maxWidgetWidth,
|
---|
| 1083 | mi->custom()->sizeHint().width() );
|
---|
| 1084 | } else {
|
---|
| 1085 | QSize s ( mi->custom()->sizeHint() );
|
---|
| 1086 | w += s.width();
|
---|
| 1087 | }
|
---|
| 1088 | }
|
---|
| 1089 |
|
---|
| 1090 | w += maxPMWidth;
|
---|
| 1091 |
|
---|
| 1092 | if (! mi->text().isNull()) {
|
---|
| 1093 | QString s = mi->text();
|
---|
| 1094 | int t;
|
---|
| 1095 | if ( (t = s.find('\t')) >= 0 ) { // string contains tab
|
---|
| 1096 | w += fm.width( s, t );
|
---|
| 1097 | w -= s.contains('&') * fm.width('&');
|
---|
| 1098 | w += s.contains("&&") * fm.width('&');
|
---|
| 1099 | int tw = fm.width( s.mid(t + 1) );
|
---|
| 1100 | if ( tw > tab)
|
---|
| 1101 | tab = tw;
|
---|
| 1102 | } else {
|
---|
| 1103 | w += fm.width( s );
|
---|
| 1104 | w -= s.contains('&') * fm.width('&');
|
---|
| 1105 | w += s.contains("&&") * fm.width('&');
|
---|
| 1106 | }
|
---|
| 1107 | } else if (mi->pixmap())
|
---|
| 1108 | w += mi->pixmap()->width();
|
---|
| 1109 | } else {
|
---|
| 1110 | if ( mi->custom() ) {
|
---|
| 1111 | QSize s ( mi->custom()->sizeHint() );
|
---|
| 1112 | w += s.width();
|
---|
| 1113 | } else {
|
---|
| 1114 | w = itemHeight = 2;
|
---|
| 1115 | }
|
---|
| 1116 | }
|
---|
| 1117 |
|
---|
| 1118 | QSize sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
---|
| 1119 | QSize(w, itemHeight),
|
---|
| 1120 | QStyleOption(mi,maxPMWidth));
|
---|
| 1121 |
|
---|
| 1122 | w = sz.width();
|
---|
| 1123 | itemHeight = sz.height();
|
---|
| 1124 |
|
---|
| 1125 | #if defined(QT_CHECK_NULL)
|
---|
| 1126 | if ( mi->text().isNull() && !mi->pixmap() && !mi->iconSet() &&
|
---|
| 1127 | !mi->isSeparator() && !mi->widget() && !mi->custom() )
|
---|
| 1128 | qWarning( "QPopupMenu: (%s) Popup has invalid menu item",
|
---|
| 1129 | name( "unnamed" ) );
|
---|
| 1130 | #endif
|
---|
| 1131 | }
|
---|
| 1132 | height += itemHeight;
|
---|
| 1133 | if(style().styleHint(QStyle::SH_PopupMenu_Scrollable, this)) {
|
---|
| 1134 | if(scrheight && height >= d->scroll.scrollableSize)
|
---|
| 1135 | break;
|
---|
| 1136 | } else if( height + 2*frameWidth() >= dh ) {
|
---|
| 1137 | ncols++;
|
---|
| 1138 | max_height = QMAX(max_height, height - itemHeight);
|
---|
| 1139 | height = 0;
|
---|
| 1140 | }
|
---|
| 1141 | if ( w > max_width )
|
---|
| 1142 | max_width = w;
|
---|
| 1143 | }
|
---|
| 1144 | if( ncols == 1 && !max_height )
|
---|
| 1145 | max_height = height;
|
---|
| 1146 |
|
---|
| 1147 | if(style().styleHint(QStyle::SH_PopupMenu_Scrollable, this)) {
|
---|
| 1148 | height += scrheight;
|
---|
| 1149 | setMouseTracking(TRUE);
|
---|
| 1150 | }
|
---|
| 1151 |
|
---|
| 1152 | if ( tab )
|
---|
| 1153 | tab -= fontMetrics().minRightBearing();
|
---|
| 1154 | else
|
---|
| 1155 | max_width -= fontMetrics().minRightBearing();
|
---|
| 1156 |
|
---|
| 1157 | if ( max_width + tab < maxWidgetWidth )
|
---|
| 1158 | max_width = maxWidgetWidth - tab;
|
---|
| 1159 |
|
---|
| 1160 | const int fw = frameWidth();
|
---|
| 1161 | int extra_width = (fw+style().pixelMetric(QStyle::PM_PopupMenuFrameHorizontalExtra, this)) * 2,
|
---|
| 1162 | extra_height = (fw+style().pixelMetric(QStyle::PM_PopupMenuFrameVerticalExtra, this)) * 2;
|
---|
| 1163 | if ( ncols == 1 )
|
---|
| 1164 | d->calcSize = QSize( QMAX( minimumWidth(), max_width + tab + extra_width ),
|
---|
| 1165 | QMAX( minimumHeight() , height + extra_height ) );
|
---|
| 1166 | else
|
---|
| 1167 | d->calcSize = QSize( QMAX( minimumWidth(), (ncols*(max_width + tab)) + extra_width ),
|
---|
| 1168 | QMAX( minimumHeight(), QMIN( max_height + extra_height + 1, dh ) ) );
|
---|
| 1169 | badSize = FALSE;
|
---|
| 1170 | }
|
---|
| 1171 |
|
---|
| 1172 | {
|
---|
| 1173 | // Position the widget items. It could be done in drawContents
|
---|
| 1174 | // but this way we get less flicker.
|
---|
| 1175 | QSize sz;
|
---|
| 1176 | int x = contentsRect().x();
|
---|
| 1177 | int y = contentsRect().y();
|
---|
| 1178 | int itemw = contentsRect().width() / ncols;
|
---|
| 1179 | for(QMenuItemListIt it(*mitems); it.current(); ++it) {
|
---|
| 1180 | QMenuItem *mi = it.current();
|
---|
| 1181 | if ( !mi->isVisible() )
|
---|
| 1182 | continue;
|
---|
| 1183 |
|
---|
| 1184 | int itemh = itemHeight( mi );
|
---|
| 1185 |
|
---|
| 1186 | sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
---|
| 1187 | QSize(0, itemh), QStyleOption(mi,maxPMWidth));
|
---|
| 1188 | sz = sz.expandedTo(QSize(itemw, sz.height()));
|
---|
| 1189 | itemw = sz.width();
|
---|
| 1190 | itemh = sz.height();
|
---|
| 1191 |
|
---|
| 1192 | if ( ncols > 1 && y + itemh > contentsRect().bottom() ) {
|
---|
| 1193 | y = contentsRect().y();
|
---|
| 1194 | x +=itemw;
|
---|
| 1195 | }
|
---|
| 1196 | if ( mi->widget() )
|
---|
| 1197 | mi->widget()->setGeometry( x, y, itemw, mi->widget()->height() );
|
---|
| 1198 | y += itemh;
|
---|
| 1199 | }
|
---|
| 1200 | }
|
---|
| 1201 |
|
---|
| 1202 | if( do_resize && size() != d->calcSize ) {
|
---|
| 1203 | setMaximumSize( d->calcSize );
|
---|
| 1204 | resize( d->calcSize );
|
---|
| 1205 | }
|
---|
| 1206 | return d->calcSize;
|
---|
| 1207 | }
|
---|
| 1208 |
|
---|
| 1209 |
|
---|
| 1210 | #ifndef QT_NO_ACCEL
|
---|
| 1211 | /*!
|
---|
| 1212 | \internal
|
---|
| 1213 | The \a parent is 0 when it is updated when a menu item has
|
---|
| 1214 | changed a state, or it is something else if called from the menu bar.
|
---|
| 1215 | */
|
---|
| 1216 |
|
---|
| 1217 | void QPopupMenu::updateAccel( QWidget *parent )
|
---|
| 1218 | {
|
---|
| 1219 | QMenuItemListIt it(*mitems);
|
---|
| 1220 | register QMenuItem *mi;
|
---|
| 1221 |
|
---|
| 1222 | if ( parent ) {
|
---|
| 1223 | delete autoaccel;
|
---|
| 1224 | autoaccel = 0;
|
---|
| 1225 | } else if ( !autoaccel ) {
|
---|
| 1226 | // we have no parent. Rather than ignoring any accelerators we try to find this popup's main window
|
---|
| 1227 | if ( tornOff ) {
|
---|
| 1228 | parent = this;
|
---|
| 1229 | } else {
|
---|
| 1230 | QWidget *w = (QWidget *) this;
|
---|
| 1231 | parent = w->parentWidget();
|
---|
| 1232 | while ( (!w->testWFlags(WType_TopLevel) || !w->testWFlags(WType_Popup)) && parent ) {
|
---|
| 1233 | w = parent;
|
---|
| 1234 | parent = parent->parentWidget();
|
---|
| 1235 | }
|
---|
| 1236 | }
|
---|
| 1237 | }
|
---|
| 1238 |
|
---|
| 1239 | if ( parent == 0 && autoaccel == 0 )
|
---|
| 1240 | return;
|
---|
| 1241 |
|
---|
| 1242 | if ( autoaccel ) // build it from scratch
|
---|
| 1243 | autoaccel->clear();
|
---|
| 1244 | else {
|
---|
| 1245 | // create an autoaccel in any case, even if we might not use
|
---|
| 1246 | // it immediately. Maybe the user needs it later.
|
---|
| 1247 | autoaccel = new QAccel( parent, this );
|
---|
| 1248 | connect( autoaccel, SIGNAL(activated(int)),
|
---|
| 1249 | SLOT(accelActivated(int)) );
|
---|
| 1250 | connect( autoaccel, SIGNAL(activatedAmbiguously(int)),
|
---|
| 1251 | SLOT(accelActivated(int)) );
|
---|
| 1252 | connect( autoaccel, SIGNAL(destroyed()),
|
---|
| 1253 | SLOT(accelDestroyed()) );
|
---|
| 1254 | if ( accelDisabled )
|
---|
| 1255 | autoaccel->setEnabled( FALSE );
|
---|
| 1256 | }
|
---|
| 1257 | while ( (mi=it.current()) ) {
|
---|
| 1258 | ++it;
|
---|
| 1259 | QKeySequence k = mi->key();
|
---|
| 1260 | if ( (int)k ) {
|
---|
| 1261 | int id = autoaccel->insertItem( k, mi->id() );
|
---|
| 1262 | #ifndef QT_NO_WHATSTHIS
|
---|
| 1263 | autoaccel->setWhatsThis( id, mi->whatsThis() );
|
---|
| 1264 | #endif
|
---|
| 1265 | }
|
---|
| 1266 | if ( !mi->text().isNull() || mi->custom() ) {
|
---|
| 1267 | QString s = mi->text();
|
---|
| 1268 | int i = s.find('\t');
|
---|
| 1269 |
|
---|
| 1270 | // Note: Only looking at the first key in the sequence!
|
---|
| 1271 | if ( (int)k && (int)k != Key_unknown ) {
|
---|
| 1272 | QString t = (QString)mi->key();
|
---|
| 1273 | if ( i >= 0 )
|
---|
| 1274 | s.replace( i+1, s.length()-i, t );
|
---|
| 1275 | else {
|
---|
| 1276 | s += '\t';
|
---|
| 1277 | s += t;
|
---|
| 1278 | }
|
---|
| 1279 | } else if ( !k ) {
|
---|
| 1280 | if ( i >= 0 )
|
---|
| 1281 | s.truncate( i );
|
---|
| 1282 | }
|
---|
| 1283 | if ( s != mi->text() ) {
|
---|
| 1284 | mi->setText( s );
|
---|
| 1285 | badSize = TRUE;
|
---|
| 1286 | }
|
---|
| 1287 | }
|
---|
| 1288 | if ( mi->popup() && parent ) { // call recursively
|
---|
| 1289 | // reuse
|
---|
| 1290 | QPopupMenu* popup = mi->popup();
|
---|
| 1291 | if (!popup->avoid_circularity) {
|
---|
| 1292 | popup->avoid_circularity = 1;
|
---|
| 1293 | popup->updateAccel( parent );
|
---|
| 1294 | popup->avoid_circularity = 0;
|
---|
| 1295 | }
|
---|
| 1296 | }
|
---|
| 1297 | }
|
---|
| 1298 | }
|
---|
| 1299 |
|
---|
| 1300 | /*!
|
---|
| 1301 | \internal
|
---|
| 1302 | It would be better to check in the slot.
|
---|
| 1303 | */
|
---|
| 1304 |
|
---|
| 1305 | void QPopupMenu::enableAccel( bool enable )
|
---|
| 1306 | {
|
---|
| 1307 | if ( autoaccel )
|
---|
| 1308 | autoaccel->setEnabled( enable );
|
---|
| 1309 | accelDisabled = !enable; // rememeber when updateAccel
|
---|
| 1310 | QMenuItemListIt it(*mitems);
|
---|
| 1311 | register QMenuItem *mi;
|
---|
| 1312 | while ( (mi=it.current()) ) { // do the same for sub popups
|
---|
| 1313 | ++it;
|
---|
| 1314 | if ( mi->popup() ) // call recursively
|
---|
| 1315 | mi->popup()->enableAccel( enable );
|
---|
| 1316 | }
|
---|
| 1317 | }
|
---|
| 1318 | #endif
|
---|
| 1319 |
|
---|
| 1320 | /*!
|
---|
| 1321 | \reimp
|
---|
| 1322 | */
|
---|
| 1323 | void QPopupMenu::setFont( const QFont &font )
|
---|
| 1324 | {
|
---|
| 1325 | QWidget::setFont( font );
|
---|
| 1326 | badSize = TRUE;
|
---|
| 1327 | if ( isVisible() ) {
|
---|
| 1328 | updateSize();
|
---|
| 1329 | update();
|
---|
| 1330 | }
|
---|
| 1331 | }
|
---|
| 1332 |
|
---|
| 1333 | /*!
|
---|
| 1334 | \reimp
|
---|
| 1335 | */
|
---|
| 1336 | void QPopupMenu::show()
|
---|
| 1337 | {
|
---|
| 1338 | if ( !isPopup() && isVisible() )
|
---|
| 1339 | hide();
|
---|
| 1340 |
|
---|
| 1341 | if ( isVisible() ) {
|
---|
| 1342 | supressAboutToShow = FALSE;
|
---|
| 1343 | QWidget::show();
|
---|
| 1344 | return;
|
---|
| 1345 | }
|
---|
| 1346 | if (!supressAboutToShow)
|
---|
| 1347 | emit aboutToShow();
|
---|
| 1348 | else
|
---|
| 1349 | supressAboutToShow = FALSE;
|
---|
| 1350 | performDelayedChanges();
|
---|
| 1351 | updateSize(TRUE);
|
---|
| 1352 | QWidget::show();
|
---|
| 1353 | popupActive = -1;
|
---|
| 1354 | if(style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this))
|
---|
| 1355 | d->mouseMoveBuffer = QRegion();
|
---|
| 1356 | }
|
---|
| 1357 |
|
---|
| 1358 | /*!
|
---|
| 1359 | \reimp
|
---|
| 1360 | */
|
---|
| 1361 |
|
---|
| 1362 | void QPopupMenu::hide()
|
---|
| 1363 | {
|
---|
| 1364 | if ( syncMenu == this && qApp ) {
|
---|
| 1365 | qApp->exit_loop();
|
---|
| 1366 | syncMenu = 0;
|
---|
| 1367 | }
|
---|
| 1368 |
|
---|
| 1369 | if ( !isVisible() ) {
|
---|
| 1370 | QWidget::hide();
|
---|
| 1371 | return;
|
---|
| 1372 | }
|
---|
| 1373 | emit aboutToHide();
|
---|
| 1374 |
|
---|
| 1375 | actItem = popupActive = -1;
|
---|
| 1376 | if(style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this))
|
---|
| 1377 | d->mouseMoveBuffer = QRegion();
|
---|
| 1378 | mouseBtDn = FALSE; // mouse button up
|
---|
| 1379 | #if defined(QT_ACCESSIBILITY_SUPPORT)
|
---|
| 1380 | QAccessible::updateAccessibility( this, 0, QAccessible::PopupMenuEnd );
|
---|
| 1381 | #endif
|
---|
| 1382 | parentMenu = 0;
|
---|
| 1383 | hidePopups();
|
---|
| 1384 | QWidget::hide();
|
---|
| 1385 | }
|
---|
| 1386 |
|
---|
| 1387 |
|
---|
| 1388 | /*!
|
---|
| 1389 | Calculates the height in pixels of the item in row \a row.
|
---|
| 1390 | */
|
---|
| 1391 | int QPopupMenu::itemHeight( int row ) const
|
---|
| 1392 | {
|
---|
| 1393 | return itemHeight( mitems->at( row ) );
|
---|
| 1394 | }
|
---|
| 1395 |
|
---|
| 1396 | /*!
|
---|
| 1397 | \overload
|
---|
| 1398 |
|
---|
| 1399 | Calculates the height in pixels of the menu item \a mi.
|
---|
| 1400 | */
|
---|
| 1401 | int QPopupMenu::itemHeight( QMenuItem *mi ) const
|
---|
| 1402 | {
|
---|
| 1403 | if ( mi->widget() )
|
---|
| 1404 | return mi->widget()->height();
|
---|
| 1405 | if ( mi->custom() && mi->custom()->fullSpan() )
|
---|
| 1406 | return mi->custom()->sizeHint().height();
|
---|
| 1407 |
|
---|
| 1408 | QFontMetrics fm(fontMetrics());
|
---|
| 1409 | int h = 0;
|
---|
| 1410 | if ( mi->isSeparator() ) // separator height
|
---|
| 1411 | h = 2;
|
---|
| 1412 | else if ( mi->pixmap() ) // pixmap height
|
---|
| 1413 | h = mi->pixmap()->height();
|
---|
| 1414 | else // text height
|
---|
| 1415 | h = fm.height();
|
---|
| 1416 |
|
---|
| 1417 | if ( !mi->isSeparator() && mi->iconSet() != 0 )
|
---|
| 1418 | h = QMAX(h, mi->iconSet()->pixmap( QIconSet::Small,
|
---|
| 1419 | QIconSet::Normal ).height());
|
---|
| 1420 | if ( mi->custom() )
|
---|
| 1421 | h = QMAX(h, mi->custom()->sizeHint().height());
|
---|
| 1422 |
|
---|
| 1423 | return h;
|
---|
| 1424 | }
|
---|
| 1425 |
|
---|
| 1426 |
|
---|
| 1427 | /*!
|
---|
| 1428 | Draws menu item \a mi in the area \a x, \a y, \a w, \a h, using
|
---|
| 1429 | painter \a p. The item is drawn active if \a act is TRUE or drawn
|
---|
| 1430 | inactive if \a act is FALSE. The rightmost \a tab_ pixels are used
|
---|
| 1431 | for accelerator text.
|
---|
| 1432 |
|
---|
| 1433 | \sa QStyle::drawControl()
|
---|
| 1434 | */
|
---|
| 1435 | void QPopupMenu::drawItem( QPainter* p, int tab_, QMenuItem* mi,
|
---|
| 1436 | bool act, int x, int y, int w, int h)
|
---|
| 1437 | {
|
---|
| 1438 | QStyle::SFlags flags = QStyle::Style_Default;
|
---|
| 1439 | if (isEnabled() && mi->isEnabledAndVisible() && (!mi->popup() || mi->popup()->isEnabled()) )
|
---|
| 1440 | flags |= QStyle::Style_Enabled;
|
---|
| 1441 | if (act)
|
---|
| 1442 | flags |= QStyle::Style_Active;
|
---|
| 1443 | if (mouseBtDn)
|
---|
| 1444 | flags |= QStyle::Style_Down;
|
---|
| 1445 |
|
---|
| 1446 | const QColorGroup &cg = ((flags&QStyle::Style_Enabled) ? colorGroup() : palette().disabled() );
|
---|
| 1447 |
|
---|
| 1448 | if ( mi->custom() && mi->custom()->fullSpan() ) {
|
---|
| 1449 | QMenuItem dummy;
|
---|
| 1450 | style().drawControl(QStyle::CE_PopupMenuItem, p, this, QRect(x, y, w, h), cg,
|
---|
| 1451 | flags, QStyleOption(&dummy,maxPMWidth,tab_));
|
---|
| 1452 | mi->custom()->paint( p, cg, act, flags&QStyle::Style_Enabled, x, y, w, h );
|
---|
| 1453 | } else
|
---|
| 1454 | style().drawControl(QStyle::CE_PopupMenuItem, p, this, QRect(x, y, w, h), cg,
|
---|
| 1455 | flags, QStyleOption(mi,maxPMWidth,tab_));
|
---|
| 1456 | }
|
---|
| 1457 |
|
---|
| 1458 |
|
---|
| 1459 | /*!
|
---|
| 1460 | Draws all menu items using painter \a p.
|
---|
| 1461 | */
|
---|
| 1462 | void QPopupMenu::drawContents( QPainter* p )
|
---|
| 1463 | {
|
---|
| 1464 | QMenuItemListIt it(*mitems);
|
---|
| 1465 | QMenuItem *mi = 0;
|
---|
| 1466 | int row = 0;
|
---|
| 1467 | int x = contentsRect().x();
|
---|
| 1468 | int y = contentsRect().y();
|
---|
| 1469 | if(d->scroll.scrollable) {
|
---|
| 1470 | if(d->scroll.topScrollableIndex) {
|
---|
| 1471 | for( ; (mi = it.current()) && row < d->scroll.topScrollableIndex; row++)
|
---|
| 1472 | ++it;
|
---|
| 1473 | if(!mi)
|
---|
| 1474 | it.toFirst();
|
---|
| 1475 | }
|
---|
| 1476 | if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp) {
|
---|
| 1477 | QRect rect(x, y, contentsRect().width(),
|
---|
| 1478 | style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this));
|
---|
| 1479 | if(!p->hasClipping() || p->clipRegion().contains(rect)) {
|
---|
| 1480 | QStyle::SFlags flags = QStyle::Style_Up;
|
---|
| 1481 | if (isEnabled())
|
---|
| 1482 | flags |= QStyle::Style_Enabled;
|
---|
| 1483 | style().drawControl(QStyle::CE_PopupMenuScroller, p, this, rect,
|
---|
| 1484 | colorGroup(), flags, QStyleOption(maxPMWidth));
|
---|
| 1485 | }
|
---|
| 1486 | y += rect.height();
|
---|
| 1487 | }
|
---|
| 1488 | }
|
---|
| 1489 |
|
---|
| 1490 | int itemw = contentsRect().width() / ncols;
|
---|
| 1491 | QSize sz;
|
---|
| 1492 | QStyle::SFlags flags;
|
---|
| 1493 | while ( (mi=it.current()) ) {
|
---|
| 1494 | if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown &&
|
---|
| 1495 | y >= contentsRect().height() - style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this))
|
---|
| 1496 | break;
|
---|
| 1497 | ++it;
|
---|
| 1498 | if ( !mi->isVisible() ) {
|
---|
| 1499 | ++row;
|
---|
| 1500 | continue;
|
---|
| 1501 | }
|
---|
| 1502 | int itemh = itemHeight( mi );
|
---|
| 1503 | sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
---|
| 1504 | QSize(0, itemh),
|
---|
| 1505 | QStyleOption(mi,maxPMWidth,0)
|
---|
| 1506 | );
|
---|
| 1507 | sz = sz.expandedTo(QSize(itemw, sz.height()));
|
---|
| 1508 | itemw = sz.width();
|
---|
| 1509 | itemh = sz.height();
|
---|
| 1510 |
|
---|
[8] | 1511 | if ( ncols > 1 && y + itemh - 1 > contentsRect().bottom() ) {
|
---|
| 1512 | if ( y <= contentsRect().bottom() ) {
|
---|
| 1513 | QRect rect(x, y, itemw, contentsRect().bottom() - y + 1);
|
---|
[2] | 1514 | if(!p->hasClipping() || p->clipRegion().contains(rect)) {
|
---|
| 1515 | flags = QStyle::Style_Default;
|
---|
| 1516 | if (isEnabled() && mi->isEnabledAndVisible())
|
---|
| 1517 | flags |= QStyle::Style_Enabled;
|
---|
| 1518 | style().drawControl(QStyle::CE_PopupMenuItem, p, this, rect,
|
---|
| 1519 | colorGroup(), flags, QStyleOption((QMenuItem*)0,maxPMWidth));
|
---|
| 1520 | }
|
---|
| 1521 | }
|
---|
| 1522 | y = contentsRect().y();
|
---|
| 1523 | x +=itemw;
|
---|
| 1524 | }
|
---|
| 1525 | if (!mi->widget() && (!p->hasClipping() || p->clipRegion().contains(QRect(x, y, itemw, itemh))))
|
---|
| 1526 | drawItem( p, tab, mi, row == actItem, x, y, itemw, itemh );
|
---|
| 1527 | y += itemh;
|
---|
| 1528 | ++row;
|
---|
| 1529 | }
|
---|
[8] | 1530 | if ( y <= contentsRect().bottom() ) {
|
---|
| 1531 | QRect rect(x, y, itemw, contentsRect().bottom() - y + 1);
|
---|
[2] | 1532 | if(!p->hasClipping() || p->clipRegion().contains(rect)) {
|
---|
| 1533 | flags = QStyle::Style_Default;
|
---|
| 1534 | if ( isEnabled() )
|
---|
| 1535 | flags |= QStyle::Style_Enabled;
|
---|
| 1536 | style().drawControl(QStyle::CE_PopupMenuItem, p, this, rect,
|
---|
| 1537 | colorGroup(), flags, QStyleOption((QMenuItem*)0,maxPMWidth));
|
---|
| 1538 | }
|
---|
| 1539 | }
|
---|
| 1540 | if( d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown ) {
|
---|
| 1541 | int sh = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
---|
| 1542 | QRect rect(x, contentsRect().height() - sh, contentsRect().width(), sh);
|
---|
| 1543 | if(!p->hasClipping() || p->clipRegion().contains(rect)) {
|
---|
| 1544 | QStyle::SFlags flags = QStyle::Style_Down;
|
---|
| 1545 | if (isEnabled())
|
---|
| 1546 | flags |= QStyle::Style_Enabled;
|
---|
| 1547 | style().drawControl(QStyle::CE_PopupMenuScroller, p, this, rect,
|
---|
| 1548 | colorGroup(), flags, QStyleOption(maxPMWidth));
|
---|
| 1549 | }
|
---|
| 1550 | }
|
---|
| 1551 | #if defined( DEBUG_SLOPPY_SUBMENU )
|
---|
| 1552 | if ( style().styleHint(QStyle::SH_PopupMenu_SloppySubMenus, this )) {
|
---|
| 1553 | p->setClipRegion( d->mouseMoveBuffer );
|
---|
| 1554 | p->fillRect( d->mouseMoveBuffer.boundingRect(), colorGroup().brush( QColorGroup::Highlight ) );
|
---|
| 1555 | }
|
---|
| 1556 | #endif
|
---|
| 1557 | }
|
---|
| 1558 |
|
---|
| 1559 |
|
---|
| 1560 | /*****************************************************************************
|
---|
| 1561 | Event handlers
|
---|
| 1562 | *****************************************************************************/
|
---|
| 1563 |
|
---|
| 1564 | /*!
|
---|
| 1565 | \reimp
|
---|
| 1566 | */
|
---|
| 1567 |
|
---|
| 1568 | void QPopupMenu::paintEvent( QPaintEvent *e )
|
---|
| 1569 | {
|
---|
| 1570 | QFrame::paintEvent( e );
|
---|
| 1571 | }
|
---|
| 1572 |
|
---|
| 1573 | /*!
|
---|
| 1574 | \reimp
|
---|
| 1575 | */
|
---|
| 1576 |
|
---|
| 1577 | void QPopupMenu::closeEvent( QCloseEvent * e) {
|
---|
| 1578 | e->accept();
|
---|
| 1579 | byeMenuBar();
|
---|
| 1580 | }
|
---|
| 1581 |
|
---|
| 1582 |
|
---|
| 1583 | /*!
|
---|
| 1584 | \reimp
|
---|
| 1585 | */
|
---|
| 1586 |
|
---|
| 1587 | void QPopupMenu::mousePressEvent( QMouseEvent *e )
|
---|
| 1588 | {
|
---|
| 1589 | int sh = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
---|
| 1590 | if (rect().contains(e->pos()) &&
|
---|
| 1591 | ((d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp && e->pos().y() <= sh) || //up
|
---|
| 1592 | (d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown &&
|
---|
| 1593 | e->pos().y() >= contentsRect().height() - sh))) //down
|
---|
| 1594 | return;
|
---|
| 1595 |
|
---|
| 1596 | mouseBtDn = TRUE; // mouse button down
|
---|
| 1597 | int item = itemAtPos( e->pos() );
|
---|
| 1598 | if ( item == -1 ) {
|
---|
| 1599 | if ( !rect().contains(e->pos()) && !tryMenuBar(e) ) {
|
---|
| 1600 | byeMenuBar();
|
---|
| 1601 | }
|
---|
| 1602 | return;
|
---|
| 1603 | }
|
---|
| 1604 | register QMenuItem *mi = mitems->at(item);
|
---|
| 1605 | if ( item != actItem ) // new item activated
|
---|
| 1606 | setActiveItem( item );
|
---|
| 1607 |
|
---|
| 1608 | QPopupMenu *popup = mi->popup();
|
---|
| 1609 | if ( popup ) {
|
---|
| 1610 | if ( popup->isVisible() ) { // sub menu already open
|
---|
| 1611 | int pactItem = popup->actItem;
|
---|
| 1612 | popup->actItem = -1;
|
---|
| 1613 | popup->hidePopups();
|
---|
| 1614 | popup->updateRow( pactItem );
|
---|
| 1615 | } else { // open sub menu
|
---|
| 1616 | hidePopups();
|
---|
| 1617 | popupSubMenuLater( 20, this );
|
---|
| 1618 | }
|
---|
| 1619 | } else {
|
---|
| 1620 | hidePopups();
|
---|
| 1621 | }
|
---|
| 1622 | }
|
---|
| 1623 |
|
---|
| 1624 | /*!
|
---|
| 1625 | \reimp
|
---|
| 1626 | */
|
---|
| 1627 |
|
---|
| 1628 | void QPopupMenu::mouseReleaseEvent( QMouseEvent *e )
|
---|
| 1629 | {
|
---|
| 1630 | // do not hide a standalone context menu on press-release, unless
|
---|
| 1631 | // the user moved the mouse significantly
|
---|
| 1632 | if ( !parentMenu && !mouseBtDn && actItem < 0 && motion < 6 )
|
---|
| 1633 | return;
|
---|
| 1634 |
|
---|
| 1635 | mouseBtDn = FALSE;
|
---|
| 1636 |
|
---|
| 1637 | // if the user released the mouse outside the menu, pass control
|
---|
| 1638 | // to the menubar or our parent menu
|
---|
| 1639 | int sh = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
---|
| 1640 | if ( !rect().contains( e->pos() ) && tryMenuBar(e) )
|
---|
| 1641 | return;
|
---|
| 1642 | else if((d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp && e->pos().y() <= sh) || //up
|
---|
| 1643 | (d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown &&
|
---|
| 1644 | e->pos().y() >= contentsRect().height() - sh)) //down
|
---|
| 1645 | return;
|
---|
| 1646 |
|
---|
| 1647 | if ( actItem < 0 ) { // we do not have an active item
|
---|
| 1648 | // if the release is inside without motion (happens with
|
---|
| 1649 | // oversized popup menus on small screens), ignore it
|
---|
| 1650 | if ( rect().contains( e->pos() ) && motion < 6 )
|
---|
| 1651 | return;
|
---|
| 1652 | else
|
---|
| 1653 | byeMenuBar();
|
---|
| 1654 | } else { // selected menu item!
|
---|
| 1655 | register QMenuItem *mi = mitems->at(actItem);
|
---|
| 1656 | if ( mi ->widget() ) {
|
---|
| 1657 | QWidget* widgetAt = QApplication::widgetAt( e->globalPos(), TRUE );
|
---|
| 1658 | if ( widgetAt && widgetAt != this ) {
|
---|
| 1659 | QMouseEvent me( e->type(), widgetAt->mapFromGlobal( e->globalPos() ),
|
---|
| 1660 | e->globalPos(), e->button(), e->state() );
|
---|
| 1661 | QApplication::sendEvent( widgetAt, &me );
|
---|
| 1662 | }
|
---|
| 1663 | }
|
---|
| 1664 | QPopupMenu *popup = mi->popup();
|
---|
| 1665 | #ifndef QT_NO_WHATSTHIS
|
---|
| 1666 | bool b = QWhatsThis::inWhatsThisMode();
|
---|
| 1667 | #else
|
---|
| 1668 | const bool b = FALSE;
|
---|
| 1669 | #endif
|
---|
| 1670 | if ( !mi->isEnabledAndVisible() ) {
|
---|
| 1671 | #ifndef QT_NO_WHATSTHIS
|
---|
| 1672 | if ( b ) {
|
---|
| 1673 | actItem = -1;
|
---|
| 1674 | updateItem( mi->id() );
|
---|
| 1675 | byeMenuBar();
|
---|
| 1676 | actSig( mi->id(), b);
|
---|
| 1677 | }
|
---|
| 1678 | #endif
|
---|
[8] | 1679 | #if defined(Q_WS_PM)
|
---|
| 1680 | #ifndef QT_NO_WHATSTHIS
|
---|
| 1681 | else
|
---|
| 1682 | #endif
|
---|
| 1683 | //@@TODO (dmik): we need to play a sound when a disabled item is activated
|
---|
| 1684 | // to conform to the OS/2 CUA. Btw, should'n it be supported by QAccessibility
|
---|
| 1685 | // globally in Qt?
|
---|
| 1686 | WinAlarm( HWND_DESKTOP, WA_ERROR );
|
---|
| 1687 | #endif
|
---|
[2] | 1688 | } else if ( popup ) {
|
---|
| 1689 | popup->setFirstItemActive();
|
---|
| 1690 | } else { // normal menu item
|
---|
| 1691 | byeMenuBar(); // deactivate menu bar
|
---|
| 1692 | if ( mi->isEnabledAndVisible() ) {
|
---|
| 1693 | actItem = -1;
|
---|
| 1694 | updateItem( mi->id() );
|
---|
| 1695 | active_popup_menu = this;
|
---|
| 1696 | QGuardedPtr<QSignal> signal = mi->signal();
|
---|
| 1697 | actSig( mi->id(), b );
|
---|
| 1698 | if ( signal && !b )
|
---|
| 1699 | signal->activate();
|
---|
| 1700 | active_popup_menu = 0;
|
---|
| 1701 | }
|
---|
| 1702 | }
|
---|
| 1703 | }
|
---|
| 1704 | }
|
---|
| 1705 |
|
---|
| 1706 | /*!
|
---|
| 1707 | \reimp
|
---|
| 1708 | */
|
---|
| 1709 |
|
---|
| 1710 | void QPopupMenu::mouseMoveEvent( QMouseEvent *e )
|
---|
| 1711 | {
|
---|
| 1712 | motion++;
|
---|
| 1713 |
|
---|
| 1714 | if ( parentMenu && parentMenu->isPopupMenu ) {
|
---|
| 1715 | QPopupMenu* p = (QPopupMenu*)parentMenu;
|
---|
| 1716 | int myIndex;
|
---|
| 1717 |
|
---|
| 1718 | p->findPopup( this, &myIndex );
|
---|
| 1719 | QPoint pPos = p->mapFromParent( e->globalPos() );
|
---|
| 1720 | if ( p->actItem != myIndex && !p->rect().contains( pPos ) )
|
---|
| 1721 | p->setActiveItem( myIndex );
|
---|
| 1722 |
|
---|
| 1723 | if ( style().styleHint(QStyle::SH_PopupMenu_SloppySubMenus, this )) {
|
---|
| 1724 | p->d->mouseMoveBuffer = QRegion();
|
---|
| 1725 | #ifdef DEBUG_SLOPPY_SUBMENU
|
---|
| 1726 | p->repaint();
|
---|
| 1727 | #endif
|
---|
| 1728 | }
|
---|
| 1729 | }
|
---|
| 1730 |
|
---|
| 1731 | if ( (e->state() & Qt::MouseButtonMask) == 0 &&
|
---|
| 1732 | !hasMouseTracking() )
|
---|
| 1733 | return;
|
---|
| 1734 |
|
---|
| 1735 | int item = itemAtPos( e->pos() );
|
---|
| 1736 | if ( item == -1 ) { // no valid item
|
---|
| 1737 | int lastActItem = actItem;
|
---|
| 1738 | actItem = -1;
|
---|
| 1739 | if ( lastActItem >= 0 )
|
---|
| 1740 | updateRow( lastActItem );
|
---|
| 1741 | if(d->scroll.scrollable &&
|
---|
| 1742 | e->pos().x() >= rect().x() && e->pos().x() <= rect().width()) {
|
---|
| 1743 | if(!d->scroll.scrolltimer) {
|
---|
| 1744 | d->scroll.scrolltimer = new QTimer(this, "popup scroll timer");
|
---|
| 1745 | QObject::connect( d->scroll.scrolltimer, SIGNAL(timeout()),
|
---|
| 1746 | this, SLOT(subScrollTimer()) );
|
---|
| 1747 | }
|
---|
| 1748 | if(!d->scroll.scrolltimer->isActive())
|
---|
| 1749 | d->scroll.scrolltimer->start(40);
|
---|
| 1750 | } else if ( lastActItem > 0 ||
|
---|
| 1751 | ( !rect().contains( e->pos() ) && !tryMenuBar( e ) ) ) {
|
---|
| 1752 | popupSubMenuLater(style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay,
|
---|
| 1753 | this), this);
|
---|
| 1754 | }
|
---|
| 1755 | } else { // mouse on valid item
|
---|
| 1756 | // but did not register mouse press
|
---|
| 1757 | if ( (e->state() & Qt::MouseButtonMask) && !mouseBtDn )
|
---|
| 1758 | mouseBtDn = TRUE; // so mouseReleaseEvent will pop down
|
---|
| 1759 |
|
---|
| 1760 | register QMenuItem *mi = mitems->at( item );
|
---|
| 1761 |
|
---|
| 1762 | if ( mi->widget() ) {
|
---|
| 1763 | QWidget* widgetAt = QApplication::widgetAt( e->globalPos(), TRUE );
|
---|
| 1764 | if ( widgetAt && widgetAt != this ) {
|
---|
| 1765 | QMouseEvent me( e->type(), widgetAt->mapFromGlobal( e->globalPos() ),
|
---|
| 1766 | e->globalPos(), e->button(), e->state() );
|
---|
| 1767 | QApplication::sendEvent( widgetAt, &me );
|
---|
| 1768 | }
|
---|
| 1769 | }
|
---|
| 1770 |
|
---|
| 1771 | if ( actItem == item )
|
---|
| 1772 | return;
|
---|
| 1773 |
|
---|
| 1774 | if ( style().styleHint(QStyle::SH_PopupMenu_SloppySubMenus, this) &&
|
---|
| 1775 | d->mouseMoveBuffer.contains( e->pos() ) ) {
|
---|
| 1776 | actItem = item;
|
---|
| 1777 | popupSubMenuLater( style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this) * 6,
|
---|
| 1778 | this );
|
---|
| 1779 | return;
|
---|
| 1780 | }
|
---|
| 1781 |
|
---|
| 1782 | if ( mi->popup() || ( popupActive >= 0 && popupActive != item ))
|
---|
| 1783 | popupSubMenuLater( style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this),
|
---|
| 1784 | this );
|
---|
| 1785 | else if ( singleSingleShot )
|
---|
| 1786 | singleSingleShot->stop();
|
---|
| 1787 |
|
---|
| 1788 | if ( item != actItem )
|
---|
| 1789 | setActiveItem( item );
|
---|
| 1790 | }
|
---|
| 1791 | }
|
---|
| 1792 |
|
---|
| 1793 |
|
---|
| 1794 | /*!
|
---|
| 1795 | \reimp
|
---|
| 1796 | */
|
---|
| 1797 |
|
---|
| 1798 | void QPopupMenu::keyPressEvent( QKeyEvent *e )
|
---|
| 1799 | {
|
---|
| 1800 | /*
|
---|
| 1801 | I get nothing but complaints about this. -Brad
|
---|
| 1802 |
|
---|
| 1803 | - if (mouseBtDn && actItem >= 0) {
|
---|
| 1804 | - if (e->key() == Key_Shift ||
|
---|
| 1805 | - e->key() == Key_Control ||
|
---|
| 1806 | - e->key() == Key_Alt)
|
---|
| 1807 | - return;
|
---|
| 1808 | -
|
---|
| 1809 | - QMenuItem *mi = mitems->at(actItem);
|
---|
| 1810 | - int modifier = (((e->state() & ShiftButton) ? SHIFT : 0) |
|
---|
| 1811 | - ((e->state() & ControlButton) ? CTRL : 0) |
|
---|
| 1812 | - ((e->state() & AltButton) ? ALT : 0));
|
---|
| 1813 | -
|
---|
| 1814 | - #ifndef QT_NO_ACCEL
|
---|
| 1815 | - if (mi)
|
---|
| 1816 | - setAccel(modifier + e->key(), mi->id());
|
---|
| 1817 | - #endif
|
---|
| 1818 | - return;
|
---|
| 1819 | - }
|
---|
| 1820 | */
|
---|
| 1821 |
|
---|
| 1822 | QMenuItem *mi = 0;
|
---|
| 1823 | QPopupMenu *popup;
|
---|
| 1824 | int dy = 0;
|
---|
| 1825 | bool ok_key = TRUE;
|
---|
| 1826 |
|
---|
| 1827 | int key = e->key();
|
---|
| 1828 | if ( QApplication::reverseLayout() ) {
|
---|
| 1829 | // in reverse mode opening and closing keys for submenues are reversed
|
---|
| 1830 | if ( key == Key_Left )
|
---|
| 1831 | key = Key_Right;
|
---|
| 1832 | else if ( key == Key_Right )
|
---|
| 1833 | key = Key_Left;
|
---|
| 1834 | }
|
---|
| 1835 |
|
---|
| 1836 | switch ( key ) {
|
---|
| 1837 | case Key_Tab:
|
---|
| 1838 | // ignore tab, otherwise it will be passed to the menubar
|
---|
| 1839 | break;
|
---|
| 1840 |
|
---|
| 1841 | case Key_Up:
|
---|
| 1842 | dy = -1;
|
---|
| 1843 | break;
|
---|
| 1844 |
|
---|
| 1845 | case Key_Down:
|
---|
| 1846 | dy = 1;
|
---|
| 1847 | break;
|
---|
| 1848 |
|
---|
| 1849 | case Key_Alt:
|
---|
| 1850 | if ( style().styleHint(QStyle::SH_MenuBar_AltKeyNavigation, this) )
|
---|
| 1851 | byeMenuBar();
|
---|
| 1852 | break;
|
---|
| 1853 |
|
---|
| 1854 | case Key_Escape:
|
---|
| 1855 | if ( tornOff ) {
|
---|
| 1856 | close();
|
---|
| 1857 | return;
|
---|
| 1858 | }
|
---|
| 1859 | // just hide one
|
---|
| 1860 | {
|
---|
| 1861 | QMenuData* p = parentMenu;
|
---|
| 1862 | hide();
|
---|
| 1863 | #ifndef QT_NO_MENUBAR
|
---|
| 1864 | if ( p && p->isMenuBar )
|
---|
| 1865 | ((QMenuBar*) p)->goodbye( TRUE );
|
---|
| 1866 | #endif
|
---|
| 1867 | }
|
---|
| 1868 | break;
|
---|
| 1869 |
|
---|
| 1870 | case Key_Left:
|
---|
| 1871 | if ( ncols > 1 && actItem >= 0 ) {
|
---|
| 1872 | QRect r( itemGeometry( actItem ) );
|
---|
| 1873 | int newActItem = itemAtPos( QPoint( r.left() - 1, r.center().y() ) );
|
---|
| 1874 | if ( newActItem >= 0 ) {
|
---|
| 1875 | setActiveItem( newActItem );
|
---|
| 1876 | break;
|
---|
| 1877 | }
|
---|
| 1878 | }
|
---|
| 1879 | if ( parentMenu && parentMenu->isPopupMenu ) {
|
---|
| 1880 | ((QPopupMenu *)parentMenu)->hidePopups();
|
---|
| 1881 | if ( singleSingleShot )
|
---|
| 1882 | singleSingleShot->stop();
|
---|
| 1883 | break;
|
---|
| 1884 | }
|
---|
| 1885 |
|
---|
| 1886 | ok_key = FALSE;
|
---|
| 1887 | break;
|
---|
| 1888 |
|
---|
| 1889 | case Key_Right:
|
---|
| 1890 | if ( actItem >= 0 && ( mi=mitems->at(actItem) )->isEnabledAndVisible() && (popup=mi->popup()) ) {
|
---|
| 1891 | hidePopups();
|
---|
| 1892 | if ( singleSingleShot )
|
---|
| 1893 | singleSingleShot->stop();
|
---|
| 1894 | // ### The next two lines were switched to fix the problem with the first item of the
|
---|
| 1895 | // submenu not being highlighted...any reason why they should have been the other way??
|
---|
| 1896 | subMenuTimer();
|
---|
| 1897 | popup->setFirstItemActive();
|
---|
| 1898 | break;
|
---|
| 1899 | } else if ( actItem == -1 && ( parentMenu && !parentMenu->isMenuBar )) {
|
---|
| 1900 | dy = 1;
|
---|
| 1901 | break;
|
---|
| 1902 | }
|
---|
| 1903 | if ( ncols > 1 && actItem >= 0 ) {
|
---|
| 1904 | QRect r( itemGeometry( actItem ) );
|
---|
| 1905 | int newActItem = itemAtPos( QPoint( r.right() + 1, r.center().y() ) );
|
---|
| 1906 | if ( newActItem >= 0 ) {
|
---|
| 1907 | setActiveItem( newActItem );
|
---|
| 1908 | break;
|
---|
| 1909 | }
|
---|
| 1910 | }
|
---|
| 1911 | ok_key = FALSE;
|
---|
| 1912 | break;
|
---|
| 1913 |
|
---|
| 1914 | case Key_Space:
|
---|
| 1915 | if (! style().styleHint(QStyle::SH_PopupMenu_SpaceActivatesItem, this))
|
---|
| 1916 | break;
|
---|
| 1917 | // for motif, fall through
|
---|
| 1918 |
|
---|
| 1919 | case Key_Return:
|
---|
| 1920 | case Key_Enter:
|
---|
| 1921 | {
|
---|
| 1922 | if ( actItem < 0 )
|
---|
| 1923 | break;
|
---|
| 1924 | #ifndef QT_NO_WHATSTHIS
|
---|
| 1925 | bool b = QWhatsThis::inWhatsThisMode();
|
---|
| 1926 | #else
|
---|
| 1927 | const bool b = FALSE;
|
---|
| 1928 | #endif
|
---|
| 1929 | mi = mitems->at( actItem );
|
---|
[8] | 1930 | if ( !mi->isEnabled() && !b ) {
|
---|
| 1931 | #if defined(Q_WS_PM)
|
---|
| 1932 | //@@TODO (dmik): we need to play a sound when a disabled item is activated
|
---|
| 1933 | // to conform to the OS/2 CUA. Btw, should'n it be supported by QAccessibility
|
---|
| 1934 | // globally in Qt?
|
---|
| 1935 | WinAlarm( HWND_DESKTOP, WA_ERROR );
|
---|
| 1936 | #endif
|
---|
[2] | 1937 | break;
|
---|
[8] | 1938 | }
|
---|
[2] | 1939 | popup = mi->popup();
|
---|
| 1940 | if ( popup ) {
|
---|
| 1941 | hidePopups();
|
---|
| 1942 | popupSubMenuLater( 20, this );
|
---|
| 1943 | popup->setFirstItemActive();
|
---|
| 1944 | } else {
|
---|
| 1945 | actItem = -1;
|
---|
| 1946 | updateItem( mi->id() );
|
---|
| 1947 | byeMenuBar();
|
---|
| 1948 | if ( mi->isEnabledAndVisible() || b ) {
|
---|
| 1949 | active_popup_menu = this;
|
---|
| 1950 | QGuardedPtr<QSignal> signal = mi->signal();
|
---|
| 1951 | actSig( mi->id(), b );
|
---|
| 1952 | if ( signal && !b )
|
---|
| 1953 | signal->activate();
|
---|
| 1954 | active_popup_menu = 0;
|
---|
| 1955 | }
|
---|
| 1956 | }
|
---|
| 1957 | }
|
---|
| 1958 | break;
|
---|
| 1959 | #ifndef QT_NO_WHATSTHIS
|
---|
| 1960 | case Key_F1:
|
---|
| 1961 | if ( actItem < 0 || e->state() != ShiftButton)
|
---|
| 1962 | break;
|
---|
| 1963 | mi = mitems->at( actItem );
|
---|
| 1964 | if ( !mi->whatsThis().isNull() ){
|
---|
| 1965 | if ( !QWhatsThis::inWhatsThisMode() )
|
---|
| 1966 | QWhatsThis::enterWhatsThisMode();
|
---|
| 1967 | QRect r( itemGeometry( actItem) );
|
---|
| 1968 | QWhatsThis::leaveWhatsThisMode( mi->whatsThis(), mapToGlobal( r.bottomLeft()) );
|
---|
| 1969 | }
|
---|
| 1970 | //fall-through!
|
---|
| 1971 | #endif
|
---|
| 1972 | default:
|
---|
| 1973 | ok_key = FALSE;
|
---|
| 1974 |
|
---|
| 1975 | }
|
---|
| 1976 | if ( !ok_key &&
|
---|
| 1977 | ( !e->state() || e->state() == AltButton || e->state() == ShiftButton ) &&
|
---|
| 1978 | e->text().length()==1 ) {
|
---|
| 1979 | QChar c = e->text()[0].upper();
|
---|
| 1980 |
|
---|
| 1981 | QMenuItemListIt it(*mitems);
|
---|
| 1982 | QMenuItem* first = 0;
|
---|
| 1983 | QMenuItem* currentSelected = 0;
|
---|
| 1984 | QMenuItem* firstAfterCurrent = 0;
|
---|
| 1985 |
|
---|
| 1986 | register QMenuItem *m;
|
---|
| 1987 | mi = 0;
|
---|
| 1988 | int indx = 0;
|
---|
| 1989 | int clashCount = 0;
|
---|
| 1990 | while ( (m=it.current()) ) {
|
---|
| 1991 | ++it;
|
---|
| 1992 | QString s = m->text();
|
---|
| 1993 | if ( !s.isEmpty() ) {
|
---|
| 1994 | int i = s.find( '&' );
|
---|
| 1995 | while ( i >= 0 && i < (int)s.length() - 1 ) {
|
---|
| 1996 | if ( s[i+1].upper() == c ) {
|
---|
| 1997 | ok_key = TRUE;
|
---|
| 1998 | clashCount++;
|
---|
| 1999 | if ( !first )
|
---|
| 2000 | first = m;
|
---|
| 2001 | if ( indx == actItem )
|
---|
| 2002 | currentSelected = m;
|
---|
| 2003 | else if ( !firstAfterCurrent && currentSelected )
|
---|
| 2004 | firstAfterCurrent = m;
|
---|
| 2005 | break;
|
---|
| 2006 | } else if ( s[i+1] == '&' ) {
|
---|
| 2007 | i = s.find( '&', i+2 );
|
---|
| 2008 | } else {
|
---|
| 2009 | break;
|
---|
| 2010 | }
|
---|
| 2011 | }
|
---|
| 2012 | }
|
---|
| 2013 | if ( mi )
|
---|
| 2014 | break;
|
---|
| 2015 | indx++;
|
---|
| 2016 | }
|
---|
| 2017 |
|
---|
| 2018 | if ( 1 == clashCount ) { // No clashes, continue with selection
|
---|
| 2019 | mi = first;
|
---|
| 2020 | popup = mi->popup();
|
---|
| 2021 | if ( popup ) {
|
---|
| 2022 | setActiveItem( indexOf(mi->id()) );
|
---|
| 2023 | hidePopups();
|
---|
| 2024 | popupSubMenuLater( 20, this );
|
---|
| 2025 | popup->setFirstItemActive();
|
---|
| 2026 | } else {
|
---|
[8] | 2027 | #if !defined(Q_WS_PM) // do it later, only in case of item activation
|
---|
[2] | 2028 | byeMenuBar();
|
---|
[8] | 2029 | #endif
|
---|
[2] | 2030 | #ifndef QT_NO_WHATSTHIS
|
---|
| 2031 | bool b = QWhatsThis::inWhatsThisMode();
|
---|
| 2032 | #else
|
---|
| 2033 | const bool b = FALSE;
|
---|
| 2034 | #endif
|
---|
| 2035 | if ( mi->isEnabledAndVisible() || b ) {
|
---|
[8] | 2036 | #if defined(Q_WS_PM)
|
---|
| 2037 | byeMenuBar();
|
---|
| 2038 | #endif
|
---|
[2] | 2039 | active_popup_menu = this;
|
---|
| 2040 | QGuardedPtr<QSignal> signal = mi->signal();
|
---|
| 2041 | actSig( mi->id(), b );
|
---|
| 2042 | if ( signal && !b )
|
---|
| 2043 | signal->activate();
|
---|
| 2044 | active_popup_menu = 0;
|
---|
| 2045 | }
|
---|
[8] | 2046 | #if defined(Q_WS_PM)
|
---|
| 2047 | //@@TODO (dmik): we need to play a sound when the disabled item is activated
|
---|
| 2048 | // to conform to the OS/2 CUA. Btw, should'n it be supported by QAccessibility
|
---|
| 2049 | // globally in Qt?
|
---|
| 2050 | else
|
---|
| 2051 | WinAlarm( HWND_DESKTOP, WA_ERROR );
|
---|
| 2052 | #endif
|
---|
[2] | 2053 | }
|
---|
| 2054 | } else if ( clashCount > 1 ) { // Clashes, highlight next...
|
---|
| 2055 | // If there's clashes and no one is selected, use first one
|
---|
| 2056 | // or if there is no clashes _after_ current, use first one
|
---|
| 2057 | if ( !currentSelected || (currentSelected && !firstAfterCurrent))
|
---|
| 2058 | dy = indexOf( first->id() ) - actItem;
|
---|
| 2059 | else
|
---|
| 2060 | dy = indexOf( firstAfterCurrent->id() ) - actItem;
|
---|
| 2061 | }
|
---|
| 2062 | }
|
---|
| 2063 | #ifndef QT_NO_MENUBAR
|
---|
| 2064 | if ( !ok_key ) { // send to menu bar
|
---|
| 2065 | register QMenuData *top = this; // find top level
|
---|
| 2066 | while ( top->parentMenu )
|
---|
| 2067 | top = top->parentMenu;
|
---|
| 2068 | if ( top->isMenuBar ) {
|
---|
| 2069 | int beforeId = top->actItem;
|
---|
| 2070 | ((QMenuBar*)top)->tryKeyEvent( this, e );
|
---|
| 2071 | if ( beforeId != top->actItem )
|
---|
| 2072 | ok_key = TRUE;
|
---|
| 2073 | }
|
---|
| 2074 | }
|
---|
| 2075 | #endif
|
---|
| 2076 | if ( actItem < 0 ) {
|
---|
| 2077 | if ( dy > 0 ) {
|
---|
| 2078 | setFirstItemActive();
|
---|
| 2079 | } else if ( dy < 0 ) {
|
---|
| 2080 | QMenuItemListIt it(*mitems);
|
---|
| 2081 | it.toLast();
|
---|
| 2082 | register QMenuItem *mi;
|
---|
| 2083 | int ai = count() - 1;
|
---|
| 2084 | while ( (mi=it.current()) ) {
|
---|
| 2085 | --it;
|
---|
| 2086 | if ( !mi->isSeparator() && mi->id() != QMenuData::d->aInt ) {
|
---|
| 2087 | setActiveItem( ai );
|
---|
| 2088 | return;
|
---|
| 2089 | }
|
---|
| 2090 | ai--;
|
---|
| 2091 | }
|
---|
| 2092 | actItem = -1;
|
---|
| 2093 | }
|
---|
| 2094 | return;
|
---|
| 2095 | }
|
---|
| 2096 |
|
---|
| 2097 | if ( dy ) { // highlight next/prev
|
---|
| 2098 | register int i = actItem;
|
---|
| 2099 | int c = mitems->count();
|
---|
| 2100 | for(int n = c; n; n--) {
|
---|
| 2101 | i = i + dy;
|
---|
| 2102 | if(d->scroll.scrollable) {
|
---|
| 2103 | if(d->scroll.scrolltimer)
|
---|
| 2104 | d->scroll.scrolltimer->stop();
|
---|
| 2105 | if(i < 0)
|
---|
| 2106 | i = 0;
|
---|
| 2107 | else if(i >= c)
|
---|
| 2108 | i = c - 1;
|
---|
| 2109 | } else {
|
---|
| 2110 | if ( i == c )
|
---|
| 2111 | i = 0;
|
---|
| 2112 | else if ( i < 0 )
|
---|
| 2113 | i = c - 1;
|
---|
| 2114 | }
|
---|
| 2115 | mi = mitems->at( i );
|
---|
| 2116 | if ( !mi || !mi->isVisible() )
|
---|
| 2117 | continue;
|
---|
| 2118 |
|
---|
| 2119 | if ( !mi->isSeparator() &&
|
---|
| 2120 | ( style().styleHint(QStyle::SH_PopupMenu_AllowActiveAndDisabled, this)
|
---|
| 2121 | || mi->isEnabledAndVisible() ) )
|
---|
| 2122 | break;
|
---|
| 2123 | }
|
---|
| 2124 | if ( i != actItem )
|
---|
| 2125 | setActiveItem( i );
|
---|
| 2126 | if(d->scroll.scrollable) { //need to scroll to make it visible?
|
---|
| 2127 | QRect r = itemGeometry(actItem);
|
---|
| 2128 | if(r.isNull() || r.height() < itemHeight(mitems->at(actItem))) {
|
---|
| 2129 | bool refresh = FALSE;
|
---|
| 2130 | if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp && dy == -1) { //up
|
---|
| 2131 | if(d->scroll.topScrollableIndex >= 0) {
|
---|
| 2132 | d->scroll.topScrollableIndex--;
|
---|
| 2133 | refresh = TRUE;
|
---|
| 2134 | }
|
---|
| 2135 | } else if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown) { //down
|
---|
| 2136 | QMenuItemListIt it(*mitems);
|
---|
| 2137 | int sh = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
---|
| 2138 | for(int i = 0, y = ((d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp) ? sh : 0); it.current(); i++, ++it) {
|
---|
| 2139 | if(i >= d->scroll.topScrollableIndex) {
|
---|
| 2140 | int itemh = itemHeight(it.current());
|
---|
| 2141 | QSize sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
---|
| 2142 | QSize(0, itemh),
|
---|
| 2143 | QStyleOption(it.current(),maxPMWidth,0));
|
---|
| 2144 | y += sz.height();
|
---|
| 2145 | if(y > (contentsRect().height()-sh)) {
|
---|
| 2146 | if(sz.height() > sh || !it.atLast())
|
---|
| 2147 | d->scroll.topScrollableIndex++;
|
---|
| 2148 | refresh = TRUE;
|
---|
| 2149 | break;
|
---|
| 2150 | }
|
---|
| 2151 | }
|
---|
| 2152 | }
|
---|
| 2153 | }
|
---|
| 2154 | if(refresh) {
|
---|
| 2155 | updateScrollerState();
|
---|
| 2156 | update();
|
---|
| 2157 | }
|
---|
| 2158 | }
|
---|
| 2159 | }
|
---|
| 2160 | }
|
---|
| 2161 |
|
---|
| 2162 | #ifdef Q_OS_WIN32
|
---|
| 2163 | if ( !ok_key &&
|
---|
| 2164 | !( e->key() == Key_Control || e->key() == Key_Shift || e->key() == Key_Meta ) )
|
---|
| 2165 | qApp->beep();
|
---|
| 2166 | #endif // Q_OS_WIN32
|
---|
| 2167 | }
|
---|
| 2168 |
|
---|
| 2169 |
|
---|
| 2170 | /*!
|
---|
| 2171 | \reimp
|
---|
| 2172 | */
|
---|
| 2173 |
|
---|
| 2174 | void QPopupMenu::timerEvent( QTimerEvent *e )
|
---|
| 2175 | {
|
---|
| 2176 | QFrame::timerEvent( e );
|
---|
| 2177 | }
|
---|
| 2178 |
|
---|
| 2179 | /*!
|
---|
| 2180 | \reimp
|
---|
| 2181 | */
|
---|
| 2182 | void QPopupMenu::leaveEvent( QEvent * )
|
---|
| 2183 | {
|
---|
| 2184 | if ( testWFlags( WStyle_Tool ) && style().styleHint(QStyle::SH_PopupMenu_MouseTracking, this) ) {
|
---|
| 2185 | int lastActItem = actItem;
|
---|
| 2186 | actItem = -1;
|
---|
| 2187 | if ( lastActItem >= 0 )
|
---|
| 2188 | updateRow( lastActItem );
|
---|
| 2189 | }
|
---|
| 2190 | }
|
---|
| 2191 |
|
---|
| 2192 | /*!
|
---|
| 2193 | \reimp
|
---|
| 2194 | */
|
---|
| 2195 | void QPopupMenu::styleChange( QStyle& old )
|
---|
| 2196 | {
|
---|
| 2197 | QFrame::styleChange( old );
|
---|
| 2198 | setMouseTracking(style().styleHint(QStyle::SH_PopupMenu_MouseTracking, this));
|
---|
| 2199 | style().polishPopupMenu( this );
|
---|
| 2200 | updateSize(TRUE);
|
---|
| 2201 | }
|
---|
| 2202 |
|
---|
| 2203 | /*!\reimp
|
---|
| 2204 | */
|
---|
| 2205 | void QPopupMenu::enabledChange( bool )
|
---|
| 2206 | {
|
---|
| 2207 | if ( QMenuData::d->aWidget ) // torn-off menu
|
---|
| 2208 | QMenuData::d->aWidget->setEnabled( isEnabled() );
|
---|
| 2209 | }
|
---|
| 2210 |
|
---|
| 2211 |
|
---|
| 2212 | /*!
|
---|
| 2213 | If a popup menu does not fit on the screen it lays itself out so
|
---|
| 2214 | that it does fit. It is style dependent what layout means (for
|
---|
| 2215 | example, on Windows it will use multiple columns).
|
---|
| 2216 |
|
---|
| 2217 | This functions returns the number of columns necessary.
|
---|
| 2218 |
|
---|
| 2219 | \sa sizeHint()
|
---|
| 2220 | */
|
---|
| 2221 | int QPopupMenu::columns() const
|
---|
| 2222 | {
|
---|
| 2223 | return ncols;
|
---|
| 2224 | }
|
---|
| 2225 |
|
---|
| 2226 | /* This private slot handles the scrolling popupmenu */
|
---|
| 2227 | void QPopupMenu::subScrollTimer() {
|
---|
| 2228 | QPoint pos = QCursor::pos();
|
---|
| 2229 | if(!d->scroll.scrollable || !isVisible()) {
|
---|
| 2230 | if(d->scroll.scrolltimer)
|
---|
| 2231 | d->scroll.scrolltimer->stop();
|
---|
| 2232 | return;
|
---|
| 2233 | } else if(pos.x() > x() + width() || pos.x() < x()) {
|
---|
| 2234 | return;
|
---|
| 2235 | }
|
---|
| 2236 | int sh = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
---|
| 2237 | if(!d->scroll.lastScroll.isValid()) {
|
---|
| 2238 | d->scroll.lastScroll = QTime::currentTime();
|
---|
| 2239 | } else {
|
---|
| 2240 | int factor=0;
|
---|
| 2241 | if(pos.y() < y())
|
---|
| 2242 | factor = y() - pos.y();
|
---|
| 2243 | else if(pos.y() > y() + height())
|
---|
| 2244 | factor = pos.y() - (y() + height());
|
---|
| 2245 | int msecs = 250 - ((factor / 10) * 40);
|
---|
| 2246 | if(d->scroll.lastScroll.msecsTo(QTime::currentTime()) < QMAX(0, msecs))
|
---|
| 2247 | return;
|
---|
| 2248 | d->scroll.lastScroll = QTime::currentTime();
|
---|
| 2249 | }
|
---|
| 2250 | if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp && pos.y() <= y() + sh) { //up
|
---|
| 2251 | if(d->scroll.topScrollableIndex > 0) {
|
---|
| 2252 | d->scroll.topScrollableIndex--;
|
---|
| 2253 | updateScrollerState();
|
---|
| 2254 | update(contentsRect());
|
---|
| 2255 | }
|
---|
| 2256 | } else if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown &&
|
---|
| 2257 | pos.y() >= (y() + contentsRect().height()) - sh) { //down
|
---|
| 2258 | QMenuItemListIt it(*mitems);
|
---|
| 2259 | for(int i = 0, y = contentsRect().y() + sh; it.current(); i++, ++it) {
|
---|
| 2260 | if(i >= d->scroll.topScrollableIndex) {
|
---|
| 2261 | int itemh = itemHeight(it.current());
|
---|
| 2262 | QSize sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this, QSize(0, itemh),
|
---|
| 2263 | QStyleOption(it.current(),maxPMWidth,0));
|
---|
| 2264 | y += sz.height();
|
---|
| 2265 | if(y > contentsRect().height() - sh) {
|
---|
| 2266 | d->scroll.topScrollableIndex++;
|
---|
| 2267 | updateScrollerState();
|
---|
| 2268 | update(contentsRect());
|
---|
| 2269 | break;
|
---|
| 2270 | }
|
---|
| 2271 | }
|
---|
| 2272 | }
|
---|
| 2273 | }
|
---|
| 2274 | }
|
---|
| 2275 |
|
---|
| 2276 | /* This private slot handles the delayed submenu effects */
|
---|
| 2277 |
|
---|
| 2278 | void QPopupMenu::subMenuTimer() {
|
---|
| 2279 |
|
---|
| 2280 | if ( !isVisible() || (actItem < 0 && popupActive < 0) || actItem == popupActive )
|
---|
| 2281 | return;
|
---|
| 2282 |
|
---|
| 2283 | if ( popupActive >= 0 ) {
|
---|
| 2284 | hidePopups();
|
---|
| 2285 | popupActive = -1;
|
---|
| 2286 | }
|
---|
| 2287 |
|
---|
| 2288 | // hidePopups() may change actItem etc.
|
---|
| 2289 | if ( !isVisible() || actItem < 0 || actItem == popupActive )
|
---|
| 2290 | return;
|
---|
| 2291 |
|
---|
| 2292 | QMenuItem *mi = mitems->at(actItem);
|
---|
| 2293 | if ( !mi || !mi->isEnabledAndVisible() )
|
---|
| 2294 | return;
|
---|
| 2295 |
|
---|
| 2296 | QPopupMenu *popup = mi->popup();
|
---|
| 2297 | if ( !popup || !popup->isEnabled() )
|
---|
| 2298 | return;
|
---|
| 2299 |
|
---|
| 2300 | //avoid circularity
|
---|
| 2301 | if ( popup->isVisible() )
|
---|
| 2302 | return;
|
---|
| 2303 |
|
---|
| 2304 | Q_ASSERT( popup->parentMenu == 0 );
|
---|
| 2305 | popup->parentMenu = this; // set parent menu
|
---|
| 2306 |
|
---|
| 2307 | emit popup->aboutToShow();
|
---|
| 2308 | supressAboutToShow = TRUE;
|
---|
| 2309 |
|
---|
| 2310 |
|
---|
| 2311 | QRect r( itemGeometry( actItem ) );
|
---|
| 2312 | QPoint p;
|
---|
| 2313 | QSize ps = popup->sizeHint();
|
---|
| 2314 | if( QApplication::reverseLayout() ) {
|
---|
| 2315 | p = QPoint( r.left() + motifArrowHMargin - ps.width(), r.top() + motifArrowVMargin );
|
---|
| 2316 | p = mapToGlobal( p );
|
---|
| 2317 |
|
---|
| 2318 | bool right = FALSE;
|
---|
| 2319 | if ( ( parentMenu && parentMenu->isPopupMenu &&
|
---|
| 2320 | ((QPopupMenu*)parentMenu)->geometry().x() < geometry().x() ) ||
|
---|
| 2321 | p.x() < 0 )
|
---|
| 2322 | right = TRUE;
|
---|
| 2323 | if ( right && (ps.width() > QApplication::desktop()->width() - mapToGlobal( r.topRight() ).x() ) )
|
---|
| 2324 | right = FALSE;
|
---|
| 2325 | if ( right )
|
---|
| 2326 | p.setX( mapToGlobal( r.topRight() ).x() );
|
---|
| 2327 | } else {
|
---|
| 2328 | p = QPoint( r.right() - motifArrowHMargin, r.top() + motifArrowVMargin );
|
---|
| 2329 | p = mapToGlobal( p );
|
---|
| 2330 |
|
---|
| 2331 | bool left = FALSE;
|
---|
| 2332 | if ( ( parentMenu && parentMenu->isPopupMenu &&
|
---|
| 2333 | ((QPopupMenu*)parentMenu)->geometry().x() > geometry().x() ) ||
|
---|
| 2334 | p.x() + ps.width() > QApplication::desktop()->width() )
|
---|
| 2335 | left = TRUE;
|
---|
| 2336 | if ( left && (ps.width() > mapToGlobal( r.topLeft() ).x() ) )
|
---|
| 2337 | left = FALSE;
|
---|
| 2338 | if ( left )
|
---|
| 2339 | p.setX( mapToGlobal( r.topLeft() ).x() - ps.width() );
|
---|
| 2340 | }
|
---|
| 2341 | QRect pr = popup->itemGeometry(popup->count() - 1);
|
---|
| 2342 | if (p.y() + ps.height() > QApplication::desktop()->height() &&
|
---|
| 2343 | p.y() - ps.height() + (QCOORD) pr.height() >= 0)
|
---|
| 2344 | p.setY( p.y() - ps.height() + (QCOORD) pr.height());
|
---|
| 2345 |
|
---|
| 2346 | if ( style().styleHint(QStyle::SH_PopupMenu_SloppySubMenus, this )) {
|
---|
| 2347 | QPoint cur = QCursor::pos();
|
---|
| 2348 | if ( r.contains( mapFromGlobal( cur ) ) ) {
|
---|
| 2349 | QPoint pts[4];
|
---|
| 2350 | pts[0] = QPoint( cur.x(), cur.y() - 2 );
|
---|
| 2351 | pts[3] = QPoint( cur.x(), cur.y() + 2 );
|
---|
| 2352 | if ( p.x() >= cur.x() ) {
|
---|
| 2353 | pts[1] = QPoint( geometry().right(), p.y() );
|
---|
| 2354 | pts[2] = QPoint( geometry().right(), p.y() + ps.height() );
|
---|
| 2355 | } else {
|
---|
| 2356 | pts[1] = QPoint( p.x() + ps.width(), p.y() );
|
---|
| 2357 | pts[2] = QPoint( p.x() + ps.width(), p.y() + ps.height() );
|
---|
| 2358 | }
|
---|
| 2359 | QPointArray points( 4 );
|
---|
| 2360 | for( int i = 0; i < 4; i++ )
|
---|
| 2361 | points.setPoint( i, mapFromGlobal( pts[i] ) );
|
---|
| 2362 | d->mouseMoveBuffer = QRegion( points );
|
---|
| 2363 | repaint();
|
---|
| 2364 | }
|
---|
| 2365 | }
|
---|
| 2366 |
|
---|
| 2367 | popupActive = actItem;
|
---|
| 2368 | popup->popup( p );
|
---|
| 2369 | }
|
---|
| 2370 |
|
---|
| 2371 | void QPopupMenu::allowAnimation()
|
---|
| 2372 | {
|
---|
| 2373 | preventAnimation = FALSE;
|
---|
| 2374 | }
|
---|
| 2375 |
|
---|
| 2376 | void QPopupMenu::updateRow( int row )
|
---|
| 2377 | {
|
---|
| 2378 | if ( !isVisible() )
|
---|
| 2379 | return;
|
---|
| 2380 |
|
---|
| 2381 | if ( badSize ) {
|
---|
| 2382 | updateSize();
|
---|
| 2383 | update();
|
---|
| 2384 | return;
|
---|
| 2385 | }
|
---|
| 2386 | updateSize();
|
---|
| 2387 | QRect r = itemGeometry( row );
|
---|
| 2388 | if ( !r.isNull() ) // can happen via the scroller
|
---|
| 2389 | repaint( r );
|
---|
| 2390 | }
|
---|
| 2391 |
|
---|
| 2392 |
|
---|
| 2393 | /*!
|
---|
| 2394 | \overload
|
---|
| 2395 |
|
---|
| 2396 | Executes this popup synchronously.
|
---|
| 2397 |
|
---|
| 2398 | Opens the popup menu so that the item number \a indexAtPoint will
|
---|
| 2399 | be at the specified \e global position \a pos. To translate a
|
---|
| 2400 | widget's local coordinates into global coordinates, use
|
---|
| 2401 | QWidget::mapToGlobal().
|
---|
| 2402 |
|
---|
| 2403 | The return code is the id of the selected item in either the popup
|
---|
| 2404 | menu or one of its submenus, or -1 if no item is selected
|
---|
| 2405 | (normally because the user pressed Esc).
|
---|
| 2406 |
|
---|
| 2407 | Note that all signals are emitted as usual. If you connect a menu
|
---|
| 2408 | item to a slot and call the menu's exec(), you get the result both
|
---|
| 2409 | via the signal-slot connection and in the return value of exec().
|
---|
| 2410 |
|
---|
| 2411 | Common usage is to position the popup at the current mouse
|
---|
| 2412 | position:
|
---|
| 2413 | \code
|
---|
| 2414 | exec( QCursor::pos() );
|
---|
| 2415 | \endcode
|
---|
| 2416 | or aligned to a widget:
|
---|
| 2417 | \code
|
---|
| 2418 | exec( somewidget.mapToGlobal(QPoint(0, 0)) );
|
---|
| 2419 | \endcode
|
---|
| 2420 |
|
---|
| 2421 | When positioning a popup with exec() or popup(), bear in mind that
|
---|
| 2422 | you cannot rely on the popup menu's current size(). For
|
---|
| 2423 | performance reasons, the popup adapts its size only when
|
---|
| 2424 | necessary. So in many cases, the size before and after the show is
|
---|
| 2425 | different. Instead, use sizeHint(). It calculates the proper size
|
---|
| 2426 | depending on the menu's current contents.
|
---|
| 2427 |
|
---|
| 2428 | \sa popup(), sizeHint()
|
---|
| 2429 | */
|
---|
| 2430 |
|
---|
| 2431 | int QPopupMenu::exec( const QPoint & pos, int indexAtPoint )
|
---|
| 2432 | {
|
---|
| 2433 | snapToMouse = TRUE;
|
---|
| 2434 | if ( !qApp )
|
---|
| 2435 | return -1;
|
---|
| 2436 |
|
---|
| 2437 | QPopupMenu* priorSyncMenu = syncMenu;
|
---|
| 2438 |
|
---|
| 2439 | syncMenu = this;
|
---|
| 2440 | syncMenuId = -1;
|
---|
| 2441 |
|
---|
| 2442 | QGuardedPtr<QPopupMenu> that = this;
|
---|
| 2443 | connectModal( that, TRUE );
|
---|
| 2444 | popup( pos, indexAtPoint );
|
---|
| 2445 | qApp->enter_loop();
|
---|
| 2446 | connectModal( that, FALSE );
|
---|
| 2447 |
|
---|
| 2448 | syncMenu = priorSyncMenu;
|
---|
| 2449 | return syncMenuId;
|
---|
| 2450 | }
|
---|
| 2451 |
|
---|
| 2452 |
|
---|
| 2453 |
|
---|
| 2454 | /*
|
---|
| 2455 | Connect the popup and all its submenus to modalActivation() if
|
---|
| 2456 | \a doConnect is true, otherwise disconnect.
|
---|
| 2457 | */
|
---|
| 2458 | void QPopupMenu::connectModal( QPopupMenu* receiver, bool doConnect )
|
---|
| 2459 | {
|
---|
| 2460 | if ( !receiver )
|
---|
| 2461 | return;
|
---|
| 2462 |
|
---|
| 2463 | connectModalRecursionSafety = doConnect;
|
---|
| 2464 |
|
---|
| 2465 | if ( doConnect )
|
---|
| 2466 | connect( this, SIGNAL(activated(int)),
|
---|
| 2467 | receiver, SLOT(modalActivation(int)) );
|
---|
| 2468 | else
|
---|
| 2469 | disconnect( this, SIGNAL(activated(int)),
|
---|
| 2470 | receiver, SLOT(modalActivation(int)) );
|
---|
| 2471 |
|
---|
| 2472 | QMenuItemListIt it(*mitems);
|
---|
| 2473 | register QMenuItem *mi;
|
---|
| 2474 | while ( (mi=it.current()) ) {
|
---|
| 2475 | ++it;
|
---|
| 2476 | if ( mi->popup() && mi->popup() != receiver
|
---|
| 2477 | && (bool)(mi->popup()->connectModalRecursionSafety) != doConnect )
|
---|
| 2478 | mi->popup()->connectModal( receiver, doConnect ); //avoid circular
|
---|
| 2479 | }
|
---|
| 2480 | }
|
---|
| 2481 |
|
---|
| 2482 |
|
---|
| 2483 | /*!
|
---|
| 2484 | Executes this popup synchronously.
|
---|
| 2485 |
|
---|
| 2486 | This is equivalent to \c{exec(mapToGlobal(QPoint(0,0)))}. In most
|
---|
| 2487 | situations you'll want to specify the position yourself, for
|
---|
| 2488 | example at the current mouse position:
|
---|
| 2489 | \code
|
---|
| 2490 | exec(QCursor::pos());
|
---|
| 2491 | \endcode
|
---|
| 2492 | or aligned to a widget:
|
---|
| 2493 | \code
|
---|
| 2494 | exec(somewidget.mapToGlobal(QPoint(0,0)));
|
---|
| 2495 | \endcode
|
---|
| 2496 | */
|
---|
| 2497 |
|
---|
| 2498 | int QPopupMenu::exec()
|
---|
| 2499 | {
|
---|
| 2500 | return exec(mapToGlobal(QPoint(0,0)));
|
---|
| 2501 | }
|
---|
| 2502 |
|
---|
| 2503 |
|
---|
| 2504 | /* Internal slot used for exec(). */
|
---|
| 2505 |
|
---|
| 2506 | void QPopupMenu::modalActivation( int id )
|
---|
| 2507 | {
|
---|
| 2508 | syncMenuId = id;
|
---|
| 2509 | }
|
---|
| 2510 |
|
---|
| 2511 |
|
---|
| 2512 | /*!
|
---|
| 2513 | Sets the currently active item to index \a i and repaints as necessary.
|
---|
| 2514 | */
|
---|
| 2515 |
|
---|
| 2516 | void QPopupMenu::setActiveItem( int i )
|
---|
| 2517 | {
|
---|
| 2518 | int lastActItem = actItem;
|
---|
| 2519 | actItem = i;
|
---|
| 2520 | if ( lastActItem >= 0 )
|
---|
| 2521 | updateRow( lastActItem );
|
---|
| 2522 | if ( i >= 0 && i != lastActItem )
|
---|
| 2523 | updateRow( i );
|
---|
| 2524 | QMenuItem *mi = mitems->at( actItem );
|
---|
| 2525 | if ( !mi )
|
---|
| 2526 | return;
|
---|
| 2527 |
|
---|
| 2528 | if ( mi->widget() && mi->widget()->isFocusEnabled() ) {
|
---|
| 2529 | mi->widget()->setFocus();
|
---|
| 2530 | } else {
|
---|
| 2531 | setFocus();
|
---|
| 2532 | QRect mfrect = itemGeometry( actItem );
|
---|
| 2533 | setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE );
|
---|
| 2534 | }
|
---|
| 2535 | if ( mi->id() != -1 )
|
---|
| 2536 | hilitSig( mi->id() );
|
---|
| 2537 | #ifndef QT_NO_WHATSTHIS
|
---|
| 2538 | if (whatsThisItem && whatsThisItem != mi) {
|
---|
| 2539 | qWhatsThisBDH();
|
---|
| 2540 | }
|
---|
| 2541 | whatsThisItem = mi;
|
---|
| 2542 | #endif
|
---|
| 2543 | }
|
---|
| 2544 |
|
---|
| 2545 |
|
---|
| 2546 | /*!
|
---|
| 2547 | \reimp
|
---|
| 2548 | */
|
---|
| 2549 | QSize QPopupMenu::sizeHint() const
|
---|
| 2550 | {
|
---|
| 2551 | constPolish();
|
---|
| 2552 | if(style().styleHint(QStyle::SH_PopupMenu_Scrollable, this))
|
---|
| 2553 | return minimumSize(); //can be any size..
|
---|
| 2554 |
|
---|
| 2555 | QPopupMenu* that = (QPopupMenu*) this;
|
---|
| 2556 | //We do not need a resize here, just the sizeHint..
|
---|
| 2557 | return that->updateSize(FALSE, FALSE).expandedTo( QApplication::globalStrut() );
|
---|
| 2558 | }
|
---|
| 2559 |
|
---|
| 2560 |
|
---|
| 2561 | /*!
|
---|
| 2562 | \overload
|
---|
| 2563 |
|
---|
| 2564 | Returns the id of the item at \a pos, or -1 if there is no item
|
---|
| 2565 | there or if it is a separator.
|
---|
| 2566 | */
|
---|
| 2567 | int QPopupMenu::idAt( const QPoint& pos ) const
|
---|
| 2568 | {
|
---|
| 2569 | return idAt( itemAtPos( pos ) );
|
---|
| 2570 | }
|
---|
| 2571 |
|
---|
| 2572 |
|
---|
| 2573 | /*!
|
---|
| 2574 | \fn int QPopupMenu::idAt( int index ) const
|
---|
| 2575 |
|
---|
| 2576 | Returns the identifier of the menu item at position \a index in
|
---|
| 2577 | the internal list, or -1 if \a index is out of range.
|
---|
| 2578 |
|
---|
| 2579 | \sa QMenuData::setId(), QMenuData::indexOf()
|
---|
| 2580 | */
|
---|
| 2581 |
|
---|
| 2582 |
|
---|
| 2583 | /*!
|
---|
| 2584 | \reimp
|
---|
| 2585 | */
|
---|
| 2586 | bool QPopupMenu::customWhatsThis() const
|
---|
| 2587 | {
|
---|
| 2588 | return TRUE;
|
---|
| 2589 | }
|
---|
| 2590 |
|
---|
| 2591 |
|
---|
| 2592 | /*!
|
---|
| 2593 | \reimp
|
---|
| 2594 | */
|
---|
| 2595 | bool QPopupMenu::focusNextPrevChild( bool next )
|
---|
| 2596 | {
|
---|
| 2597 | register QMenuItem *mi;
|
---|
| 2598 | int dy = next? 1 : -1;
|
---|
| 2599 | if ( dy && actItem < 0 ) {
|
---|
| 2600 | setFirstItemActive();
|
---|
| 2601 | } else if ( dy ) { // highlight next/prev
|
---|
| 2602 | register int i = actItem;
|
---|
| 2603 | int c = mitems->count();
|
---|
| 2604 | int n = c;
|
---|
| 2605 | while ( n-- ) {
|
---|
| 2606 | i = i + dy;
|
---|
| 2607 | if ( i == c )
|
---|
| 2608 | i = 0;
|
---|
| 2609 | else if ( i < 0 )
|
---|
| 2610 | i = c - 1;
|
---|
| 2611 | mi = mitems->at( i );
|
---|
| 2612 | if ( mi && !mi->isSeparator() &&
|
---|
| 2613 | ( style().styleHint(QStyle::SH_PopupMenu_AllowActiveAndDisabled, this)
|
---|
| 2614 | || mi->isEnabledAndVisible() ) )
|
---|
| 2615 | break;
|
---|
| 2616 | }
|
---|
| 2617 | if ( i != actItem )
|
---|
| 2618 | setActiveItem( i );
|
---|
| 2619 | }
|
---|
| 2620 | return TRUE;
|
---|
| 2621 | }
|
---|
| 2622 |
|
---|
| 2623 |
|
---|
| 2624 | /*!
|
---|
| 2625 | \reimp
|
---|
| 2626 | */
|
---|
| 2627 | void QPopupMenu::focusInEvent( QFocusEvent * )
|
---|
| 2628 | {
|
---|
| 2629 | }
|
---|
| 2630 |
|
---|
| 2631 | /*!
|
---|
| 2632 | \reimp
|
---|
| 2633 | */
|
---|
| 2634 | void QPopupMenu::focusOutEvent( QFocusEvent * )
|
---|
| 2635 | {
|
---|
| 2636 | }
|
---|
| 2637 |
|
---|
| 2638 |
|
---|
| 2639 | class QTearOffMenuItem : public QCustomMenuItem
|
---|
| 2640 | {
|
---|
| 2641 | public:
|
---|
| 2642 | QTearOffMenuItem()
|
---|
| 2643 | {
|
---|
| 2644 | }
|
---|
| 2645 | ~QTearOffMenuItem()
|
---|
| 2646 | {
|
---|
| 2647 | }
|
---|
| 2648 | void paint( QPainter* p, const QColorGroup& cg, bool /* act*/,
|
---|
| 2649 | bool /*enabled*/, int x, int y, int w, int h )
|
---|
| 2650 | {
|
---|
| 2651 | p->setPen( QPen( cg.dark(), 1, DashLine ) );
|
---|
| 2652 | p->drawLine( x+2, y+h/2-1, x+w-4, y+h/2-1 );
|
---|
| 2653 | p->setPen( QPen( cg.light(), 1, DashLine ) );
|
---|
| 2654 | p->drawLine( x+2, y+h/2, x+w-4, y+h/2 );
|
---|
| 2655 | }
|
---|
| 2656 | bool fullSpan() const
|
---|
| 2657 | {
|
---|
| 2658 | return TRUE;
|
---|
| 2659 | }
|
---|
| 2660 |
|
---|
| 2661 | QSize sizeHint()
|
---|
| 2662 | {
|
---|
| 2663 | return QSize( 20, 6 );
|
---|
| 2664 | }
|
---|
| 2665 | };
|
---|
| 2666 |
|
---|
| 2667 |
|
---|
| 2668 |
|
---|
| 2669 | /*!
|
---|
| 2670 | Inserts a tear-off handle into the menu. A tear-off handle is a
|
---|
| 2671 | special menu item that creates a copy of the menu when the menu is
|
---|
| 2672 | selected. This "torn-off" copy lives in a separate window. It
|
---|
| 2673 | contains the same menu items as the original menu, with the
|
---|
| 2674 | exception of the tear-off handle.
|
---|
| 2675 |
|
---|
| 2676 | The handle item is assigned the identifier \a id or an
|
---|
| 2677 | automatically generated identifier if \a id is < 0. The generated
|
---|
| 2678 | identifiers (negative integers) are guaranteed to be unique within
|
---|
| 2679 | the entire application.
|
---|
| 2680 |
|
---|
| 2681 | The \a index specifies the position in the menu. The tear-off
|
---|
| 2682 | handle is appended at the end of the list if \a index is negative.
|
---|
| 2683 | */
|
---|
| 2684 | int QPopupMenu::insertTearOffHandle( int id, int index )
|
---|
| 2685 | {
|
---|
| 2686 | int myid = insertItem( new QTearOffMenuItem, id, index );
|
---|
| 2687 | connectItem( myid, this, SLOT( toggleTearOff() ) );
|
---|
| 2688 | QMenuData::d->aInt = myid;
|
---|
| 2689 | return myid;
|
---|
| 2690 | }
|
---|
| 2691 |
|
---|
| 2692 |
|
---|
| 2693 | /*!\internal
|
---|
| 2694 |
|
---|
| 2695 | implements tear-off menus
|
---|
| 2696 | */
|
---|
| 2697 | void QPopupMenu::toggleTearOff()
|
---|
| 2698 | {
|
---|
| 2699 | if ( active_popup_menu && active_popup_menu->tornOff ) {
|
---|
| 2700 | active_popup_menu->close();
|
---|
| 2701 | } else if (QMenuData::d->aWidget ) {
|
---|
| 2702 | delete (QWidget*) QMenuData::d->aWidget; // delete the old one
|
---|
| 2703 | } else {
|
---|
| 2704 | // create a tear off menu
|
---|
| 2705 | QPopupMenu* p = new QPopupMenu( parentWidget(), "tear off menu" );
|
---|
| 2706 | connect( p, SIGNAL( activated(int) ), this, SIGNAL( activated(int) ) );
|
---|
| 2707 | #ifndef QT_NO_WIDGET_TOPEXTRA
|
---|
| 2708 | p->setCaption( caption() );
|
---|
| 2709 | #endif
|
---|
| 2710 | p->setCheckable( isCheckable() );
|
---|
| 2711 | p->reparent( parentWidget(), WType_TopLevel | WStyle_Tool |
|
---|
| 2712 | WNoAutoErase | WDestructiveClose,
|
---|
| 2713 | geometry().topLeft(), FALSE );
|
---|
| 2714 | p->mitems->setAutoDelete( FALSE );
|
---|
| 2715 | p->tornOff = TRUE;
|
---|
| 2716 | for ( QMenuItemListIt it( *mitems ); it.current(); ++it ) {
|
---|
| 2717 | if ( it.current()->id() != QMenuData::d->aInt && !it.current()->widget() )
|
---|
| 2718 | p->mitems->append( it.current() );
|
---|
| 2719 | }
|
---|
| 2720 | p->show();
|
---|
| 2721 | QMenuData::d->aWidget = p;
|
---|
| 2722 | }
|
---|
| 2723 | }
|
---|
| 2724 |
|
---|
| 2725 | /*!
|
---|
| 2726 | \reimp
|
---|
| 2727 | */
|
---|
| 2728 | void QPopupMenu::activateItemAt( int index )
|
---|
| 2729 | {
|
---|
| 2730 | if ( index >= 0 && index < (int) mitems->count() ) {
|
---|
| 2731 | QMenuItem *mi = mitems->at( index );
|
---|
| 2732 | if ( index != actItem ) // new item activated
|
---|
| 2733 | setActiveItem( index );
|
---|
| 2734 | QPopupMenu *popup = mi->popup();
|
---|
| 2735 | if ( popup ) {
|
---|
| 2736 | if ( popup->isVisible() ) { // sub menu already open
|
---|
| 2737 | int pactItem = popup->actItem;
|
---|
| 2738 | popup->actItem = -1;
|
---|
| 2739 | popup->hidePopups();
|
---|
| 2740 | popup->updateRow( pactItem );
|
---|
| 2741 | } else { // open sub menu
|
---|
| 2742 | hidePopups();
|
---|
| 2743 | actItem = index;
|
---|
| 2744 | subMenuTimer();
|
---|
| 2745 | popup->setFirstItemActive();
|
---|
| 2746 | }
|
---|
| 2747 | } else {
|
---|
| 2748 | byeMenuBar(); // deactivate menu bar
|
---|
| 2749 |
|
---|
| 2750 | #ifndef QT_NO_WHATSTHIS
|
---|
| 2751 | bool b = QWhatsThis::inWhatsThisMode();
|
---|
| 2752 | #else
|
---|
| 2753 | const bool b = FALSE;
|
---|
| 2754 | #endif
|
---|
| 2755 | if ( !mi->isEnabledAndVisible() ) {
|
---|
| 2756 | #ifndef QT_NO_WHATSTHIS
|
---|
| 2757 | if ( b ) {
|
---|
| 2758 | actItem = -1;
|
---|
| 2759 | updateItem( mi->id() );
|
---|
| 2760 | byeMenuBar();
|
---|
| 2761 | actSig( mi->id(), b);
|
---|
| 2762 | }
|
---|
| 2763 | #endif
|
---|
| 2764 | } else {
|
---|
| 2765 | byeMenuBar(); // deactivate menu bar
|
---|
| 2766 | if ( mi->isEnabledAndVisible() ) {
|
---|
| 2767 | actItem = -1;
|
---|
| 2768 | updateItem( mi->id() );
|
---|
| 2769 | active_popup_menu = this;
|
---|
| 2770 | QGuardedPtr<QSignal> signal = mi->signal();
|
---|
| 2771 | actSig( mi->id(), b );
|
---|
| 2772 | if ( signal && !b )
|
---|
| 2773 | signal->activate();
|
---|
| 2774 | active_popup_menu = 0;
|
---|
| 2775 | }
|
---|
| 2776 | }
|
---|
| 2777 | }
|
---|
| 2778 | } else {
|
---|
| 2779 | if ( tornOff ) {
|
---|
| 2780 | close();
|
---|
| 2781 | } else {
|
---|
| 2782 | QMenuData* p = parentMenu;
|
---|
| 2783 | hide();
|
---|
| 2784 | #ifndef QT_NO_MENUBAR
|
---|
| 2785 | if ( p && p->isMenuBar )
|
---|
| 2786 | ((QMenuBar*) p)->goodbye( TRUE );
|
---|
| 2787 | #endif
|
---|
| 2788 | }
|
---|
| 2789 | }
|
---|
| 2790 |
|
---|
| 2791 | }
|
---|
| 2792 |
|
---|
| 2793 | /*! \internal
|
---|
| 2794 | This private function is to update the scroll states in styles that support scrolling. */
|
---|
| 2795 | void
|
---|
| 2796 | QPopupMenu::updateScrollerState()
|
---|
| 2797 | {
|
---|
| 2798 | uint old_scrollable = d->scroll.scrollable;
|
---|
| 2799 | d->scroll.scrollable = QPopupMenuPrivate::Scroll::ScrollNone;
|
---|
| 2800 | if(!style().styleHint(QStyle::SH_PopupMenu_Scrollable, this))
|
---|
| 2801 | return;
|
---|
| 2802 |
|
---|
| 2803 | QMenuItem *mi;
|
---|
| 2804 | QMenuItemListIt it( *mitems );
|
---|
| 2805 | if(d->scroll.topScrollableIndex) {
|
---|
| 2806 | for(int row = 0; (mi = it.current()) && row < d->scroll.topScrollableIndex; row++)
|
---|
| 2807 | ++it;
|
---|
| 2808 | if(!mi)
|
---|
| 2809 | it.toFirst();
|
---|
| 2810 | }
|
---|
| 2811 | int y = 0, sh = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
---|
| 2812 | if(!it.atFirst()) {
|
---|
| 2813 | // can't use |= because of a bug/feature in IBM xlC 5.0.2
|
---|
| 2814 | d->scroll.scrollable = d->scroll.scrollable | QPopupMenuPrivate::Scroll::ScrollUp;
|
---|
| 2815 | y += sh;
|
---|
| 2816 | }
|
---|
| 2817 | while ( (mi=it.current()) ) {
|
---|
| 2818 | ++it;
|
---|
| 2819 | int myheight = contentsRect().height();
|
---|
| 2820 | QSize sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
---|
| 2821 | QSize(0, itemHeight( mi )),
|
---|
| 2822 | QStyleOption(mi,maxPMWidth));
|
---|
| 2823 | if(y + sz.height() >= myheight) {
|
---|
| 2824 | d->scroll.scrollable = d->scroll.scrollable | QPopupMenuPrivate::Scroll::ScrollDown;
|
---|
| 2825 | break;
|
---|
| 2826 | }
|
---|
| 2827 | y += sz.height();
|
---|
| 2828 | }
|
---|
| 2829 | if((d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp) &&
|
---|
| 2830 | !(old_scrollable & QPopupMenuPrivate::Scroll::ScrollUp))
|
---|
| 2831 | d->scroll.topScrollableIndex++;
|
---|
| 2832 | }
|
---|
| 2833 |
|
---|
| 2834 | #endif // QT_NO_POPUPMENU
|
---|
| 2835 |
|
---|