source: vendor/trolltech/current/src/widgets/qpopupmenu.cpp

Last change on this file was 2, checked in by dmik, 20 years ago

Imported xplatform parts of the official release 3.3.1 from Trolltech

  • Property svn:keywords set to Id
File size: 76.8 KB
Line 
1/****************************************************************************
2** $Id: qpopupmenu.cpp 2 2005-11-16 15:49:26Z 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
65static const int motifArrowHMargin = 6; // arrow horizontal margin
66static 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
90static QPopupMenu * syncMenu = 0;
91static int syncMenuId = 0;
92
93// Used to detect motion prior to mouse-release
94static int motion;
95
96// used to provide ONE single-shot timer
97static QTimer * singleSingleShot = 0;
98
99static bool supressAboutToShow = FALSE;
100
101static void cleanup()
102{
103 delete singleSingleShot;
104 singleSingleShot = 0;
105}
106
107static 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
119static bool preventAnimation = FALSE;
120
121#ifndef QT_NO_WHATSTHIS
122extern void qWhatsThisBDH();
123static 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
236class QMenuDataData {
237 // attention: also defined in qmenudata.cpp
238public:
239 QMenuDataData();
240 QGuardedPtr<QWidget> aWidget;
241 int aInt;
242};
243
244class QPopupMenuPrivate {
245public:
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
258static 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
268QPopupMenu::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
304QPopupMenu::~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*/
329void QPopupMenu::updateItem( int id ) // update popup menu item
330{
331 updateRow( indexOf(id) );
332}
333
334
335void 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
355bool QPopupMenu::isCheckable() const
356{
357 return checkable;
358}
359
360void 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
375void 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
407void 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
417void 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
430void QPopupMenu::performDelayedChanges()
431{
432 if( pendingDelayedContentsChanges )
433 performDelayedContentsChanged();
434 if( pendingDelayedStateChanges )
435 performDelayedStateChanged();
436}
437
438void 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
448void 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
457void 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
476void 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
686void QPopupMenu::subActivated( int id )
687{
688 emit activatedRedirect( id );
689}
690
691void QPopupMenu::subHighlighted( int id )
692{
693 emit highlightedRedirect( id );
694}
695
696static bool fromAccel = FALSE;
697
698#ifndef QT_NO_ACCEL
699void 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
712void QPopupMenu::accelDestroyed() // accel about to be deleted
713{
714 autoaccel = 0; // don't delete it twice!
715}
716#endif //QT_NO_ACCEL
717
718void QPopupMenu::popupDestroyed( QObject *o )
719{
720 removePopup( (QPopupMenu*)o );
721}
722
723void 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
745void 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
756void 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
780void 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
809void 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
836bool 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*/
854bool 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
871void 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
892int 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
953QRect 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
1014QSize 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
1217void 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
1305void 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*/
1323void 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*/
1336void 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
1362void 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*/
1391int 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*/
1401int 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*/
1435void 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*/
1462void 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
1511 if ( ncols > 1 && y + itemh > contentsRect().bottom() ) {
1512 if ( y < contentsRect().bottom() ) {
1513 QRect rect(x, y, itemw, contentsRect().bottom() - y);
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 }
1530 if ( y < contentsRect().bottom() ) {
1531 QRect rect(x, y, itemw, contentsRect().bottom() - y);
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
1568void QPopupMenu::paintEvent( QPaintEvent *e )
1569{
1570 QFrame::paintEvent( e );
1571}
1572
1573/*!
1574 \reimp
1575*/
1576
1577void QPopupMenu::closeEvent( QCloseEvent * e) {
1578 e->accept();
1579 byeMenuBar();
1580}
1581
1582
1583/*!
1584 \reimp
1585*/
1586
1587void 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
1628void 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
1679 } else if ( popup ) {
1680 popup->setFirstItemActive();
1681 } else { // normal menu item
1682 byeMenuBar(); // deactivate menu bar
1683 if ( mi->isEnabledAndVisible() ) {
1684 actItem = -1;
1685 updateItem( mi->id() );
1686 active_popup_menu = this;
1687 QGuardedPtr<QSignal> signal = mi->signal();
1688 actSig( mi->id(), b );
1689 if ( signal && !b )
1690 signal->activate();
1691 active_popup_menu = 0;
1692 }
1693 }
1694 }
1695}
1696
1697/*!
1698 \reimp
1699*/
1700
1701void QPopupMenu::mouseMoveEvent( QMouseEvent *e )
1702{
1703 motion++;
1704
1705 if ( parentMenu && parentMenu->isPopupMenu ) {
1706 QPopupMenu* p = (QPopupMenu*)parentMenu;
1707 int myIndex;
1708
1709 p->findPopup( this, &myIndex );
1710 QPoint pPos = p->mapFromParent( e->globalPos() );
1711 if ( p->actItem != myIndex && !p->rect().contains( pPos ) )
1712 p->setActiveItem( myIndex );
1713
1714 if ( style().styleHint(QStyle::SH_PopupMenu_SloppySubMenus, this )) {
1715 p->d->mouseMoveBuffer = QRegion();
1716#ifdef DEBUG_SLOPPY_SUBMENU
1717 p->repaint();
1718#endif
1719 }
1720 }
1721
1722 if ( (e->state() & Qt::MouseButtonMask) == 0 &&
1723 !hasMouseTracking() )
1724 return;
1725
1726 int item = itemAtPos( e->pos() );
1727 if ( item == -1 ) { // no valid item
1728 int lastActItem = actItem;
1729 actItem = -1;
1730 if ( lastActItem >= 0 )
1731 updateRow( lastActItem );
1732 if(d->scroll.scrollable &&
1733 e->pos().x() >= rect().x() && e->pos().x() <= rect().width()) {
1734 if(!d->scroll.scrolltimer) {
1735 d->scroll.scrolltimer = new QTimer(this, "popup scroll timer");
1736 QObject::connect( d->scroll.scrolltimer, SIGNAL(timeout()),
1737 this, SLOT(subScrollTimer()) );
1738 }
1739 if(!d->scroll.scrolltimer->isActive())
1740 d->scroll.scrolltimer->start(40);
1741 } else if ( lastActItem > 0 ||
1742 ( !rect().contains( e->pos() ) && !tryMenuBar( e ) ) ) {
1743 popupSubMenuLater(style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay,
1744 this), this);
1745 }
1746 } else { // mouse on valid item
1747 // but did not register mouse press
1748 if ( (e->state() & Qt::MouseButtonMask) && !mouseBtDn )
1749 mouseBtDn = TRUE; // so mouseReleaseEvent will pop down
1750
1751 register QMenuItem *mi = mitems->at( item );
1752
1753 if ( mi->widget() ) {
1754 QWidget* widgetAt = QApplication::widgetAt( e->globalPos(), TRUE );
1755 if ( widgetAt && widgetAt != this ) {
1756 QMouseEvent me( e->type(), widgetAt->mapFromGlobal( e->globalPos() ),
1757 e->globalPos(), e->button(), e->state() );
1758 QApplication::sendEvent( widgetAt, &me );
1759 }
1760 }
1761
1762 if ( actItem == item )
1763 return;
1764
1765 if ( style().styleHint(QStyle::SH_PopupMenu_SloppySubMenus, this) &&
1766 d->mouseMoveBuffer.contains( e->pos() ) ) {
1767 actItem = item;
1768 popupSubMenuLater( style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this) * 6,
1769 this );
1770 return;
1771 }
1772
1773 if ( mi->popup() || ( popupActive >= 0 && popupActive != item ))
1774 popupSubMenuLater( style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this),
1775 this );
1776 else if ( singleSingleShot )
1777 singleSingleShot->stop();
1778
1779 if ( item != actItem )
1780 setActiveItem( item );
1781 }
1782}
1783
1784
1785/*!
1786 \reimp
1787*/
1788
1789void QPopupMenu::keyPressEvent( QKeyEvent *e )
1790{
1791 /*
1792 I get nothing but complaints about this. -Brad
1793
1794 - if (mouseBtDn && actItem >= 0) {
1795 - if (e->key() == Key_Shift ||
1796 - e->key() == Key_Control ||
1797 - e->key() == Key_Alt)
1798 - return;
1799 -
1800 - QMenuItem *mi = mitems->at(actItem);
1801 - int modifier = (((e->state() & ShiftButton) ? SHIFT : 0) |
1802 - ((e->state() & ControlButton) ? CTRL : 0) |
1803 - ((e->state() & AltButton) ? ALT : 0));
1804 -
1805 - #ifndef QT_NO_ACCEL
1806 - if (mi)
1807 - setAccel(modifier + e->key(), mi->id());
1808 - #endif
1809 - return;
1810 - }
1811 */
1812
1813 QMenuItem *mi = 0;
1814 QPopupMenu *popup;
1815 int dy = 0;
1816 bool ok_key = TRUE;
1817
1818 int key = e->key();
1819 if ( QApplication::reverseLayout() ) {
1820 // in reverse mode opening and closing keys for submenues are reversed
1821 if ( key == Key_Left )
1822 key = Key_Right;
1823 else if ( key == Key_Right )
1824 key = Key_Left;
1825 }
1826
1827 switch ( key ) {
1828 case Key_Tab:
1829 // ignore tab, otherwise it will be passed to the menubar
1830 break;
1831
1832 case Key_Up:
1833 dy = -1;
1834 break;
1835
1836 case Key_Down:
1837 dy = 1;
1838 break;
1839
1840 case Key_Alt:
1841 if ( style().styleHint(QStyle::SH_MenuBar_AltKeyNavigation, this) )
1842 byeMenuBar();
1843 break;
1844
1845 case Key_Escape:
1846 if ( tornOff ) {
1847 close();
1848 return;
1849 }
1850 // just hide one
1851 {
1852 QMenuData* p = parentMenu;
1853 hide();
1854#ifndef QT_NO_MENUBAR
1855 if ( p && p->isMenuBar )
1856 ((QMenuBar*) p)->goodbye( TRUE );
1857#endif
1858 }
1859 break;
1860
1861 case Key_Left:
1862 if ( ncols > 1 && actItem >= 0 ) {
1863 QRect r( itemGeometry( actItem ) );
1864 int newActItem = itemAtPos( QPoint( r.left() - 1, r.center().y() ) );
1865 if ( newActItem >= 0 ) {
1866 setActiveItem( newActItem );
1867 break;
1868 }
1869 }
1870 if ( parentMenu && parentMenu->isPopupMenu ) {
1871 ((QPopupMenu *)parentMenu)->hidePopups();
1872 if ( singleSingleShot )
1873 singleSingleShot->stop();
1874 break;
1875 }
1876
1877 ok_key = FALSE;
1878 break;
1879
1880 case Key_Right:
1881 if ( actItem >= 0 && ( mi=mitems->at(actItem) )->isEnabledAndVisible() && (popup=mi->popup()) ) {
1882 hidePopups();
1883 if ( singleSingleShot )
1884 singleSingleShot->stop();
1885 // ### The next two lines were switched to fix the problem with the first item of the
1886 // submenu not being highlighted...any reason why they should have been the other way??
1887 subMenuTimer();
1888 popup->setFirstItemActive();
1889 break;
1890 } else if ( actItem == -1 && ( parentMenu && !parentMenu->isMenuBar )) {
1891 dy = 1;
1892 break;
1893 }
1894 if ( ncols > 1 && actItem >= 0 ) {
1895 QRect r( itemGeometry( actItem ) );
1896 int newActItem = itemAtPos( QPoint( r.right() + 1, r.center().y() ) );
1897 if ( newActItem >= 0 ) {
1898 setActiveItem( newActItem );
1899 break;
1900 }
1901 }
1902 ok_key = FALSE;
1903 break;
1904
1905 case Key_Space:
1906 if (! style().styleHint(QStyle::SH_PopupMenu_SpaceActivatesItem, this))
1907 break;
1908 // for motif, fall through
1909
1910 case Key_Return:
1911 case Key_Enter:
1912 {
1913 if ( actItem < 0 )
1914 break;
1915#ifndef QT_NO_WHATSTHIS
1916 bool b = QWhatsThis::inWhatsThisMode();
1917#else
1918 const bool b = FALSE;
1919#endif
1920 mi = mitems->at( actItem );
1921 if ( !mi->isEnabled() && !b )
1922 break;
1923 popup = mi->popup();
1924 if ( popup ) {
1925 hidePopups();
1926 popupSubMenuLater( 20, this );
1927 popup->setFirstItemActive();
1928 } else {
1929 actItem = -1;
1930 updateItem( mi->id() );
1931 byeMenuBar();
1932 if ( mi->isEnabledAndVisible() || b ) {
1933 active_popup_menu = this;
1934 QGuardedPtr<QSignal> signal = mi->signal();
1935 actSig( mi->id(), b );
1936 if ( signal && !b )
1937 signal->activate();
1938 active_popup_menu = 0;
1939 }
1940 }
1941 }
1942 break;
1943#ifndef QT_NO_WHATSTHIS
1944 case Key_F1:
1945 if ( actItem < 0 || e->state() != ShiftButton)
1946 break;
1947 mi = mitems->at( actItem );
1948 if ( !mi->whatsThis().isNull() ){
1949 if ( !QWhatsThis::inWhatsThisMode() )
1950 QWhatsThis::enterWhatsThisMode();
1951 QRect r( itemGeometry( actItem) );
1952 QWhatsThis::leaveWhatsThisMode( mi->whatsThis(), mapToGlobal( r.bottomLeft()) );
1953 }
1954 //fall-through!
1955#endif
1956 default:
1957 ok_key = FALSE;
1958
1959 }
1960 if ( !ok_key &&
1961 ( !e->state() || e->state() == AltButton || e->state() == ShiftButton ) &&
1962 e->text().length()==1 ) {
1963 QChar c = e->text()[0].upper();
1964
1965 QMenuItemListIt it(*mitems);
1966 QMenuItem* first = 0;
1967 QMenuItem* currentSelected = 0;
1968 QMenuItem* firstAfterCurrent = 0;
1969
1970 register QMenuItem *m;
1971 mi = 0;
1972 int indx = 0;
1973 int clashCount = 0;
1974 while ( (m=it.current()) ) {
1975 ++it;
1976 QString s = m->text();
1977 if ( !s.isEmpty() ) {
1978 int i = s.find( '&' );
1979 while ( i >= 0 && i < (int)s.length() - 1 ) {
1980 if ( s[i+1].upper() == c ) {
1981 ok_key = TRUE;
1982 clashCount++;
1983 if ( !first )
1984 first = m;
1985 if ( indx == actItem )
1986 currentSelected = m;
1987 else if ( !firstAfterCurrent && currentSelected )
1988 firstAfterCurrent = m;
1989 break;
1990 } else if ( s[i+1] == '&' ) {
1991 i = s.find( '&', i+2 );
1992 } else {
1993 break;
1994 }
1995 }
1996 }
1997 if ( mi )
1998 break;
1999 indx++;
2000 }
2001
2002 if ( 1 == clashCount ) { // No clashes, continue with selection
2003 mi = first;
2004 popup = mi->popup();
2005 if ( popup ) {
2006 setActiveItem( indexOf(mi->id()) );
2007 hidePopups();
2008 popupSubMenuLater( 20, this );
2009 popup->setFirstItemActive();
2010 } else {
2011 byeMenuBar();
2012#ifndef QT_NO_WHATSTHIS
2013 bool b = QWhatsThis::inWhatsThisMode();
2014#else
2015 const bool b = FALSE;
2016#endif
2017 if ( mi->isEnabledAndVisible() || b ) {
2018 active_popup_menu = this;
2019 QGuardedPtr<QSignal> signal = mi->signal();
2020 actSig( mi->id(), b );
2021 if ( signal && !b )
2022 signal->activate();
2023 active_popup_menu = 0;
2024 }
2025 }
2026 } else if ( clashCount > 1 ) { // Clashes, highlight next...
2027 // If there's clashes and no one is selected, use first one
2028 // or if there is no clashes _after_ current, use first one
2029 if ( !currentSelected || (currentSelected && !firstAfterCurrent))
2030 dy = indexOf( first->id() ) - actItem;
2031 else
2032 dy = indexOf( firstAfterCurrent->id() ) - actItem;
2033 }
2034 }
2035#ifndef QT_NO_MENUBAR
2036 if ( !ok_key ) { // send to menu bar
2037 register QMenuData *top = this; // find top level
2038 while ( top->parentMenu )
2039 top = top->parentMenu;
2040 if ( top->isMenuBar ) {
2041 int beforeId = top->actItem;
2042 ((QMenuBar*)top)->tryKeyEvent( this, e );
2043 if ( beforeId != top->actItem )
2044 ok_key = TRUE;
2045 }
2046 }
2047#endif
2048 if ( actItem < 0 ) {
2049 if ( dy > 0 ) {
2050 setFirstItemActive();
2051 } else if ( dy < 0 ) {
2052 QMenuItemListIt it(*mitems);
2053 it.toLast();
2054 register QMenuItem *mi;
2055 int ai = count() - 1;
2056 while ( (mi=it.current()) ) {
2057 --it;
2058 if ( !mi->isSeparator() && mi->id() != QMenuData::d->aInt ) {
2059 setActiveItem( ai );
2060 return;
2061 }
2062 ai--;
2063 }
2064 actItem = -1;
2065 }
2066 return;
2067 }
2068
2069 if ( dy ) { // highlight next/prev
2070 register int i = actItem;
2071 int c = mitems->count();
2072 for(int n = c; n; n--) {
2073 i = i + dy;
2074 if(d->scroll.scrollable) {
2075 if(d->scroll.scrolltimer)
2076 d->scroll.scrolltimer->stop();
2077 if(i < 0)
2078 i = 0;
2079 else if(i >= c)
2080 i = c - 1;
2081 } else {
2082 if ( i == c )
2083 i = 0;
2084 else if ( i < 0 )
2085 i = c - 1;
2086 }
2087 mi = mitems->at( i );
2088 if ( !mi || !mi->isVisible() )
2089 continue;
2090
2091 if ( !mi->isSeparator() &&
2092 ( style().styleHint(QStyle::SH_PopupMenu_AllowActiveAndDisabled, this)
2093 || mi->isEnabledAndVisible() ) )
2094 break;
2095 }
2096 if ( i != actItem )
2097 setActiveItem( i );
2098 if(d->scroll.scrollable) { //need to scroll to make it visible?
2099 QRect r = itemGeometry(actItem);
2100 if(r.isNull() || r.height() < itemHeight(mitems->at(actItem))) {
2101 bool refresh = FALSE;
2102 if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp && dy == -1) { //up
2103 if(d->scroll.topScrollableIndex >= 0) {
2104 d->scroll.topScrollableIndex--;
2105 refresh = TRUE;
2106 }
2107 } else if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown) { //down
2108 QMenuItemListIt it(*mitems);
2109 int sh = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
2110 for(int i = 0, y = ((d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp) ? sh : 0); it.current(); i++, ++it) {
2111 if(i >= d->scroll.topScrollableIndex) {
2112 int itemh = itemHeight(it.current());
2113 QSize sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
2114 QSize(0, itemh),
2115 QStyleOption(it.current(),maxPMWidth,0));
2116 y += sz.height();
2117 if(y > (contentsRect().height()-sh)) {
2118 if(sz.height() > sh || !it.atLast())
2119 d->scroll.topScrollableIndex++;
2120 refresh = TRUE;
2121 break;
2122 }
2123 }
2124 }
2125 }
2126 if(refresh) {
2127 updateScrollerState();
2128 update();
2129 }
2130 }
2131 }
2132 }
2133
2134#ifdef Q_OS_WIN32
2135 if ( !ok_key &&
2136 !( e->key() == Key_Control || e->key() == Key_Shift || e->key() == Key_Meta ) )
2137 qApp->beep();
2138#endif // Q_OS_WIN32
2139}
2140
2141
2142/*!
2143 \reimp
2144*/
2145
2146void QPopupMenu::timerEvent( QTimerEvent *e )
2147{
2148 QFrame::timerEvent( e );
2149}
2150
2151/*!
2152 \reimp
2153*/
2154void QPopupMenu::leaveEvent( QEvent * )
2155{
2156 if ( testWFlags( WStyle_Tool ) && style().styleHint(QStyle::SH_PopupMenu_MouseTracking, this) ) {
2157 int lastActItem = actItem;
2158 actItem = -1;
2159 if ( lastActItem >= 0 )
2160 updateRow( lastActItem );
2161 }
2162}
2163
2164/*!
2165 \reimp
2166*/
2167void QPopupMenu::styleChange( QStyle& old )
2168{
2169 QFrame::styleChange( old );
2170 setMouseTracking(style().styleHint(QStyle::SH_PopupMenu_MouseTracking, this));
2171 style().polishPopupMenu( this );
2172 updateSize(TRUE);
2173}
2174
2175/*!\reimp
2176 */
2177void QPopupMenu::enabledChange( bool )
2178{
2179 if ( QMenuData::d->aWidget ) // torn-off menu
2180 QMenuData::d->aWidget->setEnabled( isEnabled() );
2181}
2182
2183
2184/*!
2185 If a popup menu does not fit on the screen it lays itself out so
2186 that it does fit. It is style dependent what layout means (for
2187 example, on Windows it will use multiple columns).
2188
2189 This functions returns the number of columns necessary.
2190
2191 \sa sizeHint()
2192*/
2193int QPopupMenu::columns() const
2194{
2195 return ncols;
2196}
2197
2198/* This private slot handles the scrolling popupmenu */
2199void QPopupMenu::subScrollTimer() {
2200 QPoint pos = QCursor::pos();
2201 if(!d->scroll.scrollable || !isVisible()) {
2202 if(d->scroll.scrolltimer)
2203 d->scroll.scrolltimer->stop();
2204 return;
2205 } else if(pos.x() > x() + width() || pos.x() < x()) {
2206 return;
2207 }
2208 int sh = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
2209 if(!d->scroll.lastScroll.isValid()) {
2210 d->scroll.lastScroll = QTime::currentTime();
2211 } else {
2212 int factor=0;
2213 if(pos.y() < y())
2214 factor = y() - pos.y();
2215 else if(pos.y() > y() + height())
2216 factor = pos.y() - (y() + height());
2217 int msecs = 250 - ((factor / 10) * 40);
2218 if(d->scroll.lastScroll.msecsTo(QTime::currentTime()) < QMAX(0, msecs))
2219 return;
2220 d->scroll.lastScroll = QTime::currentTime();
2221 }
2222 if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp && pos.y() <= y() + sh) { //up
2223 if(d->scroll.topScrollableIndex > 0) {
2224 d->scroll.topScrollableIndex--;
2225 updateScrollerState();
2226 update(contentsRect());
2227 }
2228 } else if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown &&
2229 pos.y() >= (y() + contentsRect().height()) - sh) { //down
2230 QMenuItemListIt it(*mitems);
2231 for(int i = 0, y = contentsRect().y() + sh; it.current(); i++, ++it) {
2232 if(i >= d->scroll.topScrollableIndex) {
2233 int itemh = itemHeight(it.current());
2234 QSize sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this, QSize(0, itemh),
2235 QStyleOption(it.current(),maxPMWidth,0));
2236 y += sz.height();
2237 if(y > contentsRect().height() - sh) {
2238 d->scroll.topScrollableIndex++;
2239 updateScrollerState();
2240 update(contentsRect());
2241 break;
2242 }
2243 }
2244 }
2245 }
2246}
2247
2248/* This private slot handles the delayed submenu effects */
2249
2250void QPopupMenu::subMenuTimer() {
2251
2252 if ( !isVisible() || (actItem < 0 && popupActive < 0) || actItem == popupActive )
2253 return;
2254
2255 if ( popupActive >= 0 ) {
2256 hidePopups();
2257 popupActive = -1;
2258 }
2259
2260 // hidePopups() may change actItem etc.
2261 if ( !isVisible() || actItem < 0 || actItem == popupActive )
2262 return;
2263
2264 QMenuItem *mi = mitems->at(actItem);
2265 if ( !mi || !mi->isEnabledAndVisible() )
2266 return;
2267
2268 QPopupMenu *popup = mi->popup();
2269 if ( !popup || !popup->isEnabled() )
2270 return;
2271
2272 //avoid circularity
2273 if ( popup->isVisible() )
2274 return;
2275
2276 Q_ASSERT( popup->parentMenu == 0 );
2277 popup->parentMenu = this; // set parent menu
2278
2279 emit popup->aboutToShow();
2280 supressAboutToShow = TRUE;
2281
2282
2283 QRect r( itemGeometry( actItem ) );
2284 QPoint p;
2285 QSize ps = popup->sizeHint();
2286 if( QApplication::reverseLayout() ) {
2287 p = QPoint( r.left() + motifArrowHMargin - ps.width(), r.top() + motifArrowVMargin );
2288 p = mapToGlobal( p );
2289
2290 bool right = FALSE;
2291 if ( ( parentMenu && parentMenu->isPopupMenu &&
2292 ((QPopupMenu*)parentMenu)->geometry().x() < geometry().x() ) ||
2293 p.x() < 0 )
2294 right = TRUE;
2295 if ( right && (ps.width() > QApplication::desktop()->width() - mapToGlobal( r.topRight() ).x() ) )
2296 right = FALSE;
2297 if ( right )
2298 p.setX( mapToGlobal( r.topRight() ).x() );
2299 } else {
2300 p = QPoint( r.right() - motifArrowHMargin, r.top() + motifArrowVMargin );
2301 p = mapToGlobal( p );
2302
2303 bool left = FALSE;
2304 if ( ( parentMenu && parentMenu->isPopupMenu &&
2305 ((QPopupMenu*)parentMenu)->geometry().x() > geometry().x() ) ||
2306 p.x() + ps.width() > QApplication::desktop()->width() )
2307 left = TRUE;
2308 if ( left && (ps.width() > mapToGlobal( r.topLeft() ).x() ) )
2309 left = FALSE;
2310 if ( left )
2311 p.setX( mapToGlobal( r.topLeft() ).x() - ps.width() );
2312 }
2313 QRect pr = popup->itemGeometry(popup->count() - 1);
2314 if (p.y() + ps.height() > QApplication::desktop()->height() &&
2315 p.y() - ps.height() + (QCOORD) pr.height() >= 0)
2316 p.setY( p.y() - ps.height() + (QCOORD) pr.height());
2317
2318 if ( style().styleHint(QStyle::SH_PopupMenu_SloppySubMenus, this )) {
2319 QPoint cur = QCursor::pos();
2320 if ( r.contains( mapFromGlobal( cur ) ) ) {
2321 QPoint pts[4];
2322 pts[0] = QPoint( cur.x(), cur.y() - 2 );
2323 pts[3] = QPoint( cur.x(), cur.y() + 2 );
2324 if ( p.x() >= cur.x() ) {
2325 pts[1] = QPoint( geometry().right(), p.y() );
2326 pts[2] = QPoint( geometry().right(), p.y() + ps.height() );
2327 } else {
2328 pts[1] = QPoint( p.x() + ps.width(), p.y() );
2329 pts[2] = QPoint( p.x() + ps.width(), p.y() + ps.height() );
2330 }
2331 QPointArray points( 4 );
2332 for( int i = 0; i < 4; i++ )
2333 points.setPoint( i, mapFromGlobal( pts[i] ) );
2334 d->mouseMoveBuffer = QRegion( points );
2335 repaint();
2336 }
2337 }
2338
2339 popupActive = actItem;
2340 popup->popup( p );
2341}
2342
2343void QPopupMenu::allowAnimation()
2344{
2345 preventAnimation = FALSE;
2346}
2347
2348void QPopupMenu::updateRow( int row )
2349{
2350 if ( !isVisible() )
2351 return;
2352
2353 if ( badSize ) {
2354 updateSize();
2355 update();
2356 return;
2357 }
2358 updateSize();
2359 QRect r = itemGeometry( row );
2360 if ( !r.isNull() ) // can happen via the scroller
2361 repaint( r );
2362}
2363
2364
2365/*!
2366 \overload
2367
2368 Executes this popup synchronously.
2369
2370 Opens the popup menu so that the item number \a indexAtPoint will
2371 be at the specified \e global position \a pos. To translate a
2372 widget's local coordinates into global coordinates, use
2373 QWidget::mapToGlobal().
2374
2375 The return code is the id of the selected item in either the popup
2376 menu or one of its submenus, or -1 if no item is selected
2377 (normally because the user pressed Esc).
2378
2379 Note that all signals are emitted as usual. If you connect a menu
2380 item to a slot and call the menu's exec(), you get the result both
2381 via the signal-slot connection and in the return value of exec().
2382
2383 Common usage is to position the popup at the current mouse
2384 position:
2385 \code
2386 exec( QCursor::pos() );
2387 \endcode
2388 or aligned to a widget:
2389 \code
2390 exec( somewidget.mapToGlobal(QPoint(0, 0)) );
2391 \endcode
2392
2393 When positioning a popup with exec() or popup(), bear in mind that
2394 you cannot rely on the popup menu's current size(). For
2395 performance reasons, the popup adapts its size only when
2396 necessary. So in many cases, the size before and after the show is
2397 different. Instead, use sizeHint(). It calculates the proper size
2398 depending on the menu's current contents.
2399
2400 \sa popup(), sizeHint()
2401*/
2402
2403int QPopupMenu::exec( const QPoint & pos, int indexAtPoint )
2404{
2405 snapToMouse = TRUE;
2406 if ( !qApp )
2407 return -1;
2408
2409 QPopupMenu* priorSyncMenu = syncMenu;
2410
2411 syncMenu = this;
2412 syncMenuId = -1;
2413
2414 QGuardedPtr<QPopupMenu> that = this;
2415 connectModal( that, TRUE );
2416 popup( pos, indexAtPoint );
2417 qApp->enter_loop();
2418 connectModal( that, FALSE );
2419
2420 syncMenu = priorSyncMenu;
2421 return syncMenuId;
2422}
2423
2424
2425
2426/*
2427 Connect the popup and all its submenus to modalActivation() if
2428 \a doConnect is true, otherwise disconnect.
2429 */
2430void QPopupMenu::connectModal( QPopupMenu* receiver, bool doConnect )
2431{
2432 if ( !receiver )
2433 return;
2434
2435 connectModalRecursionSafety = doConnect;
2436
2437 if ( doConnect )
2438 connect( this, SIGNAL(activated(int)),
2439 receiver, SLOT(modalActivation(int)) );
2440 else
2441 disconnect( this, SIGNAL(activated(int)),
2442 receiver, SLOT(modalActivation(int)) );
2443
2444 QMenuItemListIt it(*mitems);
2445 register QMenuItem *mi;
2446 while ( (mi=it.current()) ) {
2447 ++it;
2448 if ( mi->popup() && mi->popup() != receiver
2449 && (bool)(mi->popup()->connectModalRecursionSafety) != doConnect )
2450 mi->popup()->connectModal( receiver, doConnect ); //avoid circular
2451 }
2452}
2453
2454
2455/*!
2456 Executes this popup synchronously.
2457
2458 This is equivalent to \c{exec(mapToGlobal(QPoint(0,0)))}. In most
2459 situations you'll want to specify the position yourself, for
2460 example at the current mouse position:
2461 \code
2462 exec(QCursor::pos());
2463 \endcode
2464 or aligned to a widget:
2465 \code
2466 exec(somewidget.mapToGlobal(QPoint(0,0)));
2467 \endcode
2468*/
2469
2470int QPopupMenu::exec()
2471{
2472 return exec(mapToGlobal(QPoint(0,0)));
2473}
2474
2475
2476/* Internal slot used for exec(). */
2477
2478void QPopupMenu::modalActivation( int id )
2479{
2480 syncMenuId = id;
2481}
2482
2483
2484/*!
2485 Sets the currently active item to index \a i and repaints as necessary.
2486*/
2487
2488void QPopupMenu::setActiveItem( int i )
2489{
2490 int lastActItem = actItem;
2491 actItem = i;
2492 if ( lastActItem >= 0 )
2493 updateRow( lastActItem );
2494 if ( i >= 0 && i != lastActItem )
2495 updateRow( i );
2496 QMenuItem *mi = mitems->at( actItem );
2497 if ( !mi )
2498 return;
2499
2500 if ( mi->widget() && mi->widget()->isFocusEnabled() ) {
2501 mi->widget()->setFocus();
2502 } else {
2503 setFocus();
2504 QRect mfrect = itemGeometry( actItem );
2505 setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE );
2506 }
2507 if ( mi->id() != -1 )
2508 hilitSig( mi->id() );
2509#ifndef QT_NO_WHATSTHIS
2510 if (whatsThisItem && whatsThisItem != mi) {
2511 qWhatsThisBDH();
2512 }
2513 whatsThisItem = mi;
2514#endif
2515}
2516
2517
2518/*!
2519 \reimp
2520*/
2521QSize QPopupMenu::sizeHint() const
2522{
2523 constPolish();
2524 if(style().styleHint(QStyle::SH_PopupMenu_Scrollable, this))
2525 return minimumSize(); //can be any size..
2526
2527 QPopupMenu* that = (QPopupMenu*) this;
2528 //We do not need a resize here, just the sizeHint..
2529 return that->updateSize(FALSE, FALSE).expandedTo( QApplication::globalStrut() );
2530}
2531
2532
2533/*!
2534 \overload
2535
2536 Returns the id of the item at \a pos, or -1 if there is no item
2537 there or if it is a separator.
2538*/
2539int QPopupMenu::idAt( const QPoint& pos ) const
2540{
2541 return idAt( itemAtPos( pos ) );
2542}
2543
2544
2545/*!
2546 \fn int QPopupMenu::idAt( int index ) const
2547
2548 Returns the identifier of the menu item at position \a index in
2549 the internal list, or -1 if \a index is out of range.
2550
2551 \sa QMenuData::setId(), QMenuData::indexOf()
2552*/
2553
2554
2555/*!
2556 \reimp
2557 */
2558bool QPopupMenu::customWhatsThis() const
2559{
2560 return TRUE;
2561}
2562
2563
2564/*!
2565 \reimp
2566 */
2567bool QPopupMenu::focusNextPrevChild( bool next )
2568{
2569 register QMenuItem *mi;
2570 int dy = next? 1 : -1;
2571 if ( dy && actItem < 0 ) {
2572 setFirstItemActive();
2573 } else if ( dy ) { // highlight next/prev
2574 register int i = actItem;
2575 int c = mitems->count();
2576 int n = c;
2577 while ( n-- ) {
2578 i = i + dy;
2579 if ( i == c )
2580 i = 0;
2581 else if ( i < 0 )
2582 i = c - 1;
2583 mi = mitems->at( i );
2584 if ( mi && !mi->isSeparator() &&
2585 ( style().styleHint(QStyle::SH_PopupMenu_AllowActiveAndDisabled, this)
2586 || mi->isEnabledAndVisible() ) )
2587 break;
2588 }
2589 if ( i != actItem )
2590 setActiveItem( i );
2591 }
2592 return TRUE;
2593}
2594
2595
2596/*!
2597 \reimp
2598 */
2599void QPopupMenu::focusInEvent( QFocusEvent * )
2600{
2601}
2602
2603/*!
2604 \reimp
2605 */
2606void QPopupMenu::focusOutEvent( QFocusEvent * )
2607{
2608}
2609
2610
2611class QTearOffMenuItem : public QCustomMenuItem
2612{
2613public:
2614 QTearOffMenuItem()
2615 {
2616 }
2617 ~QTearOffMenuItem()
2618 {
2619 }
2620 void paint( QPainter* p, const QColorGroup& cg, bool /* act*/,
2621 bool /*enabled*/, int x, int y, int w, int h )
2622 {
2623 p->setPen( QPen( cg.dark(), 1, DashLine ) );
2624 p->drawLine( x+2, y+h/2-1, x+w-4, y+h/2-1 );
2625 p->setPen( QPen( cg.light(), 1, DashLine ) );
2626 p->drawLine( x+2, y+h/2, x+w-4, y+h/2 );
2627 }
2628 bool fullSpan() const
2629 {
2630 return TRUE;
2631 }
2632
2633 QSize sizeHint()
2634 {
2635 return QSize( 20, 6 );
2636 }
2637};
2638
2639
2640
2641/*!
2642 Inserts a tear-off handle into the menu. A tear-off handle is a
2643 special menu item that creates a copy of the menu when the menu is
2644 selected. This "torn-off" copy lives in a separate window. It
2645 contains the same menu items as the original menu, with the
2646 exception of the tear-off handle.
2647
2648 The handle item is assigned the identifier \a id or an
2649 automatically generated identifier if \a id is < 0. The generated
2650 identifiers (negative integers) are guaranteed to be unique within
2651 the entire application.
2652
2653 The \a index specifies the position in the menu. The tear-off
2654 handle is appended at the end of the list if \a index is negative.
2655*/
2656int QPopupMenu::insertTearOffHandle( int id, int index )
2657{
2658 int myid = insertItem( new QTearOffMenuItem, id, index );
2659 connectItem( myid, this, SLOT( toggleTearOff() ) );
2660 QMenuData::d->aInt = myid;
2661 return myid;
2662}
2663
2664
2665/*!\internal
2666
2667 implements tear-off menus
2668 */
2669void QPopupMenu::toggleTearOff()
2670{
2671 if ( active_popup_menu && active_popup_menu->tornOff ) {
2672 active_popup_menu->close();
2673 } else if (QMenuData::d->aWidget ) {
2674 delete (QWidget*) QMenuData::d->aWidget; // delete the old one
2675 } else {
2676 // create a tear off menu
2677 QPopupMenu* p = new QPopupMenu( parentWidget(), "tear off menu" );
2678 connect( p, SIGNAL( activated(int) ), this, SIGNAL( activated(int) ) );
2679#ifndef QT_NO_WIDGET_TOPEXTRA
2680 p->setCaption( caption() );
2681#endif
2682 p->setCheckable( isCheckable() );
2683 p->reparent( parentWidget(), WType_TopLevel | WStyle_Tool |
2684 WNoAutoErase | WDestructiveClose,
2685 geometry().topLeft(), FALSE );
2686 p->mitems->setAutoDelete( FALSE );
2687 p->tornOff = TRUE;
2688 for ( QMenuItemListIt it( *mitems ); it.current(); ++it ) {
2689 if ( it.current()->id() != QMenuData::d->aInt && !it.current()->widget() )
2690 p->mitems->append( it.current() );
2691 }
2692 p->show();
2693 QMenuData::d->aWidget = p;
2694 }
2695}
2696
2697/*!
2698 \reimp
2699 */
2700void QPopupMenu::activateItemAt( int index )
2701{
2702 if ( index >= 0 && index < (int) mitems->count() ) {
2703 QMenuItem *mi = mitems->at( index );
2704 if ( index != actItem ) // new item activated
2705 setActiveItem( index );
2706 QPopupMenu *popup = mi->popup();
2707 if ( popup ) {
2708 if ( popup->isVisible() ) { // sub menu already open
2709 int pactItem = popup->actItem;
2710 popup->actItem = -1;
2711 popup->hidePopups();
2712 popup->updateRow( pactItem );
2713 } else { // open sub menu
2714 hidePopups();
2715 actItem = index;
2716 subMenuTimer();
2717 popup->setFirstItemActive();
2718 }
2719 } else {
2720 byeMenuBar(); // deactivate menu bar
2721
2722#ifndef QT_NO_WHATSTHIS
2723 bool b = QWhatsThis::inWhatsThisMode();
2724#else
2725 const bool b = FALSE;
2726#endif
2727 if ( !mi->isEnabledAndVisible() ) {
2728#ifndef QT_NO_WHATSTHIS
2729 if ( b ) {
2730 actItem = -1;
2731 updateItem( mi->id() );
2732 byeMenuBar();
2733 actSig( mi->id(), b);
2734 }
2735#endif
2736 } else {
2737 byeMenuBar(); // deactivate menu bar
2738 if ( mi->isEnabledAndVisible() ) {
2739 actItem = -1;
2740 updateItem( mi->id() );
2741 active_popup_menu = this;
2742 QGuardedPtr<QSignal> signal = mi->signal();
2743 actSig( mi->id(), b );
2744 if ( signal && !b )
2745 signal->activate();
2746 active_popup_menu = 0;
2747 }
2748 }
2749 }
2750 } else {
2751 if ( tornOff ) {
2752 close();
2753 } else {
2754 QMenuData* p = parentMenu;
2755 hide();
2756#ifndef QT_NO_MENUBAR
2757 if ( p && p->isMenuBar )
2758 ((QMenuBar*) p)->goodbye( TRUE );
2759#endif
2760 }
2761 }
2762
2763}
2764
2765/*! \internal
2766 This private function is to update the scroll states in styles that support scrolling. */
2767void
2768QPopupMenu::updateScrollerState()
2769{
2770 uint old_scrollable = d->scroll.scrollable;
2771 d->scroll.scrollable = QPopupMenuPrivate::Scroll::ScrollNone;
2772 if(!style().styleHint(QStyle::SH_PopupMenu_Scrollable, this))
2773 return;
2774
2775 QMenuItem *mi;
2776 QMenuItemListIt it( *mitems );
2777 if(d->scroll.topScrollableIndex) {
2778 for(int row = 0; (mi = it.current()) && row < d->scroll.topScrollableIndex; row++)
2779 ++it;
2780 if(!mi)
2781 it.toFirst();
2782 }
2783 int y = 0, sh = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
2784 if(!it.atFirst()) {
2785 // can't use |= because of a bug/feature in IBM xlC 5.0.2
2786 d->scroll.scrollable = d->scroll.scrollable | QPopupMenuPrivate::Scroll::ScrollUp;
2787 y += sh;
2788 }
2789 while ( (mi=it.current()) ) {
2790 ++it;
2791 int myheight = contentsRect().height();
2792 QSize sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
2793 QSize(0, itemHeight( mi )),
2794 QStyleOption(mi,maxPMWidth));
2795 if(y + sz.height() >= myheight) {
2796 d->scroll.scrollable = d->scroll.scrollable | QPopupMenuPrivate::Scroll::ScrollDown;
2797 break;
2798 }
2799 y += sz.height();
2800 }
2801 if((d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp) &&
2802 !(old_scrollable & QPopupMenuPrivate::Scroll::ScrollUp))
2803 d->scroll.topScrollableIndex++;
2804}
2805
2806#endif // QT_NO_POPUPMENU
2807
Note: See TracBrowser for help on using the repository browser.