source: trunk/src/widgets/qpopupmenu.cpp

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

Transferred Qt for OS/2 version 3.3.1-rc5 sources from the CVS

  • Property svn:keywords set to Id
File size: 77.9 KB
Line 
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
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 - 1 > contentsRect().bottom() ) {
1512 if ( y <= contentsRect().bottom() ) {
1513 QRect rect(x, y, itemw, contentsRect().bottom() - y + 1);
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 + 1);
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#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
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
1710void 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
1798void 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 );
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
1937 break;
1938 }
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 {
2027#if !defined(Q_WS_PM) // do it later, only in case of item activation
2028 byeMenuBar();
2029#endif
2030#ifndef QT_NO_WHATSTHIS
2031 bool b = QWhatsThis::inWhatsThisMode();
2032#else
2033 const bool b = FALSE;
2034#endif
2035 if ( mi->isEnabledAndVisible() || b ) {
2036#if defined(Q_WS_PM)
2037 byeMenuBar();
2038#endif
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 }
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
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
2174void QPopupMenu::timerEvent( QTimerEvent *e )
2175{
2176 QFrame::timerEvent( e );
2177}
2178
2179/*!
2180 \reimp
2181*/
2182void 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*/
2195void 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 */
2205void 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*/
2221int QPopupMenu::columns() const
2222{
2223 return ncols;
2224}
2225
2226/* This private slot handles the scrolling popupmenu */
2227void 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
2278void 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
2371void QPopupMenu::allowAnimation()
2372{
2373 preventAnimation = FALSE;
2374}
2375
2376void 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
2431int 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 */
2458void 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
2498int QPopupMenu::exec()
2499{
2500 return exec(mapToGlobal(QPoint(0,0)));
2501}
2502
2503
2504/* Internal slot used for exec(). */
2505
2506void 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
2516void 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*/
2549QSize 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*/
2567int 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 */
2586bool QPopupMenu::customWhatsThis() const
2587{
2588 return TRUE;
2589}
2590
2591
2592/*!
2593 \reimp
2594 */
2595bool 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 */
2627void QPopupMenu::focusInEvent( QFocusEvent * )
2628{
2629}
2630
2631/*!
2632 \reimp
2633 */
2634void QPopupMenu::focusOutEvent( QFocusEvent * )
2635{
2636}
2637
2638
2639class QTearOffMenuItem : public QCustomMenuItem
2640{
2641public:
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*/
2684int 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 */
2697void 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 */
2728void 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. */
2795void
2796QPopupMenu::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
Note: See TracBrowser for help on using the repository browser.