diff options
Diffstat (limited to 'experimental/tqtinterface/qt4/src/widgets/tqpopupmenu.cpp')
-rw-r--r-- | experimental/tqtinterface/qt4/src/widgets/tqpopupmenu.cpp | 2852 |
1 files changed, 2852 insertions, 0 deletions
diff --git a/experimental/tqtinterface/qt4/src/widgets/tqpopupmenu.cpp b/experimental/tqtinterface/qt4/src/widgets/tqpopupmenu.cpp new file mode 100644 index 000000000..bbfdb60c2 --- /dev/null +++ b/experimental/tqtinterface/qt4/src/widgets/tqpopupmenu.cpp @@ -0,0 +1,2852 @@ +/**************************************************************************** +** +** Implementation of TQPopupMenu class +** +** Created : 941128 +** +** Copyright (C) 2010 Timothy Pearson and (C) 1992-2008 Trolltech ASA. +** +** This file is part of the widgets module of the TQt GUI Toolkit. +** +** This file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free +** Software Foundation and appearing in the files LICENSE.GPL2 +** and LICENSE.GPL3 included in the packaging of this file. +** Alternatively you may (at your option) use any later version +** of the GNU General Public License if such license has been +** publicly approved by Trolltech ASA (or its successors, if any) +** and the KDE Free TQt Foundation. +** +** Please review the following information to ensure GNU General +** Public Licensing requirements will be met: +** http://trolltech.com/products/qt/licenses/licensing/opensource/. +** If you are unsure which license is appropriate for your use, please +** review the following information: +** http://trolltech.com/products/qt/licenses/licensing/licensingoverview +** or contact the sales department at sales@trolltech.com. +** +** This file may be used under the terms of the Q Public License as +** defined by Trolltech ASA and appearing in the file LICENSE.TQPL +** included in the packaging of this file. Licensees holding valid TQt +** Commercial licenses may use this file in accordance with the TQt +** Commercial License Agreement provided with the Software. +** +** This file is provided "AS IS" with NO WARRANTY OF ANY KIND, +** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted +** herein. +** +**********************************************************************/ + +#include "tqpopupmenu.h" +#ifndef TQT_NO_POPUPMENU +#include "tqmenubar.h" +#include "tqaccel.h" +#include "tqpainter.h" +#include "tqdrawutil.h" +#include "tqapplication.h" +#include "tqpixmap.h" +#include "tqpixmapcache.h" +#include "tqtimer.h" +#include "tqwhatsthis.h" +#include "tqobjectlist.h" +#include "tqguardedptr.h" +#include "tqeffects_p.h" +#include "tqcursor.h" +#include "tqstyle.h" +#include "tqtimer.h" +#include "tqdatetime.h" +#if defined(TQT_ACCESSIBILITY_SUPPORT) +#include "tqaccessible.h" +#endif + +//#define ANIMATED_POPUP +//#define BLEND_POPUP + +// Motif style parameters + +static const int motifArrowHMargin = 6; // arrow horizontal margin +static const int motifArrowVMargin = 2; // arrow vertical margin + +bool tqwidget_is_popup_menu(QWidget* w) { + return tqt_dynamic_cast<TQPopupMenu*>(w); +} + +QWidget* tqwidget_parent_popup_menu(QWidget* w) { + TQPopupMenu* tqpm = dynamic_cast<TQPopupMenu*>(w); + if (tqpm) { + TQMenuData *top = tqpm; // tqfind top level + if ( top->parentMenu ) { + if (top->parentMenu->isPopupMenu == FALSE) return 0; + return (dynamic_cast<TQPopupMenu*>(top->parentMenu)); + } + else return 0; + } + else return 0; +} + +/* + ++----------------------------- +| PopupFrame +| +------------------------- +| | ItemFrame +| | +--------------------- +| | | +| | | \ +| | | ^ T E X T ^ | ItemVMargin +| | | | | / +| | ItemHMargin +| + +*/ + +#if 0 +# define DEBUG_SLOPPY_SUBMENU +#endif + +// used for internal communication +static TQPopupMenu * syncMenu = 0; +static int syncMenuId = 0; + +// Used to detect motion prior to mouse-release +static int motion; + +// used to provide ONE single-shot timer +static TQTimer * singleSingleShot = 0; + +static bool supressAboutToShow = FALSE; + +static void cleanup() +{ + delete singleSingleShot; + singleSingleShot = 0; +} + +static void popupSubMenuLater( int msec, TQPopupMenu * receiver ) { + if ( !singleSingleShot ) { + singleSingleShot = new TQTimer( tqApp, "popup submenu timer" ); + qAddPostRoutine( cleanup ); + } + + singleSingleShot->disconnect( TQT_SIGNAL(timeout()) ); + TQObject::connect( singleSingleShot, TQT_SIGNAL(timeout()), + receiver, TQT_SLOT(subMenuTimer()) ); + singleSingleShot->start( msec, TRUE ); +} + +static bool preventAnimation = FALSE; + +#ifndef TQT_NO_WHATSTHIS +extern void qWhatsThisBDH(); +static TQMenuItem* whatsThisItem = 0; +#endif + +/*! + \class TQPopupMenu tqpopupmenu.h + \brief The TQPopupMenu class provides a popup menu widget. + + \ingroup application + \ingroup basic + \mainclass + + A popup menu widget is a selection menu. It can be either a + pull-down menu in a menu bar or a standalone context (popup) menu. + Pull-down menus are shown by the menu bar when the user clicks on + the respective item or presses the specified shortcut key. Use + TQMenuBar::insertItem() to insert a popup menu into a menu bar. + Show a context menu either asynchronously with popup() or + synchronously with exec(). + + Technically, a popup menu consists of a list of menu items. You + add items with insertItem(). An item is either a string, a pixmap + or a custom item that provides its own drawing function (see + TQCustomMenuItem). In addition, items can have an optional icon + drawn on the very left side and an accelerator key such as + "Ctrl+X". + + There are three kinds of menu items: separators, menu items that + perform an action and menu items that show a submenu. Separators + are inserted with insertSeparator(). For submenus, you pass a + pointer to a TQPopupMenu in your call to insertItem(). All other + items are considered action items. + + When inserting action items you usually specify a receiver and a + slot. The receiver will be notifed whenever the item is selected. + In addition, TQPopupMenu provides two Q_SIGNALS, activated() and + highlighted(), which signal the identifier of the respective menu + item. It is sometimes practical to connect several items to one + slot. To distinguish between them, specify a slot that takes an + integer argument and use setItemParameter() to associate a unique + value with each item. + + You clear a popup menu with clear() and remove single items with + removeItem() or removeItemAt(). + + A popup menu can display check marks for certain items when + enabled with setCheckable(TRUE). You check or uncheck items with + setItemChecked(). + + Items are either enabled or disabled. You toggle their state with + setItemEnabled(). Just before a popup menu becomes visible, it + emits the aboutToShow() signal. You can use this signal to set the + correct enabled/disabled states of all menu items before the user + sees it. The corresponding aboutToHide() signal is emitted when + the menu hides again. + + You can provide What's This? help for single menu items with + setWhatsThis(). See TQWhatsThis for general information about this + kind of lightweight online help. + + For ultimate flexibility, you can also add entire widgets as items + into a popup menu (for example, a color selector). + + A TQPopupMenu can also provide a tear-off menu. A tear-off menu is + a top-level window that tqcontains a copy of the menu. This makes it + possible for the user to "tear off" frequently used menus and + position them in a convenient place on the screen. If you want + that functionality for a certain menu, insert a tear-off handle + with insertTearOffHandle(). When using tear-off menus, bear in + mind that the concept isn't typically used on Microsoft Windows so + users may not be familiar with it. Consider using a TQToolBar + instead. Tear-off menus cannot contain custom widgets; if the + original menu tqcontains a custom widget item, this item is omitted. + + \link menu-example.html menu/menu.cpp\endlink is an example of + TQMenuBar and TQPopupMenu use. + + \important insertItem removeItem removeItemAt clear text pixmap iconSet insertSeparator changeItem whatsThis setWhatsThis accel setAccel setItemEnabled isItemEnabled setItemVisible isItemVisible setItemChecked isItemChecked connectItem disconnectItem setItemParameter itemParameter + + <img src=qpopmenu-m.png> <img src=qpopmenu-w.png> + + \sa TQMenuBar + \link guibooks.html#fowler GUI Design Handbook: Menu, Drop-Down and + Pop-Up\endlink +*/ + + +/*! + \fn void TQPopupMenu::aboutToShow() + + This signal is emitted just before the popup menu is displayed. + You can connect it to any slot that sets up the menu contents + (e.g. to ensure that the right items are enabled). + + \sa aboutToHide(), setItemEnabled(), setItemChecked(), insertItem(), removeItem() +*/ + +/*! + \fn void TQPopupMenu::aboutToHide() + + This signal is emitted just before the popup menu is hidden after + it has been displayed. + + \warning Do not open a widget in a slot connected to this signal. + + \sa aboutToShow(), setItemEnabled(), setItemChecked(), insertItem(), removeItem() +*/ + + + +/***************************************************************************** + TQPopupMenu member functions + *****************************************************************************/ + +class TQMenuDataData { + // attention: also defined in qmenudata.cpp +public: + TQMenuDataData(); + TQGuardedPtr<TQWidget> aWidget; + int aInt; +}; + +class TQPopupMenuPrivate { +public: + struct Scroll { + enum { ScrollNone=0, ScrollUp=0x01, ScrollDown=0x02 }; + uint scrollable : 2; + uint direction : 1; + int topScrollableIndex, scrollableSize; + TQTime lastScroll; + TQTimer *scrolltimer; + } scroll; + TQSize calcSize; + TQRegion mouseMoveBuffer; +}; + +static TQPopupMenu* active_popup_menu = 0; + +/*! + Constructs a popup menu called \a name with tqparent \a tqparent. + + Although a popup menu is always a top-level widget, if a tqparent is + passed the popup menu will be deleted when that tqparent is + destroyed (as with any other TQObject). +*/ + +TQPopupMenu::TQPopupMenu( TQWidget *tqparent, const char *name ) + : TQFrame( tqparent, name, (WFlags)WType_Popup | TQt::WNoAutoErase ) +{ + d = new TQPopupMenuPrivate; + d->scroll.scrollableSize = d->scroll.topScrollableIndex = 0; + d->scroll.scrollable = TQPopupMenuPrivate::Scroll::ScrollNone; + d->scroll.scrolltimer = 0; + isPopupMenu = TRUE; +#ifndef TQT_NO_ACCEL + autoaccel = 0; + accelDisabled = FALSE; +#endif + popupActive = -1; + snapToMouse = TRUE; + tab = 0; + checkable = 0; + tornOff = 0; + pendingDelayedContentsChanges = 0; + pendingDelayedStateChanges = 0; + maxPMWidth = 0; + + tab = 0; + ncols = 1; + setFrameStyle( TQFrame::PopupPanel | TQFrame::Raised ); + setMouseTracking(tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_MouseTracking, this)); + tqstyle().polishPopupMenu( this ); + setBackgroundMode( TQt::PaletteButton ); + connectModalRecursionSafety = 0; + + setFocusPolicy( Qt::StrongFocus ); +} + +/*! + Destroys the popup menu. +*/ + +TQPopupMenu::~TQPopupMenu() +{ + if ( syncMenu == this && tqApp ) { + tqApp->exit_loop(); + syncMenu = 0; + } + + if(d->scroll.scrolltimer) + delete d->scroll.scrolltimer; + + if ( isVisible() ) { + parentMenu = 0; + hidePopups(); + } + + delete (TQWidget*) TQMenuData::d->aWidget; // tear-off menu + + preventAnimation = FALSE; + delete d; +} + + +/*! + Updates the item with identity \a id. +*/ +void TQPopupMenu::updateItem( int id ) // update popup menu item +{ + updateRow( indexOf(id) ); +} + + +void TQPopupMenu::setCheckable( bool enable ) +{ + if ( isCheckable() != enable ) { + checkable = enable; + badSize = TRUE; + if ( TQMenuData::d->aWidget ) + ( (TQPopupMenu*)(TQWidget*)TQMenuData::d->aWidget)->setCheckable( enable ); + } +} + +/*! + \property TQPopupMenu::checkable + \brief whether the display of check marks on menu items is enabled + + When TRUE, the display of check marks on menu items is enabled. + Checking is always enabled when in Windows-style. + + \sa TQMenuData::setItemChecked() +*/ + +bool TQPopupMenu::isCheckable() const +{ + return checkable; +} + +void TQPopupMenu::menuContentsChanged() +{ + // here the part that can't be delayed + TQMenuData::menuContentsChanged(); + badSize = TRUE; // might change the size +#if defined(TQ_WS_MAC) && !defined(TQMAC_TQMENUBAR_NO_NATIVE) + mac_dirty_popup = 1; +#endif + if( pendingDelayedContentsChanges ) + return; + pendingDelayedContentsChanges = 1; + if( !pendingDelayedStateChanges ) // if the timer hasn't been started yet + TQTimer::singleShot( 0, this, TQT_SLOT(performDelayedChanges())); +} + +void TQPopupMenu::performDelayedContentsChanged() +{ + pendingDelayedContentsChanges = 0; + // here the part the can be delayed +#ifndef TQT_NO_ACCEL + // if performDelayedStateChanged() will be called too, + // it will call updateAccel() too, no need to do it twice + if( !pendingDelayedStateChanges ) + updateAccel( 0 ); +#endif + if ( isVisible() ) { + if ( tornOff ) + return; + updateSize(TRUE); + update(); + } + TQPopupMenu* p = (TQPopupMenu*)(TQWidget*)TQMenuData::d->aWidget; + if ( p && p->isVisible() ) { + p->updateSize(TRUE); + p->update(); + } +#if defined(TQ_WS_MAC) && !defined(TQMAC_TQMENUBAR_NO_NATIVE) + mac_dirty_popup = 1; +#endif +} + + +void TQPopupMenu::menuStateChanged() +{ + // here the part that can't be delayed + if( pendingDelayedStateChanges ) + return; + pendingDelayedStateChanges = 1; + if( !pendingDelayedContentsChanges ) // if the timer hasn't been started yet + TQTimer::singleShot( 0, this, TQT_SLOT(performDelayedChanges())); +} + +void TQPopupMenu::performDelayedStateChanged() +{ + pendingDelayedStateChanges = 0; + // here the part that can be delayed +#ifndef TQT_NO_ACCEL + updateAccel( 0 ); // ### when we have a good solution for the accel vs. focus widget problem, remove that. That is only a workaround + // if you remove this, see performDelayedContentsChanged() +#endif + update(); + if ( TQMenuData::d->aWidget ) + TQMenuData::d->aWidget->update(); +} + +void TQPopupMenu::performDelayedChanges() +{ + if( pendingDelayedContentsChanges ) + performDelayedContentsChanged(); + if( pendingDelayedStateChanges ) + performDelayedStateChanged(); +} + +void TQPopupMenu::menuInsPopup( TQPopupMenu *popup ) +{ + connect( popup, TQT_SIGNAL(activatedRedirect(int)), + TQT_SLOT(subActivated(int)) ); + connect( popup, TQT_SIGNAL(highlightedRedirect(int)), + TQT_SLOT(subHighlighted(int)) ); + connect( popup, TQT_SIGNAL(destroyed(TQObject*)), + this, TQT_SLOT(popupDestroyed(TQObject*)) ); +} + +void TQPopupMenu::menuDelPopup( TQPopupMenu *popup ) +{ + popup->disconnect( TQT_SIGNAL(activatedRedirect(int)) ); + popup->disconnect( TQT_SIGNAL(highlightedRedirect(int)) ); + disconnect( popup, TQT_SIGNAL(destroyed(TQObject*)), + this, TQT_SLOT(popupDestroyed(TQObject*)) ); +} + + +void TQPopupMenu::frameChanged() +{ + menuContentsChanged(); +} + +/*! + Displays the popup menu so that the item number \a indexAtPoint + will be at the specified \e global position \a pos. To translate a + widget's local coordinates into global coordinates, use + TQWidget::mapToGlobal(). + + When positioning a popup with exec() or popup(), bear in mind that + you cannot rely on the popup menu's current size(). For + performance reasons, the popup adapts its size only when + necessary, so in many cases, the size before and after the show is + different. Instead, use tqsizeHint(). It calculates the proper size + depending on the menu's current contents. +*/ + +void TQPopupMenu::popup( const TQPoint &pos, int indexAtPoint ) +{ + if ( !isPopup() && isVisible() ) + hide(); + + //avoid circularity + if ( isVisible() || !isEnabled() ) + return; + +#if defined(TQ_WS_MAC) && !defined(TQMAC_TQMENUBAR_NO_NATIVE) + if( macPopupMenu(pos, indexAtPoint )) + return; +#endif + +#if (TQT_VERSION-0 >= 0x040000) +#error "Fix this now" + // #### should move to TQWidget - anything might need this functionality, + // #### since anything can have WType_Popup window flag. + // #### This includes stuff in TQPushButton and some stuff for setting + // #### the tqgeometry of TQDialog. + // TQPopupMenu + // ::exec() + // ::popup() + // TQPushButton (shouldn't require TQMenuPopup) + // ::popupPressed + // Some stuff in qwidget.cpp for dialogs... can't remember exactly. + // Also the code here indicatets the parameter should be a rect, not a + // point. +#endif + + if(d->scroll.scrollable) { + d->scroll.scrollable = TQPopupMenuPrivate::Scroll::ScrollNone; + d->scroll.topScrollableIndex = d->scroll.scrollableSize = 0; + badSize = TRUE; + } + updateSize(); + + TQPoint mouse = TQCursor::pos(); + snapToMouse = pos == mouse; + + // have to emit here as a menu might be setup in a slot connected + // to aboutToShow which will change the size of the menu + bool s = supressAboutToShow; + supressAboutToShow = TRUE; + if ( !s) { + emit aboutToShow(); + updateSize(TRUE); + } + + int screen_num; + if (TQApplication::desktop()->isVirtualDesktop()) + screen_num = + TQApplication::desktop()->screenNumber( TQApplication::reverseLayout() ? + pos+TQPoint(width(),0) : pos ); + else + screen_num = TQApplication::desktop()->screenNumber( this ); +#ifdef TQ_WS_MAC + TQRect screen = TQApplication::desktop()->availableGeometry( screen_num ); +#else + TQRect screen = TQApplication::desktop()->screenGeometry( screen_num ); +#endif + int sw = screen.width(); // screen width + int sh = screen.height(); // screen height + int sx = screen.x(); // screen pos + int sy = screen.y(); + int x = pos.x(); + int y = pos.y(); + if ( indexAtPoint >= 0 ) // don't subtract when < 0 + y -= itemGeometry( indexAtPoint ).y(); // (would subtract 2 pixels!) + int w = width(); + int h = height(); + + if ( snapToMouse ) { + if ( tqApp->reverseLayout() ) + x -= w; + if ( x+w > sx+sw ) + x = mouse.x()-w; + if ( y+h > sy+sh ) + y = mouse.y()-h; + if ( x < sx ) + x = mouse.x(); + if ( y < sy ) + y = sy; + } + + if ( x+w > sx+sw ) // the complete widget must + x = sx+sw - w; // be visible + if ( y+h > sy+sh ) + y = sy+sh - h; + if ( x < sx ) + x = sx; + if ( y < sy ) + y = sy; + + if(tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_Scrollable, this)) { + int off_top = 0, off_bottom = 0; + if(y+h > sy+sh) + off_bottom = (y+h) - (sy+sh); + if(y < sy) + off_top = sy - y; + if(off_bottom || off_top) { + int ch = updateSize().height(); //store the old height, before setting scrollable --Sam + const int vextra = tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuFrameVerticalExtra, this); + d->scroll.scrollableSize = h - off_top - off_bottom - 2*vextra; + if(off_top) { + move( x, y = sy ); + d->scroll.scrollable = d->scroll.scrollable | TQPopupMenuPrivate::Scroll::ScrollUp; + } + if( off_bottom ) + d->scroll.scrollable = d->scroll.scrollable | TQPopupMenuPrivate::Scroll::ScrollDown; + if( off_top != off_bottom && indexAtPoint >= 0 ) { + ch -= (vextra * 2); + if(ch > sh) //no bigger than the screen! + ch = sh; + if( ch > d->scroll.scrollableSize ) + d->scroll.scrollableSize = ch; + } + + updateSize(TRUE); //now set the size using the scrollable/scrollableSize as above + w = width(); + h = height(); + if(indexAtPoint >= 0) { + if(off_top) { //scroll to it + register TQMenuItem *mi = NULL; + TQMenuItemListIt it(*mitems); + for(int tmp_y = 0; tmp_y < off_top && (mi=it.current()); ) { + TQSize sz = tqstyle().tqsizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(0, itemHeight( mi )), + TQStyleOption(mi,maxPMWidth,0)); + tmp_y += sz.height(); + d->scroll.topScrollableIndex++; + } + } + } + } + } + move( x, y ); + motion=0; + actItem = -1; + +#ifndef TQT_NO_EFFECTS + int hGuess = tqApp->reverseLayout() ? TQEffects::LeftScroll : TQEffects::RightScroll; + int vGuess = TQEffects::DownScroll; + if ( tqApp->reverseLayout() ) { + if ( (snapToMouse && ( x + w/2 > mouse.x() )) || + ( parentMenu && parentMenu->isPopupMenu && + ( x + w/2 > ((TQPopupMenu*)parentMenu)->x() ) ) ) + hGuess = TQEffects::RightScroll; + } else { + if ( (snapToMouse && ( x + w/2 < mouse.x() )) || + ( parentMenu && parentMenu->isPopupMenu && + ( x + w/2 < ((TQPopupMenu*)parentMenu)->x() ) ) ) + hGuess = TQEffects::LeftScroll; + } + +#ifndef TQT_NO_MENUBAR + if ( (snapToMouse && ( y + h/2 < mouse.y() )) || + ( parentMenu && parentMenu->isMenuBar && + ( y + h/2 < ((TQMenuBar*)parentMenu)->mapToGlobal( ((TQMenuBar*)parentMenu)->pos() ).y() ) ) ) + vGuess = TQEffects::UpScroll; +#endif + + if ( TQApplication::isEffectEnabled( Qt::UI_AnimateMenu ) && + preventAnimation == FALSE ) { + if ( TQApplication::isEffectEnabled( Qt::UI_FadeMenu ) ) + qFadeEffect( this ); + else if ( parentMenu ) + qScrollEffect( this, parentMenu->isPopupMenu ? hGuess : vGuess ); + else + qScrollEffect( this, hGuess | vGuess ); + } else +#endif + { + show(); + } +#if defined(TQT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::PopupMenuStart ); +#endif +} + +/*! + \fn void TQPopupMenu::activated( int id ) + + This signal is emitted when a menu item is selected; \a id is the + id of the selected item. + + Normally, you connect each menu item to a single slot using + TQMenuData::insertItem(), but sometimes you will want to connect + several items to a single slot (most often if the user selects + from an array). This signal is useful in such cases. + + \sa highlighted(), TQMenuData::insertItem() +*/ + +/*! + \fn void TQPopupMenu::highlighted( int id ) + + This signal is emitted when a menu item is highlighted; \a id is + the id of the highlighted item. + + \sa activated(), TQMenuData::insertItem() +*/ + +/*! \fn void TQPopupMenu::highlightedRedirect( int id ) + \internal + Used internally to connect submenus to their parents. +*/ + +/*! \fn void TQPopupMenu::activatedRedirect( int id ) + \internal + Used internally to connect submenus to their parents. +*/ + +void TQPopupMenu::subActivated( int id ) +{ + emit activatedRedirect( id ); +} + +void TQPopupMenu::subHighlighted( int id ) +{ + emit highlightedRedirect( id ); +} + +static bool fromAccel = FALSE; + +#ifndef TQT_NO_ACCEL +void TQPopupMenu::accelActivated( int id ) +{ + TQMenuItem *mi = tqfindItem( id ); + if ( mi && mi->isEnabledAndVisible() ) { + TQGuardedPtr<TQSignal> signal = mi->signal(); + fromAccel = TRUE; + actSig( mi->id() ); + fromAccel = FALSE; + if ( signal ) + signal->activate(); + } +} + +void TQPopupMenu::accelDestroyed() // accel about to be deleted +{ + autoaccel = 0; // don't delete it twice! +} +#endif //TQT_NO_ACCEL + +void TQPopupMenu::popupDestroyed( TQObject *o ) +{ + removePopup( (TQPopupMenu*)o ); +} + +void TQPopupMenu::actSig( int id, bool inwhatsthis ) +{ + if ( !inwhatsthis ) { + emit activated( id ); +#if defined(TQT_ACCESSIBILITY_SUPPORT) + if ( !fromAccel ) + TQAccessible::updateAccessibility( this, indexOf(id)+1, TQAccessible::MenuCommand ); +#endif + } else { +#ifndef TQT_NO_WHATSTHIS + TQRect r( itemGeometry( indexOf( id ) ) ); + TQPoint p( r.center().x(), r.bottom() ); + TQString whatsThis = tqfindItem( id )->whatsThis(); + if ( whatsThis.isNull() ) + whatsThis = TQWhatsThis::textFor( this, p ); + TQWhatsThis::leaveWhatsThisMode( whatsThis, mapToGlobal( p ), this ); +#endif + } + + emit activatedRedirect( id ); +} + +void TQPopupMenu::hilitSig( int id ) +{ + emit highlighted( id ); + emit highlightedRedirect( id ); + +#if defined(TQT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, indexOf(id)+1, TQAccessible::Focus ); + TQAccessible::updateAccessibility( this, indexOf(id)+1, TQAccessible::Selection ); +#endif +} + +void TQPopupMenu::setFirstItemActive() +{ + TQMenuItemListIt it(*mitems); + register TQMenuItem *mi; + int ai = 0; + if(d->scroll.scrollable) + ai = d->scroll.topScrollableIndex; + while ( (mi=it.current()) ) { + ++it; + if ( !mi->isSeparator() && mi->id() != TQMenuData::d->aInt && + ( tqstyle().tqstyleHint( TQStyle::SH_PopupMenu_AllowActiveAndDisabled, this ) || mi->isEnabledAndVisible() )) { + setActiveItem( ai ); + return; + } + ai++; + } + actItem = -1; +} + +/*! + \internal + Hides all popup menus (in this menu tree) that are currently open. +*/ + +void TQPopupMenu::hideAllPopups() +{ + register TQMenuData *top = this; // tqfind top level popup + if ( !preventAnimation ) + TQTimer::singleShot( 10, this, TQT_SLOT(allowAnimation()) ); + preventAnimation = TRUE; + + if ( !isPopup() ) + return; // nothing to do + + while ( top->parentMenu && top->parentMenu->isPopupMenu + && ((TQPopupMenu*)top->parentMenu)->isPopup() ) + top = top->parentMenu; + ((TQPopupMenu*)top)->hide(); // cascade from top level + +#ifndef TQT_NO_WHATSTHIS + if (whatsThisItem) { + qWhatsThisBDH(); + whatsThisItem = 0; + } +#endif + +} + +/*! + \internal + Hides all popup sub-menus. +*/ + +void TQPopupMenu::hidePopups() +{ + if ( !preventAnimation ) + TQTimer::singleShot( 10, this, TQT_SLOT(allowAnimation()) ); + preventAnimation = TRUE; + + TQMenuItemListIt it(*mitems); + register TQMenuItem *mi; + while ( (mi=it.current()) ) { + ++it; + if ( mi->popup() && mi->popup()->parentMenu == this ) //avoid circularity + mi->popup()->hide(); + } + popupActive = -1; // no active sub menu + if(tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_SubMenuPopupDelay, this)) + d->mouseMoveBuffer = TQRegion(); + + TQRect mfrect = itemGeometry( actItem ); + setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE ); +} + + +/*! + \internal + Sends the event to the menu bar. +*/ + +bool TQPopupMenu::tryMenuBar( TQMouseEvent *e ) +{ + register TQMenuData *top = this; // tqfind top level + while ( top->parentMenu ) + top = top->parentMenu; +#ifndef TQT_NO_MENUBAR + return top->isMenuBar ? + ((TQMenuBar *)top)->tryMouseEvent( this, e ) : + ((TQPopupMenu*)top)->tryMouseEvent(this, e ); +#else + return ((TQPopupMenu*)top)->tryMouseEvent(this, e ); +#endif +} + + +/*! + \internal +*/ +bool TQPopupMenu::tryMouseEvent( TQPopupMenu *p, TQMouseEvent * e) +{ + if ( p == this ) + return FALSE; + TQPoint pos = mapFromGlobal( e->globalPos() ); + if ( !TQT_TQRECT_OBJECT(rect()).tqcontains( pos ) ) // outside + return FALSE; + TQMouseEvent ee( e->type(), pos, e->globalPos(), e->button(), e->state() ); + event( &ee ); + return TRUE; +} + +/*! + \internal + Tells the menu bar to go back to idle state. +*/ + +void TQPopupMenu::byeMenuBar() +{ +#ifndef TQT_NO_MENUBAR + register TQMenuData *top = this; // tqfind top level + while ( top->parentMenu ) + top = top->parentMenu; +#endif + hideAllPopups(); +#ifndef TQT_NO_MENUBAR + if ( top->isMenuBar ) + ((TQMenuBar *)top)->goodbye(); +#endif +} + + +/*! + \internal + Return the item at \a pos, or -1 if there is no item there or if + it is a separator item. +*/ + +int TQPopupMenu::itemAtPos( const TQPoint &pos, bool ignoreSeparator ) const +{ + if ( !contentsRect().tqcontains(pos) ) + return -1; + + int row = 0; + int x = contentsRect().x(); + int y = contentsRect().y(); + TQMenuItem *mi; + TQMenuItemListIt it( *mitems ); + if(d->scroll.scrollable) { + if(d->scroll.topScrollableIndex) { + for( ; (mi = it.current()) && row < d->scroll.topScrollableIndex; row++) + ++it; + if(!mi) { + row = 0; + it.toFirst(); + } + y += tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + } + } + int itemw = contentsRect().width() / ncols; + TQSize sz; + while ( (mi=it.current()) ) { + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && + y >= contentsRect().height() - tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this)) + return -1; + ++it; + if ( !mi->isVisible() ) { + ++row; + continue; + } + int itemh = itemHeight( mi ); + + sz = tqstyle().tqsizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(0, itemh), + TQStyleOption(mi,maxPMWidth)); + sz = sz.expandedTo(TQSize(itemw, sz.height())); + itemw = sz.width(); + itemh = sz.height(); + + if ( ncols > 1 && y + itemh > contentsRect().bottom() ) { + y = contentsRect().y(); + x +=itemw; + } + if ( TQRect( x, y, itemw, itemh ).tqcontains( pos ) ) + break; + y += itemh; + ++row; + } + + if ( mi && ( !ignoreSeparator || !mi->isSeparator() ) ) + return row; + return -1; +} + +/*! + \internal + Returns the tqgeometry of item number \a index. +*/ + +TQRect TQPopupMenu::itemGeometry( int index ) +{ + TQMenuItem *mi; + TQSize sz; + int row = 0, scrollh = 0; + int x = contentsRect().x(); + int y = contentsRect().y(); + TQMenuItemListIt it( *mitems ); + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp) { + scrollh = tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + y += scrollh; + if(d->scroll.topScrollableIndex) { + for( ; (mi = it.current()) && row < d->scroll.topScrollableIndex; row++) + ++it; + if(!mi) { + row = 0; + it.toFirst(); + } + } + } + int itemw = contentsRect().width() / ncols; + while ( (mi=it.current()) ) { + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && + y >= contentsRect().height() - scrollh) + break; + ++it; + if ( !mi->isVisible() ) { + ++row; + continue; + } + int itemh = itemHeight( mi ); + + sz = tqstyle().tqsizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(0, itemh), + TQStyleOption(mi,maxPMWidth)); + sz = sz.expandedTo(TQSize(itemw, sz.height())); + itemw = sz.width(); + itemh = sz.height(); + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && + (y + itemh > contentsRect().height() - scrollh)) + itemh -= (y + itemh) - (contentsRect().height() - scrollh); + if ( ncols > 1 && y + itemh > contentsRect().bottom() ) { + y = contentsRect().y(); + x +=itemw; + } + if ( row == index ) + return TQRect( x,y,itemw,itemh ); + y += itemh; + ++row; + } + + return TQRect(0,0,0,0); +} + + +/*! + \internal + Calculates and sets the size of the popup menu, based on the size + of the items. +*/ + +TQSize TQPopupMenu::updateSize(bool force_update, bool do_resize) +{ + polish(); + if ( count() == 0 ) { + TQSize ret = TQSize( 50, 8 ); + if(do_resize) + setFixedSize( ret ); + badSize = TRUE; + return ret; + } + + int scrheight = 0; + if(d->scroll.scrollableSize) { + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp) + scrheight += tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown) + scrheight += tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + } + + if(badSize || force_update) { +#ifndef TQT_NO_ACCEL + updateAccel( 0 ); +#endif + int height = 0; + int max_width = 0, max_height = 0; + TQFontMetrics fm = fontMetrics(); + register TQMenuItem *mi; + maxPMWidth = 0; + int maxWidgetWidth = 0; + tab = 0; + + for ( TQMenuItemListIt it( *mitems ); it.current(); ++it ) { + mi = it.current(); + TQWidget *miw = mi->widget(); + if (miw) { + if ( miw->parentWidget() != this ) + miw->reparent( this, TQPoint(0,0), TRUE ); + // widget items musn't propgate mouse events + ((TQPopupMenu*)miw)->setWFlags(TQt::WNoMousePropagation); + } + if ( mi->custom() ) + mi->custom()->setFont( font() ); + if ( mi->iconSet() != 0) + maxPMWidth = TQMAX( maxPMWidth, + mi->iconSet()->pixmap( TQIconSet::Small, TQIconSet::Normal ).width() + 4 ); + } + + int dh = TQApplication::desktop()->height(); + ncols = 1; + + for ( TQMenuItemListIt it2( *mitems ); it2.current(); ++it2 ) { + mi = it2.current(); + if ( !mi->isVisible() ) + continue; + int w = 0; + int itemHeight = TQPopupMenu::itemHeight( mi ); + + if ( mi->widget() ) { + TQSize s( mi->widget()->tqsizeHint() ); + s = s.expandedTo( mi->widget()->tqminimumSize() ); + mi->widget()->resize( s ); + if ( s.width() > maxWidgetWidth ) + maxWidgetWidth = s.width(); + itemHeight = s.height(); + } else { + if( ! mi->isSeparator() ) { + if ( mi->custom() ) { + if ( mi->custom()->fullSpan() ) { + maxWidgetWidth = TQMAX( maxWidgetWidth, + mi->custom()->tqsizeHint().width() ); + } else { + TQSize s ( mi->custom()->tqsizeHint() ); + w += s.width(); + } + } + + w += maxPMWidth; + + if (! mi->text().isNull()) { + TQString s = mi->text(); + int t; + if ( (t = s.tqfind('\t')) >= 0 ) { // string tqcontains tab + w += fm.width( s, t ); + w -= s.tqcontains('&') * fm.width('&'); + w += s.tqcontains("&&") * fm.width('&'); + int tw = fm.width( s.mid(t + 1) ); + if ( tw > tab) + tab = tw; + } else { + w += fm.width( s ); + w -= s.tqcontains('&') * fm.width('&'); + w += s.tqcontains("&&") * fm.width('&'); + } + } else if (mi->pixmap()) + w += mi->pixmap()->width(); + } else { + if ( mi->custom() ) { + TQSize s ( mi->custom()->tqsizeHint() ); + w += s.width(); + } else { + w = itemHeight = 2; + } + } + + TQSize sz = tqstyle().tqsizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(w, itemHeight), + TQStyleOption(mi,maxPMWidth)); + + w = sz.width(); + itemHeight = sz.height(); + +#if defined(TQT_CHECK_NULL) + if ( mi->text().isNull() && !mi->pixmap() && !mi->iconSet() && + !mi->isSeparator() && !mi->widget() && !mi->custom() ) + qWarning( "TQPopupMenu: (%s) Popup has invalid menu item", + name( "unnamed" ) ); +#endif + } + height += itemHeight; + if(tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_Scrollable, this)) { + if(scrheight && height >= d->scroll.scrollableSize - scrheight) { + height = d->scroll.scrollableSize - scrheight; + break; + } + } else if( height + 2*frameWidth() >= dh ) { + ncols++; + max_height = TQMAX(max_height, height - itemHeight); + height = itemHeight; + } + if ( w > max_width ) + max_width = w; + } + if( ncols == 1 && !max_height ) + max_height = height; + + if(tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_Scrollable, this)) { + height += scrheight; + setMouseTracking(TRUE); + } + + if ( tab ) + tab -= fontMetrics().minRightBearing(); + else + max_width -= fontMetrics().minRightBearing(); + + if ( max_width + tab < maxWidgetWidth ) + max_width = maxWidgetWidth - tab; + + const int fw = frameWidth(); + int extra_width = (fw+tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuFrameHorizontalExtra, this)) * 2, + extra_height = (fw+tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuFrameVerticalExtra, this)) * 2; + if ( ncols == 1 ) + d->calcSize = TQSize( TQMAX( minimumWidth(), max_width + tab + extra_width ), + TQMAX( minimumHeight() , height + extra_height ) ); + else + d->calcSize = TQSize( TQMAX( minimumWidth(), (ncols*(max_width + tab)) + extra_width ), + TQMAX( minimumHeight(), TQMIN( max_height + extra_height + 1, dh ) ) ); + badSize = FALSE; + } + + { + // Position the widget items. It could be done in drawContents + // but this way we get less flicker. + TQSize sz; + int x = contentsRect().x(); + int y = contentsRect().y(); + int itemw = contentsRect().width() / ncols; + for(TQMenuItemListIt it(*mitems); it.current(); ++it) { + TQMenuItem *mi = it.current(); + if ( !mi->isVisible() ) + continue; + + int itemh = itemHeight( mi ); + + sz = tqstyle().tqsizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(0, itemh), TQStyleOption(mi,maxPMWidth)); + sz = sz.expandedTo(TQSize(itemw, sz.height())); + itemw = sz.width(); + itemh = sz.height(); + + if ( ncols > 1 && y + itemh > contentsRect().bottom() ) { + y = contentsRect().y(); + x +=itemw; + } + if ( mi->widget() ) + mi->widget()->setGeometry( x, y, itemw, mi->widget()->height() ); + y += itemh; + } + } + + if( do_resize && size() != d->calcSize ) { + setMaximumSize( d->calcSize ); + d->calcSize = tqmaximumSize(); //let the max size adjust it (virtual) + resize( d->calcSize ); + } + return d->calcSize; +} + + +#ifndef TQT_NO_ACCEL +/*! + \internal + The \a tqparent is 0 when it is updated when a menu item has + changed a state, or it is something else if called from the menu bar. +*/ + +void TQPopupMenu::updateAccel( TQWidget *tqparent ) +{ + TQMenuItemListIt it(*mitems); + register TQMenuItem *mi; + + if ( tqparent ) { + delete autoaccel; + autoaccel = 0; + } else if ( !autoaccel ) { + // we have no tqparent. Rather than ignoring any accelerators we try to tqfind this popup's main window + if ( tornOff ) { + tqparent = this; + } else { + TQWidget *w = (TQWidget *) this; + tqparent = w->parentWidget(); + while ( (!w->testWFlags(TQt::WType_TopLevel) || !w->testWFlags(WType_Popup)) && tqparent ) { + w = tqparent; + tqparent = tqparent->parentWidget(); + } + } + } + + if ( tqparent == 0 && autoaccel == 0 ) + return; + + if ( autoaccel ) // build it from scratch + autoaccel->clear(); + else { + // create an autoaccel in any case, even if we might not use + // it immediately. Maybe the user needs it later. + autoaccel = new TQAccel( tqparent, this ); + connect( autoaccel, TQT_SIGNAL(activated(int)), + TQT_SLOT(accelActivated(int)) ); + connect( autoaccel, TQT_SIGNAL(activatedAmbiguously(int)), + TQT_SLOT(accelActivated(int)) ); + connect( autoaccel, TQT_SIGNAL(destroyed()), + TQT_SLOT(accelDestroyed()) ); + if ( accelDisabled ) + autoaccel->setEnabled( FALSE ); + } + while ( (mi=it.current()) ) { + ++it; + TQKeySequence k = mi->key(); + if ( (int)k ) { + int id = autoaccel->insertItem( k, mi->id() ); +#ifndef TQT_NO_WHATSTHIS + autoaccel->setWhatsThis( id, mi->whatsThis() ); +#endif + } + if ( !mi->text().isNull() || mi->custom() ) { + TQString s = mi->text(); + int i = s.tqfind('\t'); + + // Note: Only looking at the first key in the sequence! + if ( (int)k && (int)k != TQt::Key_unknown ) { + TQString t = (TQString)mi->key(); + if ( i >= 0 ) + s.tqreplace( i+1, s.length()-i, t ); + else { + s += '\t'; + s += t; + } + } else if ( !k ) { + if ( i >= 0 ) + s.truncate( i ); + } + if ( s != mi->text() ) { + mi->setText( s ); + badSize = TRUE; + } + } + if ( mi->popup() && tqparent ) { // call recursively + // reuse + TQPopupMenu* popup = mi->popup(); + if (!popup->avoid_circularity) { + popup->avoid_circularity = 1; + popup->updateAccel( tqparent ); + popup->avoid_circularity = 0; + } + } + } +} + +/*! + \internal + It would be better to check in the slot. +*/ + +void TQPopupMenu::enableAccel( bool enable ) +{ + if ( autoaccel ) + autoaccel->setEnabled( enable ); + accelDisabled = !enable; // rememeber when updateAccel + TQMenuItemListIt it(*mitems); + register TQMenuItem *mi; + while ( (mi=it.current()) ) { // do the same for sub popups + ++it; + if ( mi->popup() ) // call recursively + mi->popup()->enableAccel( enable ); + } +} +#endif + +/*! + \reimp +*/ +void TQPopupMenu::setFont( const TQFont &font ) +{ + TQWidget::setFont( font ); + badSize = TRUE; + if ( isVisible() ) { + updateSize(); + update(); + } +} + +/*! + \reimp +*/ +void TQPopupMenu::show() +{ + if ( !isPopup() && isVisible() ) + hide(); + + if ( isVisible() ) { + supressAboutToShow = FALSE; + TQWidget::show(); + return; + } + if (!supressAboutToShow) + emit aboutToShow(); + else + supressAboutToShow = FALSE; + performDelayedChanges(); + updateSize(TRUE); + +// NOTE TO ANYONE WONDERING ABOUT TQT_TQWIDGET_FIX_BROKEN_POPUP_MOUSE_FOCUS +// Menu problems are caused by the fact that the popup menu mouse focus is shifted to the new child widget (which then receives the mouseReleaseEvent) pretty much as soon as show() is called +// This would seem to be a direct violation of the Qt4 documentation, which states: +// "Qt automatically grabs the mouse when a mouse button is pressed inside a widget; the widget will continue to receive mouse events until the last mouse button is released." +// Upon closer inspection, it is not: +// "mouseReleaseEvent() - called when a mouse button is released. A widget receives mouse release events when it has received the corresponding mouse press event. This means that if the user presses the mouse inside your widget, then drags the mouse to somewhere else, then releases, your widget receives the release event. There is one exception: if a popup menu appears while the mouse button is held down, this popup immediately steals the mouse events." +// +// Now for the real cause: +// It's an undocumented change in the mouse event handler +// qapplication_x11.cpp +// if ((windowType() == Qt::Popup) && rect().contains(pos) && 0) +// The "&& 0" is the problem +// It shuts down a very important chunk of code... +// ...the chunk that was responsible for mapping the mouse events to any popup menu which was actually *under* the cursor! + + TQWidget::show(); + popupActive = -1; + if(tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_SubMenuPopupDelay, this)) + d->mouseMoveBuffer = TQRegion(); +} + +/*! + \reimp +*/ + +void TQPopupMenu::hide() +{ + if ( syncMenu == this && tqApp ) { + tqApp->exit_loop(); + syncMenu = 0; + } + + if ( !isVisible() ) { + TQWidget::hide(); + return; + } + emit aboutToHide(); + + actItem = popupActive = -1; + if(tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_SubMenuPopupDelay, this)) + d->mouseMoveBuffer = TQRegion(); + mouseBtDn = FALSE; // mouse button up +#if defined(TQT_ACCESSIBILITY_SUPPORT) + TQAccessible::updateAccessibility( this, 0, TQAccessible::PopupMenuEnd ); +#endif + parentMenu = 0; + hidePopups(); + TQWidget::hide(); +} + + +/*! + Calculates the height in pixels of the item in row \a row. +*/ +int TQPopupMenu::itemHeight( int row ) const +{ + return itemHeight( mitems->at( row ) ); +} + +/*! + \overload + + Calculates the height in pixels of the menu item \a mi. +*/ +int TQPopupMenu::itemHeight( TQMenuItem *mi ) const +{ + if ( mi->widget() ) + return mi->widget()->height(); + if ( mi->custom() && mi->custom()->fullSpan() ) + return mi->custom()->tqsizeHint().height(); + + TQFontMetrics fm(fontMetrics()); + int h = 0; + if ( mi->isSeparator() ) // separator height + h = 2; + else if ( mi->pixmap() ) // pixmap height + h = mi->pixmap()->height(); + else // text height + h = fm.height(); + + if ( !mi->isSeparator() && mi->iconSet() != 0 ) + h = TQMAX(h, mi->iconSet()->pixmap( TQIconSet::Small, + TQIconSet::Normal ).height()); + if ( mi->custom() ) + h = TQMAX(h, mi->custom()->tqsizeHint().height()); + + return h; +} + + +/*! + Draws menu item \a mi in the area \a x, \a y, \a w, \a h, using + painter \a p. The item is drawn active if \a act is TRUE or drawn + inactive if \a act is FALSE. The rightmost \a tab_ pixels are used + for accelerator text. + + \sa TQStyle::tqdrawControl() +*/ +void TQPopupMenu::drawItem( TQPainter* p, int tab_, TQMenuItem* mi, + bool act, int x, int y, int w, int h) +{ + TQStyle::SFlags flags = TQStyle::Style_Default; + if (isEnabled() && mi->isEnabledAndVisible() && (!mi->popup() || mi->popup()->isEnabled()) ) + flags |= TQStyle::Style_Enabled; + if (act) + flags |= TQStyle::Style_Active; + if (mouseBtDn) + flags |= TQStyle::Style_Down; + + const TQColorGroup &cg = ((flags&TQStyle::Style_Enabled) ? tqcolorGroup() : tqpalette().disabled() ); + + if ( mi->custom() && mi->custom()->fullSpan() ) { + TQMenuItem dummy; + tqstyle().tqdrawControl(TQStyle::CE_PopupMenuItem, p, this, TQRect(x, y, w, h), cg, + flags, TQStyleOption(&dummy,maxPMWidth,tab_)); + mi->custom()->paint( p, cg, act, flags&TQStyle::Style_Enabled, x, y, w, h ); + } else + tqstyle().tqdrawControl(TQStyle::CE_PopupMenuItem, p, this, TQRect(x, y, w, h), cg, + flags, TQStyleOption(mi,maxPMWidth,tab_)); +} + + +/*! + Draws all menu items using painter \a p. +*/ +void TQPopupMenu::drawContents( TQPainter* p ) +{ + TQMenuItemListIt it(*mitems); + TQMenuItem *mi = 0; + int row = 0; + int x = contentsRect().x(); + int y = contentsRect().y(); + if(d->scroll.scrollable) { + if(d->scroll.topScrollableIndex) { + for( ; (mi = it.current()) && row < d->scroll.topScrollableIndex; row++) + ++it; + if(!mi) + it.toFirst(); + } + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp) { + TQRect rect(x, y, contentsRect().width(), + tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this)); + if(!p->hasClipping() || TQT_TQREGION_OBJECT(p->clipRegion()).tqcontains(rect)) { + TQStyle::SFlags flags = TQStyle::Style_Up; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + tqstyle().tqdrawControl(TQStyle::CE_PopupMenuScroller, p, this, rect, + tqcolorGroup(), flags, TQStyleOption(maxPMWidth)); + } + y += rect.height(); + } + } + + int itemw = contentsRect().width() / ncols; + TQSize sz; + TQStyle::SFlags flags; + while ( (mi=it.current()) ) { + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && + y >= contentsRect().height() - tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this)) + break; + ++it; + if ( !mi->isVisible() ) { + ++row; + continue; + } + int itemh = itemHeight( mi ); + sz = tqstyle().tqsizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(0, itemh), + TQStyleOption(mi,maxPMWidth,0) + ); + sz = sz.expandedTo(TQSize(itemw, sz.height())); + itemw = sz.width(); + itemh = sz.height(); + + if ( ncols > 1 && y + itemh > contentsRect().bottom() ) { + if ( y < contentsRect().bottom() ) { + TQRect rect(x, y, itemw, contentsRect().bottom() - y); + if(!p->hasClipping() || TQT_TQREGION_OBJECT(p->clipRegion()).tqcontains(rect)) { + flags = TQStyle::Style_Default; + if (isEnabled() && mi->isEnabledAndVisible()) + flags |= TQStyle::Style_Enabled; + tqstyle().tqdrawControl(TQStyle::CE_PopupMenuItem, p, this, rect, + tqcolorGroup(), flags, TQStyleOption((TQMenuItem*)0,maxPMWidth)); + } + } + y = contentsRect().y(); + x +=itemw; + } + if (!mi->widget() && (!p->hasClipping() || TQT_TQREGION_OBJECT(p->clipRegion()).tqcontains(TQRect(x, y, itemw, itemh)))) + drawItem( p, tab, mi, row == actItem, x, y, itemw, itemh ); + y += itemh; + ++row; + } + if ( y < contentsRect().bottom() ) { + TQRect rect(x, y, itemw, contentsRect().bottom() - y); + if(!p->hasClipping() || TQT_TQREGION_OBJECT(p->clipRegion()).tqcontains(rect)) { + flags = TQStyle::Style_Default; + if ( isEnabled() ) + flags |= TQStyle::Style_Enabled; + tqstyle().tqdrawControl(TQStyle::CE_PopupMenuItem, p, this, rect, + tqcolorGroup(), flags, TQStyleOption((TQMenuItem*)0,maxPMWidth)); + } + } + if( d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown ) { + int sh = tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + TQRect rect(x, contentsRect().height() - sh, contentsRect().width(), sh); + if(!p->hasClipping() || TQT_TQREGION_OBJECT(p->clipRegion()).tqcontains(rect)) { + TQStyle::SFlags flags = TQStyle::Style_Down; + if (isEnabled()) + flags |= TQStyle::Style_Enabled; + tqstyle().tqdrawControl(TQStyle::CE_PopupMenuScroller, p, this, rect, + tqcolorGroup(), flags, TQStyleOption(maxPMWidth)); + } + } +#if defined( DEBUG_SLOPPY_SUBMENU ) + if ( tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_SloppySubMenus, this )) { + p->setClipRegion( d->mouseMoveBuffer ); + p->fillRect( d->mouseMoveBuffer.boundingRect(), tqcolorGroup().brush( TQColorGroup::Highlight ) ); + } +#endif +} + + +/***************************************************************************** + Event handlers + *****************************************************************************/ + +/*! + \reimp +*/ + +void TQPopupMenu::paintEvent( TQPaintEvent *e ) +{ + TQFrame::paintEvent( e ); +} + +/*! + \reimp +*/ + +void TQPopupMenu::closeEvent( TQCloseEvent * e) { + e->accept(); + byeMenuBar(); +} + + +/*! + \reimp +*/ + +void TQPopupMenu::mousePressEvent( TQMouseEvent *e ) +{ + int sh = tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + if (TQT_TQRECT_OBJECT(rect()).tqcontains(e->pos()) && + ((d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp && e->pos().y() <= sh) || //up + (d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && + e->pos().y() >= contentsRect().height() - sh))) //down + return; + + mouseBtDn = TRUE; // mouse button down + int item = itemAtPos( e->pos() ); + if ( item == -1 ) { + if ( !TQT_TQRECT_OBJECT(rect()).tqcontains(e->pos()) && !tryMenuBar(e) ) { + byeMenuBar(); + } + return; + } + register TQMenuItem *mi = mitems->at(item); + if ( item != actItem ) // new item activated + setActiveItem( item ); + + TQPopupMenu *popup = mi->popup(); + if ( popup ) { + if ( popup->isVisible() ) { // sub menu already open + int pactItem = popup->actItem; + popup->actItem = -1; + popup->hidePopups(); + popup->updateRow( pactItem ); + } else { // open sub menu + hidePopups(); + popupSubMenuLater( 20, this ); + } + } else { + hidePopups(); + } +} + +/*! + \reimp +*/ + +void TQPopupMenu::mouseReleaseEvent( TQMouseEvent *e ) +{ + // do not hide a standalone context menu on press-release, unless + // the user moved the mouse significantly + if ( !parentMenu && !mouseBtDn && actItem < 0 && motion < 6 ) + return; + + mouseBtDn = FALSE; + + // if the user released the mouse outside the menu, pass control + // to the menubar or our parent menu + int sh = tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + + if ( !TQT_TQRECT_OBJECT(rect()).tqcontains( e->pos() ) && tryMenuBar(e) ) { + return; + } + else if((d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp && e->pos().y() <= sh) || //up + (d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && + e->pos().y() >= contentsRect().height() - sh)) //down + return; + + if ( actItem < 0 ) { // we do not have an active item + // if the release is inside without motion (happens with + // oversized popup menus on small screens), ignore it + if ( TQT_TQRECT_OBJECT(rect()).tqcontains( e->pos() ) && motion < 6 ) + return; + else + byeMenuBar(); + } else { // selected menu item! + register TQMenuItem *mi = mitems->at(actItem); + if ( mi ->widget() ) { + TQWidget* widgetAt = TQApplication::widgetAt( e->globalPos(), TRUE ); + if ( widgetAt && widgetAt != this ) { + TQMouseEvent me( e->type(), widgetAt->mapFromGlobal( e->globalPos() ), + e->globalPos(), e->button(), e->state() ); + TQApplication::sendEvent( widgetAt, &me ); + } + } + TQPopupMenu *popup = mi->popup(); +#ifndef TQT_NO_WHATSTHIS + bool b = TQWhatsThis::inWhatsThisMode(); +#else + const bool b = FALSE; +#endif + if ( !mi->isEnabledAndVisible() ) { +#ifndef TQT_NO_WHATSTHIS + if ( b ) { + actItem = -1; + updateItem( mi->id() ); + byeMenuBar(); + actSig( mi->id(), b); + } +#endif + } else if ( popup ) { + popup->setFirstItemActive(); + } else { // normal menu item + byeMenuBar(); // deactivate menu bar + if ( mi->isEnabledAndVisible() ) { + actItem = -1; + updateItem( mi->id() ); + active_popup_menu = this; + TQGuardedPtr<TQSignal> signal = mi->signal(); + actSig( mi->id(), b ); + if ( signal && !b ) + signal->activate(); + active_popup_menu = 0; + } + } + } +} + +/*! + \reimp +*/ + +void TQPopupMenu::mouseMoveEvent( TQMouseEvent *e ) +{ + motion++; + + if ( parentMenu && parentMenu->isPopupMenu ) { + TQPopupMenu* p = (TQPopupMenu*)parentMenu; + int myIndex; + + p->tqfindPopup( this, &myIndex ); + TQPoint pPos = p->mapFromParent( e->globalPos() ); + if ( p->actItem != myIndex && !TQT_TQRECT_OBJECT(p->rect()).tqcontains( pPos ) ) + p->setActiveItem( myIndex ); + + if ( tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_SloppySubMenus, this )) { + p->d->mouseMoveBuffer = TQRegion(); +#ifdef DEBUG_SLOPPY_SUBMENU + p->tqrepaint(); +#endif + } + } + + if ( (e->state() & Qt::MouseButtonMask) == 0 && + !hasMouseTracking() ) + return; + + if(d->scroll.scrollable && e->pos().x() >= rect().x() && e->pos().x() <= rect().width()) { + int sh = tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + if((d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp && e->pos().y() <= sh) || + (d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && e->pos().y() >= height()-sh)) { + if(!d->scroll.scrolltimer) { + d->scroll.scrolltimer = new TQTimer(this, "popup scroll timer"); + TQObject::connect( d->scroll.scrolltimer, TQT_SIGNAL(timeout()), + this, TQT_SLOT(subScrollTimer()) ); + } + if(!d->scroll.scrolltimer->isActive()) + d->scroll.scrolltimer->start(40); + return; + } + } + + int item = itemAtPos( e->pos() ); + if ( item == -1 ) { // no valid item + int lastActItem = actItem; + actItem = -1; + if ( lastActItem >= 0 ) + updateRow( lastActItem ); + if ( lastActItem > 0 || + ( !TQT_TQRECT_OBJECT(rect()).tqcontains( e->pos() ) && !tryMenuBar( e ) ) ) { + popupSubMenuLater(tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_SubMenuPopupDelay, + this), this); + } + } else { // mouse on valid item + // but did not register mouse press + if ( (e->state() & Qt::MouseButtonMask) && !mouseBtDn ) + mouseBtDn = TRUE; // so mouseReleaseEvent will pop down + + register TQMenuItem *mi = mitems->at( item ); + + if ( mi->widget() ) { + TQWidget* widgetAt = TQApplication::widgetAt( e->globalPos(), TRUE ); + if ( widgetAt && widgetAt != this ) { + TQMouseEvent me( e->type(), widgetAt->mapFromGlobal( e->globalPos() ), + e->globalPos(), e->button(), e->state() ); + TQApplication::sendEvent( widgetAt, &me ); + } + } + + if ( actItem == item ) + return; + + if ( tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_SloppySubMenus, this) && + d->mouseMoveBuffer.tqcontains( e->pos() ) ) { + actItem = item; + popupSubMenuLater( tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_SubMenuPopupDelay, this) * 6, + this ); + return; + } + + if ( mi->popup() || ( popupActive >= 0 && popupActive != item )) { + popupSubMenuLater( tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_SubMenuPopupDelay, this), + this ); + } + else if ( singleSingleShot ) { + singleSingleShot->stop(); + } + + if ( item != actItem ) { + setActiveItem( item ); + } + } +} + + +/*! + \reimp +*/ + +void TQPopupMenu::keyPressEvent( TQKeyEvent *e ) +{ + /* + I get nothing but complaints about this. -Brad + + - if (mouseBtDn && actItem >= 0) { + - if (e->key() == Qt::Key_Shift || + - e->key() == Qt::Key_Control || + - e->key() == Qt::Key_Alt) + - return; + - + - TQMenuItem *mi = mitems->at(actItem); + - int modifier = (((e->state() & ShiftButton) ? SHIFT : 0) | + - ((e->state() & ControlButton) ? CTRL : 0) | + - ((e->state() & AltButton) ? ALT : 0)); + - + - #ifndef TQT_NO_ACCEL + - if (mi) + - setAccel(modifier + e->key(), mi->id()); + - #endif + - return; + - } + */ + + TQMenuItem *mi = 0; + TQPopupMenu *popup; + int dy = 0; + bool ok_key = TRUE; + + int key = e->key(); + if ( TQApplication::reverseLayout() ) { + // in reverse mode opening and closing keys for submenues are reversed + if ( key == Qt::Key_Left ) + key = Qt::Key_Right; + else if ( key == Qt::Key_Right ) + key = Qt::Key_Left; + } + + switch ( key ) { + case Qt::Key_Tab: + // ignore tab, otherwise it will be passed to the menubar + break; + + case Qt::Key_Up: + dy = -1; + break; + + case Qt::Key_Down: + dy = 1; + break; + + case Qt::Key_Alt: + if ( tqstyle().tqstyleHint(TQStyle::SH_MenuBar_AltKeyNavigation, this) ) + byeMenuBar(); + break; + + case Key_Escape: + if ( tornOff ) { + close(); + return; + } + // just hide one + { + TQMenuData* p = parentMenu; + hide(); +#ifndef TQT_NO_MENUBAR + if ( p && p->isMenuBar ) + ((TQMenuBar*) p)->goodbye( TRUE ); +#endif + } + break; + + case Qt::Key_Left: + if ( ncols > 1 && actItem >= 0 ) { + TQRect r( itemGeometry( actItem ) ); + int newActItem = itemAtPos( TQPoint( r.left() - 1, r.center().y() ) ); + if ( newActItem >= 0 ) { + setActiveItem( newActItem ); + break; + } + } + if ( parentMenu && parentMenu->isPopupMenu ) { + ((TQPopupMenu *)parentMenu)->hidePopups(); + if ( singleSingleShot ) + singleSingleShot->stop(); + break; + } + + ok_key = FALSE; + break; + + case Qt::Key_Right: + if ( actItem >= 0 && ( mi=mitems->at(actItem) )->isEnabledAndVisible() && (popup=mi->popup()) ) { + hidePopups(); + if ( singleSingleShot ) + singleSingleShot->stop(); + // ### The next two lines were switched to fix the problem with the first item of the + // submenu not being highlighted...any reason why they should have been the other way?? + subMenuTimer(); + popup->setFirstItemActive(); + break; + } else if ( actItem == -1 && ( parentMenu && !parentMenu->isMenuBar )) { + dy = 1; + break; + } + if ( ncols > 1 && actItem >= 0 ) { + TQRect r( itemGeometry( actItem ) ); + int newActItem = itemAtPos( TQPoint( r.right() + 1, r.center().y() ) ); + if ( newActItem >= 0 ) { + setActiveItem( newActItem ); + break; + } + } + ok_key = FALSE; + break; + + case Qt::Key_Space: + if (! tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_SpaceActivatesItem, this)) + break; + // for motif, fall through + + case Qt::Key_Return: + case Qt::Key_Enter: + { + if ( actItem < 0 ) + break; +#ifndef TQT_NO_WHATSTHIS + bool b = TQWhatsThis::inWhatsThisMode(); +#else + const bool b = FALSE; +#endif + mi = mitems->at( actItem ); + if ( !mi->isEnabled() && !b ) + break; + popup = mi->popup(); + if ( popup ) { + hidePopups(); + popupSubMenuLater( 20, this ); + popup->setFirstItemActive(); + } else { + actItem = -1; + updateItem( mi->id() ); + byeMenuBar(); + if ( mi->isEnabledAndVisible() || b ) { + active_popup_menu = this; + TQGuardedPtr<TQSignal> signal = mi->signal(); + actSig( mi->id(), b ); + if ( signal && !b ) + signal->activate(); + active_popup_menu = 0; + } + } + } + break; +#ifndef TQT_NO_WHATSTHIS + case Qt::Key_F1: + if ( actItem < 0 || e->state() != ShiftButton) + break; + mi = mitems->at( actItem ); + if ( !mi->whatsThis().isNull() ){ + if ( !TQWhatsThis::inWhatsThisMode() ) + TQWhatsThis::enterWhatsThisMode(); + TQRect r( itemGeometry( actItem) ); + TQWhatsThis::leaveWhatsThisMode( mi->whatsThis(), mapToGlobal( r.bottomLeft()) ); + } + //fall-through! +#endif + default: + ok_key = FALSE; + + } + if ( !ok_key && + ( !e->state() || e->state() == TQt::AltButton || e->state() == ShiftButton ) && + e->text().length()==1 ) { + TQChar c = TQT_TQCHAR(e->text()[0]).upper(); + + TQMenuItemListIt it(*mitems); + TQMenuItem* first = 0; + TQMenuItem* currentSelected = 0; + TQMenuItem* firstAfterCurrent = 0; + + register TQMenuItem *m; + mi = 0; + int indx = 0; + int clashCount = 0; + while ( (m=it.current()) ) { + ++it; + TQString s = m->text(); + if ( !s.isEmpty() ) { + int i = s.tqfind( '&' ); + while ( i >= 0 && i < (int)s.length() - 1 ) { + if ( s[i+1].upper() == c ) { + ok_key = TRUE; + clashCount++; + if ( !first ) + first = m; + if ( indx == actItem ) + currentSelected = m; + else if ( !firstAfterCurrent && currentSelected ) + firstAfterCurrent = m; + break; + } else if ( s[i+1] == '&' ) { + i = s.tqfind( '&', i+2 ); + } else { + break; + } + } + } + if ( mi ) + break; + indx++; + } + + if ( 1 == clashCount ) { // No clashes, continue with selection + mi = first; + popup = mi->popup(); + if ( popup ) { + setActiveItem( indexOf(mi->id()) ); + hidePopups(); + popupSubMenuLater( 20, this ); + popup->setFirstItemActive(); + } else { + byeMenuBar(); +#ifndef TQT_NO_WHATSTHIS + bool b = TQWhatsThis::inWhatsThisMode(); +#else + const bool b = FALSE; +#endif + if ( mi->isEnabledAndVisible() || b ) { + active_popup_menu = this; + TQGuardedPtr<TQSignal> signal = mi->signal(); + actSig( mi->id(), b ); + if ( signal && !b ) + signal->activate(); + active_popup_menu = 0; + } + } + } else if ( clashCount > 1 ) { // Clashes, highlight next... + // If there's clashes and no one is selected, use first one + // or if there is no clashes _after_ current, use first one + if ( !currentSelected || (currentSelected && !firstAfterCurrent)) + dy = indexOf( first->id() ) - actItem; + else + dy = indexOf( firstAfterCurrent->id() ) - actItem; + } + } +#ifndef TQT_NO_MENUBAR + if ( !ok_key ) { // send to menu bar + register TQMenuData *top = this; // tqfind top level + while ( top->parentMenu ) + top = top->parentMenu; + if ( top->isMenuBar ) { + int beforeId = top->actItem; + ((TQMenuBar*)top)->tryKeyEvent( this, e ); + if ( beforeId != top->actItem ) + ok_key = TRUE; + } + } +#endif + if ( actItem < 0 ) { + if ( dy > 0 ) { + setFirstItemActive(); + } else if ( dy < 0 ) { + TQMenuItemListIt it(*mitems); + it.toLast(); + register TQMenuItem *mi; + int ai = count() - 1; + while ( (mi=it.current()) ) { + --it; + if ( !mi->isSeparator() && mi->id() != TQMenuData::d->aInt ) { + setActiveItem( ai ); + return; + } + ai--; + } + actItem = -1; + } + return; + } + + if ( dy ) { // highlight next/prev + register int i = actItem; + int c = mitems->count(); + for(int n = c; n; n--) { + i = i + dy; + if(d->scroll.scrollable) { + if(d->scroll.scrolltimer) + d->scroll.scrolltimer->stop(); + if(i < 0) + i = 0; + else if(i >= c) + i = c - 1; + } else { + if ( i == c ) + i = 0; + else if ( i < 0 ) + i = c - 1; + } + mi = mitems->at( i ); + if ( !mi || !mi->isVisible() ) + continue; + + if ( !mi->isSeparator() && + ( tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_AllowActiveAndDisabled, this) + || mi->isEnabledAndVisible() ) ) + break; + } + if ( i != actItem ) + setActiveItem( i ); + if(d->scroll.scrollable) { //need to scroll to make it visible? + TQRect r = itemGeometry(actItem); + if(r.isNull() || r.height() < itemHeight(mitems->at(actItem))) { + bool refresh = FALSE; + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp && dy == -1) { //up + if(d->scroll.topScrollableIndex >= 0) { + d->scroll.topScrollableIndex--; + refresh = TRUE; + } + } else if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown) { //down + TQMenuItemListIt it(*mitems); + int sh = tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + for(int i = 0, y = ((d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp) ? sh : 0); it.current(); i++, ++it) { + if(i >= d->scroll.topScrollableIndex) { + int itemh = itemHeight(it.current()); + TQSize sz = tqstyle().tqsizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(0, itemh), + TQStyleOption(it.current(),maxPMWidth,0)); + y += sz.height(); + if(y > (contentsRect().height()-sh)) { + if(sz.height() > sh || !it.atLast()) + d->scroll.topScrollableIndex++; + refresh = TRUE; + break; + } + } + } + } + if(refresh) { + updateScrollerState(); + update(); + } + } + } + } + +#ifdef TQ_OS_WIN32 + if ( !ok_key && + !( e->key() == Qt::Key_Control || e->key() == Qt::Key_Shift || e->key() == Qt::Key_Meta ) ) + tqApp->beep(); +#endif // TQ_OS_WIN32 +} + + +/*! + \reimp +*/ + +void TQPopupMenu::timerEvent( TQTimerEvent *e ) +{ + TQFrame::timerEvent( e ); +} + +/*! + \reimp +*/ +void TQPopupMenu::leaveEvent( TQEvent * ) +{ + if ( testWFlags( TQt::WStyle_Tool ) && tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_MouseTracking, this) ) { + int lastActItem = actItem; + actItem = -1; + if ( lastActItem >= 0 ) + updateRow( lastActItem ); + } +} + +/*! + \reimp +*/ +void TQPopupMenu::styleChange( TQStyle& old ) +{ + TQFrame::styleChange( old ); + setMouseTracking(tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_MouseTracking, this)); + tqstyle().polishPopupMenu( this ); + updateSize(TRUE); +} + +/*!\reimp + */ +void TQPopupMenu::enabledChange( bool ) +{ + if ( TQMenuData::d->aWidget ) // torn-off menu + TQMenuData::d->aWidget->setEnabled( isEnabled() ); +} + + +/*! + If a popup menu does not fit on the screen it lays itself out so + that it does fit. It is style dependent what tqlayout means (for + example, on Windows it will use multiple columns). + + This functions returns the number of columns necessary. + + \sa tqsizeHint() +*/ +int TQPopupMenu::columns() const +{ + return ncols; +} + +/* This private slot handles the scrolling popupmenu */ +void TQPopupMenu::subScrollTimer() { + TQPoint pos = TQCursor::pos(); + if(!d->scroll.scrollable || !isVisible()) { + if(d->scroll.scrolltimer) + d->scroll.scrolltimer->stop(); + return; + } else if(pos.x() > x() + width() || pos.x() < x()) { + return; + } + int sh = tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + if(!d->scroll.lastScroll.isValid()) { + d->scroll.lastScroll = TQTime::currentTime(); + } else { + int factor=0; + if(pos.y() < y()) + factor = y() - pos.y(); + else if(pos.y() > y() + height()) + factor = pos.y() - (y() + height()); + int msecs = 250 - ((factor / 10) * 40); + if(d->scroll.lastScroll.msecsTo(TQTime::currentTime()) < TQMAX(0, msecs)) + return; + d->scroll.lastScroll = TQTime::currentTime(); + } + if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp && pos.y() <= y() + sh) { //up + if(d->scroll.topScrollableIndex > 0) { + d->scroll.topScrollableIndex--; + updateScrollerState(); + update(contentsRect()); + } + } else if(d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollDown && + pos.y() >= (y() + contentsRect().height()) - sh) { //down + TQMenuItemListIt it(*mitems); + for(int i = 0, y = contentsRect().y() + sh; it.current(); i++, ++it) { + if(i >= d->scroll.topScrollableIndex) { + int itemh = itemHeight(it.current()); + TQSize sz = tqstyle().tqsizeFromContents(TQStyle::CT_PopupMenuItem, this, TQSize(0, itemh), + TQStyleOption(it.current(),maxPMWidth,0)); + y += sz.height(); + if(y > contentsRect().height() - sh) { + d->scroll.topScrollableIndex++; + updateScrollerState(); + update(contentsRect()); + break; + } + } + } + } +} + +/* This private slot handles the delayed submenu effects */ + +void TQPopupMenu::subMenuTimer() { + if ( !isVisible() || (actItem < 0 && popupActive < 0) || actItem == popupActive ) + return; + + if ( popupActive >= 0 ) { + hidePopups(); + popupActive = -1; + } + + // hidePopups() may change actItem etc. + if ( !isVisible() || actItem < 0 || actItem == popupActive ) + return; + + TQMenuItem *mi = mitems->at(actItem); + if ( !mi || !mi->isEnabledAndVisible() ) + return; + + TQPopupMenu *popup = mi->popup(); + if ( !popup || !popup->isEnabled() ) + return; + + //avoid circularity + if ( popup->isVisible() ) + return; + + TQ_ASSERT( popup->parentMenu == 0 ); + popup->parentMenu = this; // set parent menu + + emit popup->aboutToShow(); + supressAboutToShow = TRUE; + + + TQRect r( itemGeometry( actItem ) ); + TQPoint p; + TQSize ps = popup->tqsizeHint(); + if( TQApplication::reverseLayout() ) { + p = TQPoint( r.left() + motifArrowHMargin - ps.width(), r.top() + motifArrowVMargin ); + p = mapToGlobal( p ); + + bool right = FALSE; + if ( ( parentMenu && parentMenu->isPopupMenu && + ((TQPopupMenu*)parentMenu)->tqgeometry().x() < tqgeometry().x() ) || + p.x() < 0 ) + right = TRUE; + if ( right && (ps.width() > TQApplication::desktop()->width() - mapToGlobal( r.topRight() ).x() ) ) + right = FALSE; + if ( right ) + p.setX( mapToGlobal( r.topRight() ).x() ); + } else { + p = TQPoint( r.right() - motifArrowHMargin, r.top() + motifArrowVMargin ); + p = mapToGlobal( p ); + + bool left = FALSE; + if ( ( parentMenu && parentMenu->isPopupMenu && + ((TQPopupMenu*)parentMenu)->tqgeometry().x() > tqgeometry().x() ) || + p.x() + ps.width() > TQApplication::desktop()->width() ) + left = TRUE; + if ( left && (ps.width() > mapToGlobal( r.topLeft() ).x() ) ) + left = FALSE; + if ( left ) + p.setX( mapToGlobal( r.topLeft() ).x() - ps.width() ); + } + TQRect pr = popup->itemGeometry(popup->count() - 1); + if (p.y() + ps.height() > TQApplication::desktop()->height() && + p.y() - ps.height() + (TQCOORD) pr.height() >= 0) + p.setY( p.y() - ps.height() + (TQCOORD) pr.height()); + + if ( tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_SloppySubMenus, this )) { + TQPoint cur = TQCursor::pos(); + if ( r.tqcontains( mapFromGlobal( cur ) ) ) { + TQPoint pts[4]; + pts[0] = TQPoint( cur.x(), cur.y() - 2 ); + pts[3] = TQPoint( cur.x(), cur.y() + 2 ); + if ( p.x() >= cur.x() ) { + pts[1] = TQPoint( tqgeometry().right(), p.y() ); + pts[2] = TQPoint( tqgeometry().right(), p.y() + ps.height() ); + } else { + pts[1] = TQPoint( p.x() + ps.width(), p.y() ); + pts[2] = TQPoint( p.x() + ps.width(), p.y() + ps.height() ); + } + TQPointArray points( 4 ); + for( int i = 0; i < 4; i++ ) + points.setPoint( i, mapFromGlobal( pts[i] ) ); + d->mouseMoveBuffer = TQRegion( points ); + tqrepaint(); + } + } + + popupActive = actItem; + popup->popup( p ); +} + +void TQPopupMenu::allowAnimation() +{ + preventAnimation = FALSE; +} + +void TQPopupMenu::updateRow( int row ) +{ + if ( !isVisible() ) + return; + + if ( badSize ) { + updateSize(); + update(); + return; + } + updateSize(); + TQRect r = itemGeometry( row ); + if ( !r.isNull() ) // can happen via the scroller + tqrepaint( r ); +} + + +/*! + \overload + + Executes this popup synchronously. + + Opens the popup menu so that the item number \a indexAtPoint will + be at the specified \e global position \a pos. To translate a + widget's local coordinates into global coordinates, use + TQWidget::mapToGlobal(). + + The return code is the id of the selected item in either the popup + menu or one of its submenus, or -1 if no item is selected + (normally because the user pressed Esc). + + Note that all Q_SIGNALS are emitted as usual. If you connect a menu + item to a slot and call the menu's exec(), you get the result both + via the signal-slot connection and in the return value of exec(). + + Common usage is to position the popup at the current mouse + position: + \code + exec( TQCursor::pos() ); + \endcode + or aligned to a widget: + \code + exec( somewidget.mapToGlobal(TQPoint(0, 0)) ); + \endcode + + When positioning a popup with exec() or popup(), bear in mind that + you cannot rely on the popup menu's current size(). For + performance reasons, the popup adapts its size only when + necessary. So in many cases, the size before and after the show is + different. Instead, use tqsizeHint(). It calculates the proper size + depending on the menu's current contents. + + \sa popup(), tqsizeHint() +*/ + +int TQPopupMenu::exec( const TQPoint & pos, int indexAtPoint ) +{ + snapToMouse = TRUE; + if ( !tqApp ) + return -1; + + TQPopupMenu* priorSyncMenu = syncMenu; + + syncMenu = this; + syncMenuId = -1; + + TQGuardedPtr<TQPopupMenu> that = this; + connectModal( that, TRUE ); + popup( pos, indexAtPoint ); + tqApp->enter_loop(); + connectModal( that, FALSE ); + + syncMenu = priorSyncMenu; + return syncMenuId; +} + + + +/* + Connect the popup and all its submenus to modalActivation() if + \a doConnect is true, otherwise disconnect. + */ +void TQPopupMenu::connectModal( TQPopupMenu* receiver, bool doConnect ) +{ + if ( !receiver ) + return; + + connectModalRecursionSafety = doConnect; + + if ( doConnect ) + connect( this, TQT_SIGNAL(activated(int)), + receiver, TQT_SLOT(modalActivation(int)) ); + else + disconnect( this, TQT_SIGNAL(activated(int)), + receiver, TQT_SLOT(modalActivation(int)) ); + + TQMenuItemListIt it(*mitems); + register TQMenuItem *mi; + while ( (mi=it.current()) ) { + ++it; + if ( mi->popup() && mi->popup() != receiver + && (bool)(mi->popup()->connectModalRecursionSafety) != doConnect ) + mi->popup()->connectModal( receiver, doConnect ); //avoid circular + } +} + + +/*! + Executes this popup synchronously. + + This is equivalent to \c{exec(mapToGlobal(TQPoint(0,0)))}. In most + situations you'll want to specify the position yourself, for + example at the current mouse position: + \code + exec(TQCursor::pos()); + \endcode + or aligned to a widget: + \code + exec(somewidget.mapToGlobal(TQPoint(0,0))); + \endcode +*/ + +int TQPopupMenu::exec() +{ + return exec(mapToGlobal(TQPoint(0,0))); +} + + +/* Internal slot used for exec(). */ + +void TQPopupMenu::modalActivation( int id ) +{ + syncMenuId = id; +} + + +/*! + Sets the currently active item to index \a i and repaints as necessary. +*/ + +void TQPopupMenu::setActiveItem( int i ) +{ + int lastActItem = actItem; + actItem = i; + if ( lastActItem >= 0 ) + updateRow( lastActItem ); + if ( i >= 0 && i != lastActItem ) + updateRow( i ); + TQMenuItem *mi = mitems->at( actItem ); + if ( !mi ) + return; + + if ( mi->widget() && mi->widget()->isFocusEnabled() ) { + mi->widget()->setFocus(); + } else { + setFocus(); + TQRect mfrect = itemGeometry( actItem ); + setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE ); + } + if ( mi->id() != -1 ) + hilitSig( mi->id() ); +#ifndef TQT_NO_WHATSTHIS + if (whatsThisItem && whatsThisItem != mi) { + qWhatsThisBDH(); + } + whatsThisItem = mi; +#endif +} + + +/*! + \reimp +*/ +TQSize TQPopupMenu::tqsizeHint() const +{ + constPolish(); + TQPopupMenu* that = (TQPopupMenu*) this; + //We do not need a resize here, just the tqsizeHint.. + return that->updateSize(FALSE, FALSE).expandedTo( TQApplication::globalStrut() ); +} + + +/*! + \overload + + Returns the id of the item at \a pos, or -1 if there is no item + there or if it is a separator. +*/ +int TQPopupMenu::idAt( const TQPoint& pos ) const +{ + return idAt( itemAtPos( pos ) ); +} + + +/*! + \fn int TQPopupMenu::idAt( int index ) const + + Returns the identifier of the menu item at position \a index in + the internal list, or -1 if \a index is out of range. + + \sa TQMenuData::setId(), TQMenuData::indexOf() +*/ + + +/*! + \reimp + */ +bool TQPopupMenu::customWhatsThis() const +{ + return TRUE; +} + + +/*! + \reimp + */ +bool TQPopupMenu::focusNextPrevChild( bool next ) +{ + register TQMenuItem *mi; + int dy = next? 1 : -1; + if ( dy && actItem < 0 ) { + setFirstItemActive(); + } else if ( dy ) { // highlight next/prev + register int i = actItem; + int c = mitems->count(); + int n = c; + while ( n-- ) { + i = i + dy; + if ( i == c ) + i = 0; + else if ( i < 0 ) + i = c - 1; + mi = mitems->at( i ); + if ( mi && !mi->isSeparator() && + ( ( tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_AllowActiveAndDisabled, this) + && mi->isVisible() ) + || mi->isEnabledAndVisible() ) ) + break; + } + if ( i != actItem ) + setActiveItem( i ); + } + return TRUE; +} + + +/*! + \reimp + */ +void TQPopupMenu::focusInEvent( TQFocusEvent * ) +{ +} + +/*! + \reimp + */ +void TQPopupMenu::focusOutEvent( TQFocusEvent * ) +{ +} + + +class TQTearOffMenuItem : public TQCustomMenuItem +{ +public: + TQTearOffMenuItem() + { + } + ~TQTearOffMenuItem() + { + } + void paint( TQPainter* p, const TQColorGroup& cg, bool /* act*/, + bool /*enabled*/, int x, int y, int w, int h ) + { + p->setPen( TQPen( cg.dark(), 1, DashLine ) ); + p->drawLine( x+2, y+h/2-1, x+w-4, y+h/2-1 ); + p->setPen( TQPen( cg.light(), 1, DashLine ) ); + p->drawLine( x+2, y+h/2, x+w-4, y+h/2 ); + } + bool fullSpan() const + { + return TRUE; + } + + TQSize tqsizeHint() + { + return TQSize( 20, 6 ); + } +}; + + + +/*! + Inserts a tear-off handle into the menu. A tear-off handle is a + special menu item that creates a copy of the menu when the menu is + selected. This "torn-off" copy lives in a separate window. It + tqcontains the same menu items as the original menu, with the + exception of the tear-off handle. + + The handle item is assigned the identifier \a id or an + automatically generated identifier if \a id is < 0. The generated + identifiers (negative integers) are guaranteed to be unique within + the entire application. + + The \a index specifies the position in the menu. The tear-off + handle is appended at the end of the list if \a index is negative. +*/ +int TQPopupMenu::insertTearOffHandle( int id, int index ) +{ + int myid = insertItem( new TQTearOffMenuItem, id, index ); + connectItem( myid, TQT_TQOBJECT(this), TQT_SLOT( toggleTearOff() ) ); + TQMenuData::d->aInt = myid; + return myid; +} + + +/*!\internal + + implements tear-off menus + */ +void TQPopupMenu::toggleTearOff() +{ + if ( active_popup_menu && active_popup_menu->tornOff ) { + active_popup_menu->close(); + } else if (TQMenuData::d->aWidget ) { + delete (TQWidget*) TQMenuData::d->aWidget; // delete the old one + } else { + // create a tear off menu + TQPopupMenu* p = new TQPopupMenu( parentWidget(), "tear off menu" ); + connect( p, TQT_SIGNAL( activated(int) ), this, TQT_SIGNAL( activated(int) ) ); + connect( p, TQT_SIGNAL( highlighted(int) ), this, TQT_SIGNAL( highlighted(int) ) ); +#ifndef TQT_NO_WIDGET_TOPEXTRA + p->setCaption( caption() ); +#endif + p->setCheckable( isCheckable() ); + p->reparent( parentWidget(), (WFlags)(TQt::WType_TopLevel | TQt::WStyle_Tool | + TQt::WNoAutoErase | TQt::WDestructiveClose), + tqgeometry().topLeft(), FALSE ); + p->mitems->setAutoDelete( FALSE ); + p->tornOff = TRUE; + for ( TQMenuItemListIt it( *mitems ); it.current(); ++it ) { + if ( it.current()->id() != TQMenuData::d->aInt && !it.current()->widget() ) + p->mitems->append( it.current() ); + } + p->show(); + TQMenuData::d->aWidget = p; + } +} + +/*! + \reimp + */ +void TQPopupMenu::activateItemAt( int index ) +{ + if ( index >= 0 && index < (int) mitems->count() ) { + TQMenuItem *mi = mitems->at( index ); + if ( index != actItem ) // new item activated + setActiveItem( index ); + TQPopupMenu *popup = mi->popup(); + if ( popup ) { + if ( popup->isVisible() ) { // sub menu already open + int pactItem = popup->actItem; + popup->actItem = -1; + popup->hidePopups(); + popup->updateRow( pactItem ); + } else { // open sub menu + hidePopups(); + actItem = index; + subMenuTimer(); + popup->setFirstItemActive(); + } + } else { + byeMenuBar(); // deactivate menu bar + +#ifndef TQT_NO_WHATSTHIS + bool b = TQWhatsThis::inWhatsThisMode(); +#else + const bool b = FALSE; +#endif + if ( !mi->isEnabledAndVisible() ) { +#ifndef TQT_NO_WHATSTHIS + if ( b ) { + actItem = -1; + updateItem( mi->id() ); + byeMenuBar(); + actSig( mi->id(), b); + } +#endif + } else { + byeMenuBar(); // deactivate menu bar + if ( mi->isEnabledAndVisible() ) { + actItem = -1; + updateItem( mi->id() ); + active_popup_menu = this; + TQGuardedPtr<TQSignal> signal = mi->signal(); + actSig( mi->id(), b ); + if ( signal && !b ) + signal->activate(); + active_popup_menu = 0; + } + } + } + } else { + if ( tornOff ) { + close(); + } else { + TQMenuData* p = parentMenu; + hide(); +#ifndef TQT_NO_MENUBAR + if ( p && p->isMenuBar ) + ((TQMenuBar*) p)->goodbye( TRUE ); +#endif + } + } + +} + +/*! \internal + This private function is to update the scroll states in styles that support scrolling. */ +void +TQPopupMenu::updateScrollerState() +{ + uint old_scrollable = d->scroll.scrollable; + d->scroll.scrollable = TQPopupMenuPrivate::Scroll::ScrollNone; + if(!tqstyle().tqstyleHint(TQStyle::SH_PopupMenu_Scrollable, this)) + return; + + TQMenuItem *mi; + TQMenuItemListIt it( *mitems ); + if(d->scroll.topScrollableIndex) { + for(int row = 0; (mi = it.current()) && row < d->scroll.topScrollableIndex; row++) + ++it; + if(!mi) + it.toFirst(); + } + int y = 0, sh = tqstyle().tqpixelMetric(TQStyle::PM_PopupMenuScrollerHeight, this); + if(!it.atFirst()) { + // can't use |= because of a bug/feature in IBM xlC 5.0.2 + d->scroll.scrollable = d->scroll.scrollable | TQPopupMenuPrivate::Scroll::ScrollUp; + y += sh; + } + while ( (mi=it.current()) ) { + ++it; + int myheight = contentsRect().height(); + TQSize sz = tqstyle().tqsizeFromContents(TQStyle::CT_PopupMenuItem, this, + TQSize(0, itemHeight( mi )), + TQStyleOption(mi,maxPMWidth)); + if(y + sz.height() >= myheight) { + d->scroll.scrollable = d->scroll.scrollable | TQPopupMenuPrivate::Scroll::ScrollDown; + break; + } + y += sz.height(); + } + if((d->scroll.scrollable & TQPopupMenuPrivate::Scroll::ScrollUp) && + !(old_scrollable & TQPopupMenuPrivate::Scroll::ScrollUp)) + d->scroll.topScrollableIndex++; +} + +#endif // TQT_NO_POPUPMENU + |