|
|
|
/****************************************************************************
|
|
|
|
**
|
|
|
|
** Implementation of QPopupMenu class
|
|
|
|
**
|
|
|
|
** Created : 941128
|
|
|
|
**
|
|
|
|
** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
|
|
|
|
**
|
|
|
|
** This file is part of the widgets module of the Qt 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 Qt 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.QPL
|
|
|
|
** included in the packaging of this file. Licensees holding valid Qt
|
|
|
|
** Commercial licenses may use this file in accordance with the Qt
|
|
|
|
** 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 "qpopupmenu.h"
|
|
|
|
#ifndef QT_NO_POPUPMENU
|
|
|
|
#include "qmenubar.h"
|
|
|
|
#include "qaccel.h"
|
|
|
|
#include "qpainter.h"
|
|
|
|
#include "qdrawutil.h"
|
|
|
|
#include "qapplication.h"
|
|
|
|
#include "qpixmap.h"
|
|
|
|
#include "qpixmapcache.h"
|
|
|
|
#include "qtimer.h"
|
|
|
|
#include "qwhatsthis.h"
|
|
|
|
#include "qobjectlist.h"
|
|
|
|
#include "qguardedptr.h"
|
|
|
|
#include "qeffects_p.h"
|
|
|
|
#include "qcursor.h"
|
|
|
|
#include "qstyle.h"
|
|
|
|
#include "qtimer.h"
|
|
|
|
#include "qdatetime.h"
|
|
|
|
#if defined(QT_ACCESSIBILITY_SUPPORT)
|
|
|
|
#include "qaccessible.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
|
|
|
|
|
|
|
|
static const int gtkArrowHMargin = 0; // arrow horizontal margin
|
|
|
|
static const int gtkArrowVMargin = 0; // arrow vertical margin
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
|
|
|
+-----------------------------
|
|
|
|
| PopupFrame
|
|
|
|
| +-------------------------
|
|
|
|
| | ItemFrame
|
|
|
|
| | +---------------------
|
|
|
|
| | |
|
|
|
|
| | | \
|
|
|
|
| | | ^ T E X T ^ | ItemVMargin
|
|
|
|
| | | | | /
|
|
|
|
| | ItemHMargin
|
|
|
|
|
|
|
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
# define DEBUG_SLOPPY_SUBMENU
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// used for internal communication
|
|
|
|
static QPopupMenu * 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 QTimer * singleSingleShot = 0;
|
|
|
|
|
|
|
|
static bool supressAboutToShow = FALSE;
|
|
|
|
|
|
|
|
static void cleanup()
|
|
|
|
{
|
|
|
|
delete singleSingleShot;
|
|
|
|
singleSingleShot = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void popupSubMenuLater( int msec, QPopupMenu * receiver ) {
|
|
|
|
if ( !singleSingleShot ) {
|
|
|
|
singleSingleShot = new QTimer( qApp, "popup submenu timer" );
|
|
|
|
qAddPostRoutine( cleanup );
|
|
|
|
}
|
|
|
|
|
|
|
|
singleSingleShot->disconnect( SIGNAL(timeout()) );
|
|
|
|
QObject::connect( singleSingleShot, SIGNAL(timeout()),
|
|
|
|
receiver, SLOT(subMenuTimer()) );
|
|
|
|
singleSingleShot->start( msec, TRUE );
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool preventAnimation = FALSE;
|
|
|
|
|
|
|
|
#ifndef QT_NO_WHATSTHIS
|
|
|
|
extern void qWhatsThisBDH();
|
|
|
|
static QMenuItem* whatsThisItem = 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\class QPopupMenu qpopupmenu.h
|
|
|
|
\brief The QPopupMenu 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
|
|
|
|
QMenuBar::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
|
|
|
|
QCustomMenuItem). 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 QPopupMenu 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, QPopupMenu provides two 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 QWhatsThis 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 QPopupMenu can also provide a tear-off menu. A tear-off menu is
|
|
|
|
a top-level window that contains 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 QToolBar
|
|
|
|
instead. Tear-off menus cannot contain custom widgets; if the
|
|
|
|
original menu contains a custom widget item, this item is omitted.
|
|
|
|
|
|
|
|
\link menu-example.html menu/menu.cpp\endlink is an example of
|
|
|
|
QMenuBar and QPopupMenu 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 QMenuBar
|
|
|
|
\link guibooks.html#fowler GUI Design Handbook: Menu, Drop-Down and
|
|
|
|
Pop-Up\endlink
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn void QPopupMenu::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 QPopupMenu::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()
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
QPopupMenu member functions
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
class QMenuDataData {
|
|
|
|
// attention: also defined in qmenudata.cpp
|
|
|
|
public:
|
|
|
|
QMenuDataData();
|
|
|
|
QGuardedPtr<QWidget> aWidget;
|
|
|
|
int aInt;
|
|
|
|
};
|
|
|
|
|
|
|
|
class QPopupMenuPrivate {
|
|
|
|
public:
|
|
|
|
struct Scroll {
|
|
|
|
enum { ScrollNone=0, ScrollUp=0x01, ScrollDown=0x02 };
|
|
|
|
uint scrollable : 2;
|
|
|
|
uint direction : 1;
|
|
|
|
int topScrollableIndex, scrollableSize;
|
|
|
|
QTime lastScroll;
|
|
|
|
QTimer *scrolltimer;
|
|
|
|
} scroll;
|
|
|
|
QSize calcSize;
|
|
|
|
QRegion mouseMoveBuffer;
|
|
|
|
uint hasmouse : 1;
|
|
|
|
QPoint ignoremousepos;
|
|
|
|
};
|
|
|
|
|
|
|
|
static QPopupMenu* active_popup_menu = 0;
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Constructs a popup menu called \a name with parent \a parent.
|
|
|
|
|
|
|
|
Although a popup menu is always a top-level widget, if a parent is
|
|
|
|
passed the popup menu will be deleted when that parent is
|
|
|
|
destroyed (as with any other QObject).
|
|
|
|
*/
|
|
|
|
|
|
|
|
QPopupMenu::QPopupMenu( QWidget *parent, const char *name )
|
|
|
|
: QFrame( parent, name, WType_Popup | WNoAutoErase )
|
|
|
|
{
|
|
|
|
d = new QPopupMenuPrivate;
|
|
|
|
d->scroll.scrollableSize = d->scroll.topScrollableIndex = 0;
|
|
|
|
d->scroll.scrollable = QPopupMenuPrivate::Scroll::ScrollNone;
|
|
|
|
d->scroll.scrolltimer = 0;
|
|
|
|
d->hasmouse = 0;
|
|
|
|
isPopupMenu = TRUE;
|
|
|
|
#ifndef QT_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( QFrame::PopupPanel | QFrame::Raised );
|
|
|
|
setMouseTracking(style().styleHint(QStyle::SH_PopupMenu_MouseTracking, this));
|
|
|
|
style().polishPopupMenu( this );
|
|
|
|
setBackgroundMode( PaletteButton );
|
|
|
|
connectModalRecursionSafety = 0;
|
|
|
|
|
|
|
|
setFocusPolicy( StrongFocus );
|
|
|
|
#ifdef Q_WS_X11
|
|
|
|
x11SetWindowType( X11WindowTypePopup );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Destroys the popup menu.
|
|
|
|
*/
|
|
|
|
|
|
|
|
QPopupMenu::~QPopupMenu()
|
|
|
|
{
|
|
|
|
if ( syncMenu == this && qApp ) {
|
|
|
|
qApp->exit_loop();
|
|
|
|
syncMenu = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(d->scroll.scrolltimer)
|
|
|
|
delete d->scroll.scrolltimer;
|
|
|
|
|
|
|
|
if ( isVisible() ) {
|
|
|
|
parentMenu = 0;
|
|
|
|
hidePopups();
|
|
|
|
}
|
|
|
|
|
|
|
|
delete (QWidget*) QMenuData::d->aWidget; // tear-off menu
|
|
|
|
|
|
|
|
preventAnimation = FALSE;
|
|
|
|
delete d;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Updates the item with identity \a id.
|
|
|
|
*/
|
|
|
|
void QPopupMenu::updateItem( int id ) // update popup menu item
|
|
|
|
{
|
|
|
|
updateRow( indexOf(id) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void QPopupMenu::setCheckable( bool enable )
|
|
|
|
{
|
|
|
|
if ( isCheckable() != enable ) {
|
|
|
|
checkable = enable;
|
|
|
|
badSize = TRUE;
|
|
|
|
if ( QMenuData::d->aWidget )
|
|
|
|
( (QPopupMenu*)(QWidget*)QMenuData::d->aWidget)->setCheckable( enable );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\property QPopupMenu::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 QMenuData::setItemChecked()
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool QPopupMenu::isCheckable() const
|
|
|
|
{
|
|
|
|
return checkable;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QPopupMenu::menuContentsChanged()
|
|
|
|
{
|
|
|
|
// here the part that can't be delayed
|
|
|
|
QMenuData::menuContentsChanged();
|
|
|
|
badSize = TRUE; // might change the size
|
|
|
|
#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE)
|
|
|
|
mac_dirty_popup = 1;
|
|
|
|
#endif
|
|
|
|
if( pendingDelayedContentsChanges )
|
|
|
|
return;
|
|
|
|
pendingDelayedContentsChanges = 1;
|
|
|
|
if( !pendingDelayedStateChanges ) // if the timer hasn't been started yet
|
|
|
|
QTimer::singleShot( 0, this, SLOT(performDelayedChanges()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void QPopupMenu::performDelayedContentsChanged()
|
|
|
|
{
|
|
|
|
pendingDelayedContentsChanges = 0;
|
|
|
|
// here the part the can be delayed
|
|
|
|
#ifndef QT_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();
|
|
|
|
}
|
|
|
|
QPopupMenu* p = (QPopupMenu*)(QWidget*)QMenuData::d->aWidget;
|
|
|
|
if ( p && p->isVisible() ) {
|
|
|
|
p->updateSize(TRUE);
|
|
|
|
p->update();
|
|
|
|
}
|
|
|
|
#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE)
|
|
|
|
mac_dirty_popup = 1;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void QPopupMenu::menuStateChanged()
|
|
|
|
{
|
|
|
|
// here the part that can't be delayed
|
|
|
|
if( pendingDelayedStateChanges )
|
|
|
|
return;
|
|
|
|
pendingDelayedStateChanges = 1;
|
|
|
|
if( !pendingDelayedContentsChanges ) // if the timer hasn't been started yet
|
|
|
|
QTimer::singleShot( 0, this, SLOT(performDelayedChanges()));
|
|
|
|
}
|
|
|
|
|
|
|
|
void QPopupMenu::performDelayedStateChanged()
|
|
|
|
{
|
|
|
|
pendingDelayedStateChanges = 0;
|
|
|
|
// here the part that can be delayed
|
|
|
|
#ifndef QT_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 ( QMenuData::d->aWidget )
|
|
|
|
QMenuData::d->aWidget->update();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QPopupMenu::performDelayedChanges()
|
|
|
|
{
|
|
|
|
if( pendingDelayedContentsChanges )
|
|
|
|
performDelayedContentsChanged();
|
|
|
|
if( pendingDelayedStateChanges )
|
|
|
|
performDelayedStateChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QPopupMenu::menuInsPopup( QPopupMenu *popup )
|
|
|
|
{
|
|
|
|
connect( popup, SIGNAL(activatedRedirect(int)),
|
|
|
|
SLOT(subActivated(int)) );
|
|
|
|
connect( popup, SIGNAL(highlightedRedirect(int)),
|
|
|
|
SLOT(subHighlighted(int)) );
|
|
|
|
connect( popup, SIGNAL(destroyed(QObject*)),
|
|
|
|
this, SLOT(popupDestroyed(QObject*)) );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QPopupMenu::menuDelPopup( QPopupMenu *popup )
|
|
|
|
{
|
|
|
|
popup->disconnect( SIGNAL(activatedRedirect(int)) );
|
|
|
|
popup->disconnect( SIGNAL(highlightedRedirect(int)) );
|
|
|
|
disconnect( popup, SIGNAL(destroyed(QObject*)),
|
|
|
|
this, SLOT(popupDestroyed(QObject*)) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void QPopupMenu::frameChanged()
|
|
|
|
{
|
|
|
|
menuContentsChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
QRect QPopupMenu::screenRect( const QPoint& pos )
|
|
|
|
{
|
|
|
|
int screen_num = QApplication::desktop()->screenNumber( pos );
|
|
|
|
#ifdef Q_WS_MAC
|
|
|
|
return QApplication::desktop()->availableGeometry( screen_num );
|
|
|
|
#else
|
|
|
|
return QApplication::desktop()->screenGeometry( screen_num );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
/*!
|
|
|
|
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
|
|
|
|
QWidget::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 sizeHint(). It calculates the proper size
|
|
|
|
depending on the menu's current contents.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void QPopupMenu::popup( const QPoint &pos, int indexAtPoint )
|
|
|
|
{
|
|
|
|
if ( !isPopup() && isVisible() )
|
|
|
|
hide();
|
|
|
|
|
|
|
|
//avoid circularity
|
|
|
|
if ( isVisible() || !isEnabled() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE)
|
|
|
|
if( macPopupMenu(pos, indexAtPoint ))
|
|
|
|
return;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if (QT_VERSION-0 >= 0x040000)
|
|
|
|
#error "Fix this now"
|
|
|
|
// #### should move to QWidget - anything might need this functionality,
|
|
|
|
// #### since anything can have WType_Popup window flag.
|
|
|
|
// #### This includes stuff in QPushButton and some stuff for setting
|
|
|
|
// #### the geometry of QDialog.
|
|
|
|
// QPopupMenu
|
|
|
|
// ::exec()
|
|
|
|
// ::popup()
|
|
|
|
// QPushButton (shouldn't require QMenuPopup)
|
|
|
|
// ::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
|
|
|
|
|
|
|
|
QRect screen = screenRect( geometry().center());
|
|
|
|
QRect screen2 = screenRect( QApplication::reverseLayout()
|
|
|
|
? pos+QPoint(width(),0) : pos );
|
|
|
|
// if the widget is not in the screen given by the position, move it
|
|
|
|
// there, so that updateSize() uses the right size of the screen
|
|
|
|
if( screen != screen2 ) {
|
|
|
|
screen = screen2;
|
|
|
|
move( screen.x(), screen.y());
|
|
|
|
}
|
|
|
|
if(d->scroll.scrollable) {
|
|
|
|
d->scroll.scrollable = QPopupMenuPrivate::Scroll::ScrollNone;
|
|
|
|
d->scroll.topScrollableIndex = d->scroll.scrollableSize = 0;
|
|
|
|
badSize = TRUE;
|
|
|
|
}
|
|
|
|
updateSize();
|
|
|
|
|
|
|
|
QPoint mouse = QCursor::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 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 ( qApp->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;
|
|
|
|
}
|
|
|
|
#ifdef Q_WS_X11
|
|
|
|
#ifndef QT_NO_MENUBAR
|
|
|
|
QMenuData *top = this; // find top level
|
|
|
|
while ( top->parentMenu )
|
|
|
|
top = top->parentMenu;
|
|
|
|
if( top->isMenuBar )
|
|
|
|
x11SetWindowType( X11WindowTypeDropdown );
|
|
|
|
if( parentMenu && parentMenu->isMenuBar )
|
|
|
|
x11SetWindowTransient( static_cast< QMenuBar* >( parentMenu )->topLevelWidget());
|
|
|
|
#endif
|
|
|
|
if( parentMenu && !parentMenu->isMenuBar )
|
|
|
|
x11SetWindowTransient( static_cast< QPopupMenu* >( parentMenu ));
|
|
|
|
if( !parentMenu ) {
|
|
|
|
// hackish ... try to find the main window related to this popup
|
|
|
|
QWidget* parent = parentWidget() ? parentWidget()->topLevelWidget() : NULL;
|
|
|
|
if( parent == NULL )
|
|
|
|
parent = QApplication::widgetAt( pos );
|
|
|
|
if( parent == NULL )
|
|
|
|
parent = qApp->activeWindow();
|
|
|
|
if( parent != NULL )
|
|
|
|
x11SetWindowTransient( parent );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
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(style().styleHint(QStyle::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 = style().pixelMetric(QStyle::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 | QPopupMenuPrivate::Scroll::ScrollUp;
|
|
|
|
}
|
|
|
|
if( off_bottom )
|
|
|
|
d->scroll.scrollable = d->scroll.scrollable | QPopupMenuPrivate::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 QMenuItem *mi = NULL;
|
|
|
|
QMenuItemListIt it(*mitems);
|
|
|
|
for(int tmp_y = 0; tmp_y < off_top && (mi=it.current()); ) {
|
|
|
|
QSize sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
|
|
|
QSize(0, itemHeight( mi )),
|
|
|
|
QStyleOption(mi,maxPMWidth,0));
|
|
|
|
tmp_y += sz.height();
|
|
|
|
d->scroll.topScrollableIndex++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
move( x, y );
|
|
|
|
motion=0;
|
|
|
|
actItem = -1;
|
|
|
|
|
|
|
|
#ifndef QT_NO_EFFECTS
|
|
|
|
int hGuess = qApp->reverseLayout() ? QEffects::LeftScroll : QEffects::RightScroll;
|
|
|
|
int vGuess = QEffects::DownScroll;
|
|
|
|
if ( qApp->reverseLayout() ) {
|
|
|
|
if ( ( snapToMouse && ( x + w/2 > mouse.x() ) ) ||
|
|
|
|
( parentMenu && parentMenu->isPopupMenu &&
|
|
|
|
( x + w/2 > ((QPopupMenu*)parentMenu)->x() ) ) )
|
|
|
|
hGuess = QEffects::RightScroll;
|
|
|
|
} else {
|
|
|
|
if ( ( snapToMouse && ( x + w/2 < mouse.x() ) ) ||
|
|
|
|
( parentMenu && parentMenu->isPopupMenu &&
|
|
|
|
( x + w/2 < ((QPopupMenu*)parentMenu)->x() ) ) )
|
|
|
|
hGuess = QEffects::LeftScroll;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef QT_NO_MENUBAR
|
|
|
|
if ( ( snapToMouse && ( y + h/2 < mouse.y() ) ) ||
|
|
|
|
( parentMenu && parentMenu->isMenuBar &&
|
|
|
|
( y + h/2 < ((QMenuBar*)parentMenu)->mapToGlobal( ((QMenuBar*)parentMenu)->pos() ).y() ) ) )
|
|
|
|
vGuess = QEffects::UpScroll;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if ( QApplication::isEffectEnabled( UI_AnimateMenu ) &&
|
|
|
|
preventAnimation == FALSE ) {
|
|
|
|
if ( QApplication::isEffectEnabled( UI_FadeMenu ) )
|
|
|
|
qFadeEffect( this );
|
|
|
|
else if ( parentMenu )
|
|
|
|
qScrollEffect( this, parentMenu->isPopupMenu ? hGuess : vGuess );
|
|
|
|
else
|
|
|
|
qScrollEffect( this, hGuess | vGuess );
|
|
|
|
} else
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
show();
|
|
|
|
}
|
|
|
|
#if defined(QT_ACCESSIBILITY_SUPPORT)
|
|
|
|
QAccessible::updateAccessibility( this, 0, QAccessible::PopupMenuStart );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn void QPopupMenu::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
|
|
|
|
QMenuData::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(), QMenuData::insertItem()
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn void QPopupMenu::highlighted( int id )
|
|
|
|
|
|
|
|
This signal is emitted when a menu item is highlighted; \a id is
|
|
|
|
the id of the highlighted item.
|
|
|
|
|
|
|
|
\sa activated(), QMenuData::insertItem()
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \fn void QPopupMenu::highlightedRedirect( int id )
|
|
|
|
\internal
|
|
|
|
Used internally to connect submenus to their parents.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*! \fn void QPopupMenu::activatedRedirect( int id )
|
|
|
|
\internal
|
|
|
|
Used internally to connect submenus to their parents.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void QPopupMenu::subActivated( int id )
|
|
|
|
{
|
|
|
|
emit activatedRedirect( id );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QPopupMenu::subHighlighted( int id )
|
|
|
|
{
|
|
|
|
emit highlightedRedirect( id );
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool fromAccel = FALSE;
|
|
|
|
|
|
|
|
#ifndef QT_NO_ACCEL
|
|
|
|
void QPopupMenu::accelActivated( int id )
|
|
|
|
{
|
|
|
|
QMenuItem *mi = findItem( id );
|
|
|
|
if ( mi && mi->isEnabledAndVisible() ) {
|
|
|
|
QGuardedPtr<QSignal> signal = mi->signal();
|
|
|
|
fromAccel = TRUE;
|
|
|
|
actSig( mi->id() );
|
|
|
|
fromAccel = FALSE;
|
|
|
|
if ( signal )
|
|
|
|
signal->activate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QPopupMenu::accelDestroyed() // accel about to be deleted
|
|
|
|
{
|
|
|
|
autoaccel = 0; // don't delete it twice!
|
|
|
|
}
|
|
|
|
#endif //QT_NO_ACCEL
|
|
|
|
|
|
|
|
void QPopupMenu::popupDestroyed( QObject *o )
|
|
|
|
{
|
|
|
|
removePopup( (QPopupMenu*)o );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QPopupMenu::actSig( int id, bool inwhatsthis )
|
|
|
|
{
|
|
|
|
if ( !inwhatsthis ) {
|
|
|
|
emit activated( id );
|
|
|
|
#if defined(QT_ACCESSIBILITY_SUPPORT)
|
|
|
|
if ( !fromAccel )
|
|
|
|
QAccessible::updateAccessibility( this, indexOf(id)+1, QAccessible::MenuCommand );
|
|
|
|
#endif
|
|
|
|
} else {
|
|
|
|
#ifndef QT_NO_WHATSTHIS
|
|
|
|
QRect r( itemGeometry( indexOf( id ) ) );
|
|
|
|
QPoint p( r.center().x(), r.bottom() );
|
|
|
|
QString whatsThis = findItem( id )->whatsThis();
|
|
|
|
if ( whatsThis.isNull() )
|
|
|
|
whatsThis = QWhatsThis::textFor( this, p );
|
|
|
|
QWhatsThis::leaveWhatsThisMode( whatsThis, mapToGlobal( p ), this );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
emit activatedRedirect( id );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QPopupMenu::hilitSig( int id )
|
|
|
|
{
|
|
|
|
emit highlighted( id );
|
|
|
|
emit highlightedRedirect( id );
|
|
|
|
|
|
|
|
#if defined(QT_ACCESSIBILITY_SUPPORT)
|
|
|
|
QAccessible::updateAccessibility( this, indexOf(id)+1, QAccessible::Focus );
|
|
|
|
QAccessible::updateAccessibility( this, indexOf(id)+1, QAccessible::Selection );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void QPopupMenu::setFirstItemActive()
|
|
|
|
{
|
|
|
|
QMenuItemListIt it(*mitems);
|
|
|
|
register QMenuItem *mi;
|
|
|
|
int ai = 0;
|
|
|
|
if(d->scroll.scrollable)
|
|
|
|
ai = d->scroll.topScrollableIndex;
|
|
|
|
while ( (mi=it.current()) ) {
|
|
|
|
++it;
|
|
|
|
if ( !mi->isSeparator() && mi->id() != QMenuData::d->aInt &&
|
|
|
|
( style().styleHint( QStyle::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 QPopupMenu::hideAllPopups()
|
|
|
|
{
|
|
|
|
register QMenuData *top = this; // find top level popup
|
|
|
|
if ( !preventAnimation )
|
|
|
|
QTimer::singleShot( 10, this, SLOT(allowAnimation()) );
|
|
|
|
preventAnimation = TRUE;
|
|
|
|
|
|
|
|
if ( !isPopup() )
|
|
|
|
return; // nothing to do
|
|
|
|
|
|
|
|
while ( top->parentMenu && top->parentMenu->isPopupMenu
|
|
|
|
&& ((QPopupMenu*)top->parentMenu)->isPopup() )
|
|
|
|
top = top->parentMenu;
|
|
|
|
((QPopupMenu*)top)->hide(); // cascade from top level
|
|
|
|
|
|
|
|
#ifndef QT_NO_WHATSTHIS
|
|
|
|
if (whatsThisItem) {
|
|
|
|
qWhatsThisBDH();
|
|
|
|
whatsThisItem = 0;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\internal
|
|
|
|
Hides all popup sub-menus.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void QPopupMenu::hidePopups()
|
|
|
|
{
|
|
|
|
if ( !preventAnimation )
|
|
|
|
QTimer::singleShot( 10, this, SLOT(allowAnimation()) );
|
|
|
|
preventAnimation = TRUE;
|
|
|
|
|
|
|
|
QMenuItemListIt it(*mitems);
|
|
|
|
register QMenuItem *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(style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this))
|
|
|
|
d->mouseMoveBuffer = QRegion();
|
|
|
|
|
|
|
|
QRect mfrect = itemGeometry( actItem );
|
|
|
|
setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\internal
|
|
|
|
Sends the event to the menu bar.
|
|
|
|
*/
|
|
|
|
|
|
|
|
bool QPopupMenu::tryMenuBar( QMouseEvent *e )
|
|
|
|
{
|
|
|
|
register QMenuData *top = this; // find top level
|
|
|
|
while ( top->parentMenu )
|
|
|
|
top = top->parentMenu;
|
|
|
|
#ifndef QT_NO_MENUBAR
|
|
|
|
return top->isMenuBar ?
|
|
|
|
((QMenuBar *)top)->tryMouseEvent( this, e ) :
|
|
|
|
((QPopupMenu*)top)->tryMouseEvent(this, e );
|
|
|
|
#else
|
|
|
|
return ((QPopupMenu*)top)->tryMouseEvent(this, e );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\internal
|
|
|
|
*/
|
|
|
|
bool QPopupMenu::tryMouseEvent( QPopupMenu *p, QMouseEvent * e)
|
|
|
|
{
|
|
|
|
if ( p == this )
|
|
|
|
return FALSE;
|
|
|
|
QPoint pos = mapFromGlobal( e->globalPos() );
|
|
|
|
if ( !rect().contains( pos ) ) // outside
|
|
|
|
return FALSE;
|
|
|
|
QMouseEvent 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 QPopupMenu::byeMenuBar()
|
|
|
|
{
|
|
|
|
#ifndef QT_NO_MENUBAR
|
|
|
|
register QMenuData *top = this; // find top level
|
|
|
|
while ( top->parentMenu )
|
|
|
|
top = top->parentMenu;
|
|
|
|
#endif
|
|
|
|
hideAllPopups();
|
|
|
|
#ifndef QT_NO_MENUBAR
|
|
|
|
if ( top->isMenuBar )
|
|
|
|
((QMenuBar *)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 QPopupMenu::itemAtPos( const QPoint &pos, bool ignoreSeparator ) const
|
|
|
|
{
|
|
|
|
if ( !contentsRect().contains(pos) )
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
int row = 0;
|
|
|
|
int x = contentsRect().x();
|
|
|
|
int y = contentsRect().y();
|
|
|
|
QMenuItem *mi;
|
|
|
|
QMenuItemListIt 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 += style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
int itemw = contentsRect().width() / ncols;
|
|
|
|
QSize sz;
|
|
|
|
while ( (mi=it.current()) ) {
|
|
|
|
if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown &&
|
|
|
|
y >= contentsRect().height() - style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this))
|
|
|
|
return -1;
|
|
|
|
++it;
|
|
|
|
if ( !mi->isVisible() ) {
|
|
|
|
++row;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int itemh = itemHeight( mi );
|
|
|
|
|
|
|
|
sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
|
|
|
QSize(0, itemh),
|
|
|
|
QStyleOption(mi,maxPMWidth));
|
|
|
|
sz = sz.expandedTo(QSize(itemw, sz.height()));
|
|
|
|
itemw = sz.width();
|
|
|
|
itemh = sz.height();
|
|
|
|
|
|
|
|
if ( ncols > 1 && y + itemh > contentsRect().bottom() ) {
|
|
|
|
y = contentsRect().y();
|
|
|
|
x +=itemw;
|
|
|
|
}
|
|
|
|
if ( QRect( x, y, itemw, itemh ).contains( pos ) )
|
|
|
|
break;
|
|
|
|
y += itemh;
|
|
|
|
++row;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( mi && ( !ignoreSeparator || !mi->isSeparator() ) )
|
|
|
|
return row;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\internal
|
|
|
|
Returns the geometry of item number \a index.
|
|
|
|
*/
|
|
|
|
|
|
|
|
QRect QPopupMenu::itemGeometry( int index )
|
|
|
|
{
|
|
|
|
QMenuItem *mi;
|
|
|
|
QSize sz;
|
|
|
|
int row = 0, scrollh = 0;
|
|
|
|
int x = contentsRect().x();
|
|
|
|
int y = contentsRect().y();
|
|
|
|
QMenuItemListIt it( *mitems );
|
|
|
|
if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp) {
|
|
|
|
scrollh = style().pixelMetric(QStyle::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 & QPopupMenuPrivate::Scroll::ScrollDown &&
|
|
|
|
y >= contentsRect().height() - scrollh)
|
|
|
|
break;
|
|
|
|
++it;
|
|
|
|
if ( !mi->isVisible() ) {
|
|
|
|
++row;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int itemh = itemHeight( mi );
|
|
|
|
|
|
|
|
sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
|
|
|
QSize(0, itemh),
|
|
|
|
QStyleOption(mi,maxPMWidth));
|
|
|
|
sz = sz.expandedTo(QSize(itemw, sz.height()));
|
|
|
|
itemw = sz.width();
|
|
|
|
itemh = sz.height();
|
|
|
|
if(d->scroll.scrollable & QPopupMenuPrivate::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 QRect( x,y,itemw,itemh );
|
|
|
|
y += itemh;
|
|
|
|
++row;
|
|
|
|
}
|
|
|
|
|
|
|
|
return QRect(0,0,0,0);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\internal
|
|
|
|
Calculates and sets the size of the popup menu, based on the size
|
|
|
|
of the items.
|
|
|
|
*/
|
|
|
|
|
|
|
|
QSize QPopupMenu::updateSize(bool force_update, bool do_resize)
|
|
|
|
{
|
|
|
|
polish();
|
|
|
|
if ( count() == 0 ) {
|
|
|
|
QSize ret = QSize( 50, 8 );
|
|
|
|
if(do_resize)
|
|
|
|
setFixedSize( ret );
|
|
|
|
badSize = TRUE;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
int scrheight = 0;
|
|
|
|
if(d->scroll.scrollableSize) {
|
|
|
|
if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp)
|
|
|
|
scrheight += style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
|
|
|
if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown)
|
|
|
|
scrheight += style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(badSize || force_update) {
|
|
|
|
#ifndef QT_NO_ACCEL
|
|
|
|
updateAccel( 0 );
|
|
|
|
#endif
|
|
|
|
int height = 0;
|
|
|
|
int max_width = 0, max_height = 0;
|
|
|
|
QFontMetrics fm = fontMetrics();
|
|
|
|
register QMenuItem *mi;
|
|
|
|
maxPMWidth = 0;
|
|
|
|
int maxWidgetWidth = 0;
|
|
|
|
tab = 0;
|
|
|
|
|
|
|
|
for ( QMenuItemListIt it( *mitems ); it.current(); ++it ) {
|
|
|
|
mi = it.current();
|
|
|
|
QWidget *miw = mi->widget();
|
|
|
|
if (miw) {
|
|
|
|
if ( miw->parentWidget() != this )
|
|
|
|
miw->reparent( this, QPoint(0,0), TRUE );
|
|
|
|
// widget items musn't propgate mouse events
|
|
|
|
((QPopupMenu*)miw)->setWFlags(WNoMousePropagation);
|
|
|
|
}
|
|
|
|
if ( mi->custom() )
|
|
|
|
mi->custom()->setFont( font() );
|
|
|
|
if ( mi->iconSet() != 0)
|
|
|
|
maxPMWidth = QMAX( maxPMWidth,
|
|
|
|
mi->iconSet()->pixmap( QIconSet::Small, QIconSet::Normal ).width() + 4 );
|
|
|
|
}
|
|
|
|
|
|
|
|
int dh = screenRect( geometry().center()).height();
|
|
|
|
ncols = 1;
|
|
|
|
|
|
|
|
for ( QMenuItemListIt it2( *mitems ); it2.current(); ++it2 ) {
|
|
|
|
mi = it2.current();
|
|
|
|
if ( !mi->isVisible() )
|
|
|
|
continue;
|
|
|
|
int w = 0;
|
|
|
|
int itemHeight = QPopupMenu::itemHeight( mi );
|
|
|
|
|
|
|
|
if ( mi->widget() ) {
|
|
|
|
QSize s( mi->widget()->sizeHint() );
|
|
|
|
s = s.expandedTo( mi->widget()->minimumSize() );
|
|
|
|
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 = QMAX( maxWidgetWidth,
|
|
|
|
mi->custom()->sizeHint().width() );
|
|
|
|
} else {
|
|
|
|
QSize s ( mi->custom()->sizeHint() );
|
|
|
|
w += s.width();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
w += maxPMWidth;
|
|
|
|
|
|
|
|
if (! mi->text().isNull()) {
|
|
|
|
QString s = mi->text();
|
|
|
|
int t;
|
|
|
|
if ( (t = s.find('\t')) >= 0 ) { // string contains tab
|
|
|
|
w += fm.width( s, t );
|
|
|
|
w -= s.contains('&') * fm.width('&');
|
|
|
|
w += s.contains("&&") * fm.width('&');
|
|
|
|
int tw = fm.width( s.mid(t + 1) );
|
|
|
|
if ( tw > tab)
|
|
|
|
tab = tw;
|
|
|
|
} else {
|
|
|
|
w += fm.width( s );
|
|
|
|
w -= s.contains('&') * fm.width('&');
|
|
|
|
w += s.contains("&&") * fm.width('&');
|
|
|
|
}
|
|
|
|
} else if (mi->pixmap())
|
|
|
|
w += mi->pixmap()->width();
|
|
|
|
} else {
|
|
|
|
if ( mi->custom() ) {
|
|
|
|
QSize s ( mi->custom()->sizeHint() );
|
|
|
|
w += s.width();
|
|
|
|
} else {
|
|
|
|
w = itemHeight = 2;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
|
|
|
QSize(w, itemHeight),
|
|
|
|
QStyleOption(mi,maxPMWidth));
|
|
|
|
|
|
|
|
w = sz.width();
|
|
|
|
itemHeight = sz.height();
|
|
|
|
|
|
|
|
#if defined(QT_CHECK_NULL)
|
|
|
|
if ( mi->text().isNull() && !mi->pixmap() && !mi->iconSet() &&
|
|
|
|
!mi->isSeparator() && !mi->widget() && !mi->custom() )
|
|
|
|
qWarning( "QPopupMenu: (%s) Popup has invalid menu item",
|
|
|
|
name( "unnamed" ) );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
height += itemHeight;
|
|
|
|
if(style().styleHint(QStyle::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 = QMAX(max_height, height - itemHeight);
|
|
|
|
height = itemHeight;
|
|
|
|
}
|
|
|
|
if ( w > max_width )
|
|
|
|
max_width = w;
|
|
|
|
}
|
|
|
|
if( ncols == 1 && !max_height )
|
|
|
|
max_height = height;
|
|
|
|
|
|
|
|
if(style().styleHint(QStyle::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+style().pixelMetric(QStyle::PM_PopupMenuFrameHorizontalExtra, this)) * 2,
|
|
|
|
extra_height = (fw+style().pixelMetric(QStyle::PM_PopupMenuFrameVerticalExtra, this)) * 2;
|
|
|
|
if ( ncols == 1 )
|
|
|
|
d->calcSize = QSize( QMAX( minimumWidth(), max_width + tab + extra_width ),
|
|
|
|
QMAX( minimumHeight() , height + extra_height ) );
|
|
|
|
else
|
|
|
|
d->calcSize = QSize( QMAX( minimumWidth(), (ncols*(max_width + tab)) + extra_width ),
|
|
|
|
QMAX( minimumHeight(), QMIN( 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.
|
|
|
|
QSize sz;
|
|
|
|
int x = contentsRect().x();
|
|
|
|
int y = contentsRect().y();
|
|
|
|
int itemw = contentsRect().width() / ncols;
|
|
|
|
for(QMenuItemListIt it(*mitems); it.current(); ++it) {
|
|
|
|
QMenuItem *mi = it.current();
|
|
|
|
if ( !mi->isVisible() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
int itemh = itemHeight( mi );
|
|
|
|
|
|
|
|
sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
|
|
|
QSize(0, itemh), QStyleOption(mi,maxPMWidth));
|
|
|
|
sz = sz.expandedTo(QSize(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 = maximumSize(); //let the max size adjust it (virtual)
|
|
|
|
resize( d->calcSize );
|
|
|
|
}
|
|
|
|
return d->calcSize;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#ifndef QT_NO_ACCEL
|
|
|
|
/*!
|
|
|
|
\internal
|
|
|
|
The \a parent 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 QPopupMenu::updateAccel( QWidget *parent )
|
|
|
|
{
|
|
|
|
QMenuItemListIt it(*mitems);
|
|
|
|
register QMenuItem *mi;
|
|
|
|
|
|
|
|
if ( parent ) {
|
|
|
|
delete autoaccel;
|
|
|
|
autoaccel = 0;
|
|
|
|
} else if ( !autoaccel ) {
|
|
|
|
// we have no parent. Rather than ignoring any accelerators we try to find this popup's main window
|
|
|
|
if ( tornOff ) {
|
|
|
|
parent = this;
|
|
|
|
} else {
|
|
|
|
QWidget *w = (QWidget *) this;
|
|
|
|
parent = w->parentWidget();
|
|
|
|
while ( (!w->testWFlags(WType_TopLevel) || !w->testWFlags(WType_Popup)) && parent ) {
|
|
|
|
w = parent;
|
|
|
|
parent = parent->parentWidget();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( parent == 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 QAccel( parent, this );
|
|
|
|
connect( autoaccel, SIGNAL(activated(int)),
|
|
|
|
SLOT(accelActivated(int)) );
|
|
|
|
connect( autoaccel, SIGNAL(activatedAmbiguously(int)),
|
|
|
|
SLOT(accelActivated(int)) );
|
|
|
|
connect( autoaccel, SIGNAL(destroyed()),
|
|
|
|
SLOT(accelDestroyed()) );
|
|
|
|
if ( accelDisabled )
|
|
|
|
autoaccel->setEnabled( FALSE );
|
|
|
|
}
|
|
|
|
while ( (mi=it.current()) ) {
|
|
|
|
++it;
|
|
|
|
QKeySequence k = mi->key();
|
|
|
|
if ( (int)k ) {
|
|
|
|
int id = autoaccel->insertItem( k, mi->id() );
|
|
|
|
#ifndef QT_NO_WHATSTHIS
|
|
|
|
autoaccel->setWhatsThis( id, mi->whatsThis() );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
if ( !mi->text().isNull() || mi->custom() ) {
|
|
|
|
QString s = mi->text();
|
|
|
|
int i = s.find('\t');
|
|
|
|
|
|
|
|
// Note: Only looking at the first key in the sequence!
|
|
|
|
if ( (int)k && (int)k != Key_unknown ) {
|
|
|
|
QString t = (QString)mi->key();
|
|
|
|
if ( i >= 0 )
|
|
|
|
s.replace( 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() && parent ) { // call recursively
|
|
|
|
// reuse
|
|
|
|
QPopupMenu* popup = mi->popup();
|
|
|
|
if (!popup->avoid_circularity) {
|
|
|
|
popup->avoid_circularity = 1;
|
|
|
|
popup->updateAccel( parent );
|
|
|
|
popup->avoid_circularity = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\internal
|
|
|
|
It would be better to check in the slot.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void QPopupMenu::enableAccel( bool enable )
|
|
|
|
{
|
|
|
|
if ( autoaccel )
|
|
|
|
autoaccel->setEnabled( enable );
|
|
|
|
accelDisabled = !enable; // rememeber when updateAccel
|
|
|
|
QMenuItemListIt it(*mitems);
|
|
|
|
register QMenuItem *mi;
|
|
|
|
while ( (mi=it.current()) ) { // do the same for sub popups
|
|
|
|
++it;
|
|
|
|
if ( mi->popup() ) // call recursively
|
|
|
|
mi->popup()->enableAccel( enable );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
void QPopupMenu::setFont( const QFont &font )
|
|
|
|
{
|
|
|
|
QWidget::setFont( font );
|
|
|
|
badSize = TRUE;
|
|
|
|
if ( isVisible() ) {
|
|
|
|
updateSize();
|
|
|
|
update();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
void QPopupMenu::show()
|
|
|
|
{
|
|
|
|
if ( !isPopup() && isVisible() )
|
|
|
|
hide();
|
|
|
|
|
|
|
|
if ( isVisible() ) {
|
|
|
|
supressAboutToShow = FALSE;
|
|
|
|
QWidget::show();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (!supressAboutToShow)
|
|
|
|
emit aboutToShow();
|
|
|
|
else
|
|
|
|
supressAboutToShow = FALSE;
|
|
|
|
performDelayedChanges();
|
|
|
|
updateSize(TRUE);
|
|
|
|
QWidget::show();
|
|
|
|
updateSize();
|
|
|
|
popupActive = -1;
|
|
|
|
if(style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this))
|
|
|
|
d->mouseMoveBuffer = QRegion();
|
|
|
|
d->ignoremousepos = QCursor::pos();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
|
|
|
|
void QPopupMenu::hide()
|
|
|
|
{
|
|
|
|
if ( syncMenu == this && qApp ) {
|
|
|
|
qApp->exit_loop();
|
|
|
|
syncMenu = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !isVisible() ) {
|
|
|
|
QWidget::hide();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
emit aboutToHide();
|
|
|
|
|
|
|
|
actItem = popupActive = -1;
|
|
|
|
if(style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this))
|
|
|
|
d->mouseMoveBuffer = QRegion();
|
|
|
|
mouseBtDn = FALSE; // mouse button up
|
|
|
|
#if defined(QT_ACCESSIBILITY_SUPPORT)
|
|
|
|
QAccessible::updateAccessibility( this, 0, QAccessible::PopupMenuEnd );
|
|
|
|
#endif
|
|
|
|
#ifndef QT_NO_MENUBAR
|
|
|
|
QMenuData *top = this; // find top level
|
|
|
|
while ( top->parentMenu )
|
|
|
|
top = top->parentMenu;
|
|
|
|
if( top->isMenuBar )
|
|
|
|
x11SetWindowType( X11WindowTypePopup ); // reset
|
|
|
|
#endif
|
|
|
|
parentMenu = 0;
|
|
|
|
hidePopups();
|
|
|
|
QWidget::hide();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Calculates the height in pixels of the item in row \a row.
|
|
|
|
*/
|
|
|
|
int QPopupMenu::itemHeight( int row ) const
|
|
|
|
{
|
|
|
|
return itemHeight( mitems->at( row ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\overload
|
|
|
|
|
|
|
|
Calculates the height in pixels of the menu item \a mi.
|
|
|
|
*/
|
|
|
|
int QPopupMenu::itemHeight( QMenuItem *mi ) const
|
|
|
|
{
|
|
|
|
if ( mi->widget() )
|
|
|
|
return mi->widget()->height();
|
|
|
|
if ( mi->custom() && mi->custom()->fullSpan() )
|
|
|
|
return mi->custom()->sizeHint().height();
|
|
|
|
|
|
|
|
QFontMetrics 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 = QMAX(h, mi->iconSet()->pixmap( QIconSet::Small,
|
|
|
|
QIconSet::Normal ).height());
|
|
|
|
if ( mi->custom() )
|
|
|
|
h = QMAX(h, mi->custom()->sizeHint().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 QStyle::drawControl()
|
|
|
|
*/
|
|
|
|
void QPopupMenu::drawItem( QPainter* p, int tab_, QMenuItem* mi,
|
|
|
|
bool act, int x, int y, int w, int h)
|
|
|
|
{
|
|
|
|
QStyle::SFlags flags = QStyle::Style_Default;
|
|
|
|
if (isEnabled() && mi->isEnabledAndVisible() && (!mi->popup() || mi->popup()->isEnabled()) )
|
|
|
|
flags |= QStyle::Style_Enabled;
|
|
|
|
if (act)
|
|
|
|
flags |= QStyle::Style_Active;
|
|
|
|
if (mouseBtDn)
|
|
|
|
flags |= QStyle::Style_Down;
|
|
|
|
|
|
|
|
const QColorGroup &cg = ((flags&QStyle::Style_Enabled) ? colorGroup() : palette().disabled() );
|
|
|
|
|
|
|
|
if ( mi->custom() && mi->custom()->fullSpan() ) {
|
|
|
|
QMenuItem dummy;
|
|
|
|
style().drawControl(QStyle::CE_PopupMenuItem, p, this, QRect(x, y, w, h), cg,
|
|
|
|
flags, QStyleOption(&dummy,maxPMWidth,tab_));
|
|
|
|
mi->custom()->paint( p, cg, act, flags&QStyle::Style_Enabled, x, y, w, h );
|
|
|
|
} else
|
|
|
|
style().drawControl(QStyle::CE_PopupMenuItem, p, this, QRect(x, y, w, h), cg,
|
|
|
|
flags, QStyleOption(mi,maxPMWidth,tab_));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Draws all menu items using painter \a p.
|
|
|
|
*/
|
|
|
|
void QPopupMenu::drawContents( QPainter* p )
|
|
|
|
{
|
|
|
|
QMenuItemListIt it(*mitems);
|
|
|
|
QMenuItem *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 & QPopupMenuPrivate::Scroll::ScrollUp) {
|
|
|
|
QRect rect(x, y, contentsRect().width(),
|
|
|
|
style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this));
|
|
|
|
if(!p->hasClipping() || p->clipRegion().contains(rect)) {
|
|
|
|
QStyle::SFlags flags = QStyle::Style_Up;
|
|
|
|
if (isEnabled())
|
|
|
|
flags |= QStyle::Style_Enabled;
|
|
|
|
style().drawControl(QStyle::CE_PopupMenuScroller, p, this, rect,
|
|
|
|
colorGroup(), flags, QStyleOption(maxPMWidth));
|
|
|
|
}
|
|
|
|
y += rect.height();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int itemw = contentsRect().width() / ncols;
|
|
|
|
QSize sz;
|
|
|
|
QStyle::SFlags flags;
|
|
|
|
while ( (mi=it.current()) ) {
|
|
|
|
if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown &&
|
|
|
|
y >= contentsRect().height() - style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this))
|
|
|
|
break;
|
|
|
|
++it;
|
|
|
|
if ( !mi->isVisible() ) {
|
|
|
|
++row;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
int itemh = itemHeight( mi );
|
|
|
|
sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
|
|
|
QSize(0, itemh),
|
|
|
|
QStyleOption(mi,maxPMWidth,0)
|
|
|
|
);
|
|
|
|
sz = sz.expandedTo(QSize(itemw, sz.height()));
|
|
|
|
itemw = sz.width();
|
|
|
|
itemh = sz.height();
|
|
|
|
|
|
|
|
if ( ncols > 1 && y + itemh > contentsRect().bottom() ) {
|
|
|
|
if ( y < contentsRect().bottom() ) {
|
|
|
|
QRect rect(x, y, itemw, contentsRect().bottom() - y);
|
|
|
|
if(!p->hasClipping() || p->clipRegion().contains(rect)) {
|
|
|
|
flags = QStyle::Style_Default;
|
|
|
|
if (isEnabled() && mi->isEnabledAndVisible())
|
|
|
|
flags |= QStyle::Style_Enabled;
|
|
|
|
style().drawControl(QStyle::CE_PopupMenuItem, p, this, rect,
|
|
|
|
colorGroup(), flags, QStyleOption((QMenuItem*)0,maxPMWidth));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
y = contentsRect().y();
|
|
|
|
x +=itemw;
|
|
|
|
}
|
|
|
|
if (!mi->widget() && (!p->hasClipping() || p->clipRegion().contains(QRect(x, y, itemw, itemh))))
|
|
|
|
drawItem( p, tab, mi, row == actItem, x, y, itemw, itemh );
|
|
|
|
y += itemh;
|
|
|
|
++row;
|
|
|
|
}
|
|
|
|
if ( y < contentsRect().bottom() ) {
|
|
|
|
QRect rect(x, y, itemw, contentsRect().bottom() - y);
|
|
|
|
if(!p->hasClipping() || p->clipRegion().contains(rect)) {
|
|
|
|
flags = QStyle::Style_Default;
|
|
|
|
if ( isEnabled() )
|
|
|
|
flags |= QStyle::Style_Enabled;
|
|
|
|
style().drawControl(QStyle::CE_PopupMenuItem, p, this, rect,
|
|
|
|
colorGroup(), flags, QStyleOption((QMenuItem*)0,maxPMWidth));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if( d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown ) {
|
|
|
|
int sh = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
|
|
|
QRect rect(x, contentsRect().height() - sh, contentsRect().width(), sh);
|
|
|
|
if(!p->hasClipping() || p->clipRegion().contains(rect)) {
|
|
|
|
QStyle::SFlags flags = QStyle::Style_Down;
|
|
|
|
if (isEnabled())
|
|
|
|
flags |= QStyle::Style_Enabled;
|
|
|
|
style().drawControl(QStyle::CE_PopupMenuScroller, p, this, rect,
|
|
|
|
colorGroup(), flags, QStyleOption(maxPMWidth));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#if defined( DEBUG_SLOPPY_SUBMENU )
|
|
|
|
if ( style().styleHint(QStyle::SH_PopupMenu_SloppySubMenus, this )) {
|
|
|
|
p->setClipRegion( d->mouseMoveBuffer );
|
|
|
|
p->fillRect( d->mouseMoveBuffer.boundingRect(), colorGroup().brush( QColorGroup::Highlight ) );
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*****************************************************************************
|
|
|
|
Event handlers
|
|
|
|
*****************************************************************************/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
|
|
|
|
void QPopupMenu::paintEvent( QPaintEvent *e )
|
|
|
|
{
|
|
|
|
QFrame::paintEvent( e );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
|
|
|
|
void QPopupMenu::closeEvent( QCloseEvent * e) {
|
|
|
|
e->accept();
|
|
|
|
byeMenuBar();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
|
|
|
|
void QPopupMenu::mousePressEvent( QMouseEvent *e )
|
|
|
|
{
|
|
|
|
int sh = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
|
|
|
if (rect().contains(e->pos()) &&
|
|
|
|
((d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp && e->pos().y() <= sh) || //up
|
|
|
|
(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown &&
|
|
|
|
e->pos().y() >= contentsRect().height() - sh))) //down
|
|
|
|
return;
|
|
|
|
|
|
|
|
mouseBtDn = TRUE; // mouse button down
|
|
|
|
int item = itemAtPos( e->pos() );
|
|
|
|
if ( item == -1 ) {
|
|
|
|
if ( !rect().contains(e->pos()) && !tryMenuBar(e) ) {
|
|
|
|
byeMenuBar();
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
register QMenuItem *mi = mitems->at(item);
|
|
|
|
if ( item != actItem ) // new item activated
|
|
|
|
setActiveItem( item );
|
|
|
|
|
|
|
|
QPopupMenu *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 QPopupMenu::mouseReleaseEvent( QMouseEvent *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 = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
|
|
|
if ( !rect().contains( e->pos() ) && tryMenuBar(e) )
|
|
|
|
return;
|
|
|
|
else if((d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp && e->pos().y() <= sh) || //up
|
|
|
|
(d->scroll.scrollable & QPopupMenuPrivate::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 ( rect().contains( e->pos() ) && motion < 6 )
|
|
|
|
return;
|
|
|
|
else
|
|
|
|
byeMenuBar();
|
|
|
|
} else { // selected menu item!
|
|
|
|
register QMenuItem *mi = mitems->at(actItem);
|
|
|
|
if ( mi ->widget() ) {
|
|
|
|
QWidget* widgetAt = QApplication::widgetAt( e->globalPos(), TRUE );
|
|
|
|
if ( widgetAt && widgetAt != this ) {
|
|
|
|
QMouseEvent me( e->type(), widgetAt->mapFromGlobal( e->globalPos() ),
|
|
|
|
e->globalPos(), e->button(), e->state() );
|
|
|
|
QApplication::sendEvent( widgetAt, &me );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
QPopupMenu *popup = mi->popup();
|
|
|
|
#ifndef QT_NO_WHATSTHIS
|
|
|
|
bool b = QWhatsThis::inWhatsThisMode();
|
|
|
|
#else
|
|
|
|
const bool b = FALSE;
|
|
|
|
#endif
|
|
|
|
if ( !mi->isEnabledAndVisible() ) {
|
|
|
|
#ifndef QT_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;
|
|
|
|
QGuardedPtr<QSignal> signal = mi->signal();
|
|
|
|
actSig( mi->id(), b );
|
|
|
|
if ( signal && !b )
|
|
|
|
signal->activate();
|
|
|
|
active_popup_menu = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
|
|
|
|
void QPopupMenu::mouseMoveEvent( QMouseEvent *e )
|
|
|
|
{
|
|
|
|
if( e->globalPos() == d->ignoremousepos ) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
d->ignoremousepos = QPoint();
|
|
|
|
|
|
|
|
motion++;
|
|
|
|
|
|
|
|
if ( parentMenu && parentMenu->isPopupMenu ) {
|
|
|
|
QPopupMenu* p = (QPopupMenu*)parentMenu;
|
|
|
|
int myIndex;
|
|
|
|
|
|
|
|
p->findPopup( this, &myIndex );
|
|
|
|
QPoint pPos = p->mapFromParent( e->globalPos() );
|
|
|
|
if ( p->actItem != myIndex && !p->rect().contains( pPos ) )
|
|
|
|
p->setActiveItem( myIndex );
|
|
|
|
|
|
|
|
if ( style().styleHint(QStyle::SH_PopupMenu_SloppySubMenus, this )) {
|
|
|
|
p->d->mouseMoveBuffer = QRegion();
|
|
|
|
#ifdef DEBUG_SLOPPY_SUBMENU
|
|
|
|
p->repaint();
|
|
|
|
#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 = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
|
|
|
if((d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp && e->pos().y() <= sh) ||
|
|
|
|
(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown && e->pos().y() >= height()-sh)) {
|
|
|
|
if(!d->scroll.scrolltimer) {
|
|
|
|
d->scroll.scrolltimer = new QTimer(this, "popup scroll timer");
|
|
|
|
QObject::connect( d->scroll.scrolltimer, SIGNAL(timeout()),
|
|
|
|
this, SLOT(subScrollTimer()) );
|
|
|
|
}
|
|
|
|
if(!d->scroll.scrolltimer->isActive())
|
|
|
|
d->scroll.scrolltimer->start(40);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int item = itemAtPos( e->pos() );
|
|
|
|
if ( item == -1 ) { // no valid item
|
|
|
|
if( !d->hasmouse ) {
|
|
|
|
tryMenuBar( e );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
d->hasmouse = 0;
|
|
|
|
int lastActItem = actItem;
|
|
|
|
actItem = -1;
|
|
|
|
if ( lastActItem >= 0 )
|
|
|
|
updateRow( lastActItem );
|
|
|
|
if ( lastActItem > 0 ||
|
|
|
|
( !rect().contains( e->pos() ) && !tryMenuBar( e ) ) ) {
|
|
|
|
popupSubMenuLater(style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay,
|
|
|
|
this), this);
|
|
|
|
}
|
|
|
|
} else { // mouse on valid item
|
|
|
|
// but did not register mouse press
|
|
|
|
d->hasmouse = 1;
|
|
|
|
if ( (e->state() & Qt::MouseButtonMask) && !mouseBtDn )
|
|
|
|
mouseBtDn = TRUE; // so mouseReleaseEvent will pop down
|
|
|
|
|
|
|
|
register QMenuItem *mi = mitems->at( item );
|
|
|
|
|
|
|
|
if ( mi->widget() ) {
|
|
|
|
QWidget* widgetAt = QApplication::widgetAt( e->globalPos(), TRUE );
|
|
|
|
if ( widgetAt && widgetAt != this ) {
|
|
|
|
QMouseEvent me( e->type(), widgetAt->mapFromGlobal( e->globalPos() ),
|
|
|
|
e->globalPos(), e->button(), e->state() );
|
|
|
|
QApplication::sendEvent( widgetAt, &me );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( actItem == item )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( style().styleHint(QStyle::SH_PopupMenu_SloppySubMenus, this) &&
|
|
|
|
d->mouseMoveBuffer.contains( e->pos() ) ) {
|
|
|
|
actItem = item;
|
|
|
|
popupSubMenuLater( style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this) * 6,
|
|
|
|
this );
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( mi->popup() || ( popupActive >= 0 && popupActive != item ))
|
|
|
|
popupSubMenuLater( style().styleHint(QStyle::SH_PopupMenu_SubMenuPopupDelay, this),
|
|
|
|
this );
|
|
|
|
else if ( singleSingleShot )
|
|
|
|
singleSingleShot->stop();
|
|
|
|
|
|
|
|
if ( item != actItem )
|
|
|
|
setActiveItem( item );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
|
|
|
|
void QPopupMenu::keyPressEvent( QKeyEvent *e )
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
I get nothing but complaints about this. -Brad
|
|
|
|
|
|
|
|
- if (mouseBtDn && actItem >= 0) {
|
|
|
|
- if (e->key() == Key_Shift ||
|
|
|
|
- e->key() == Key_Control ||
|
|
|
|
- e->key() == Key_Alt)
|
|
|
|
- return;
|
|
|
|
-
|
|
|
|
- QMenuItem *mi = mitems->at(actItem);
|
|
|
|
- int modifier = (((e->state() & ShiftButton) ? SHIFT : 0) |
|
|
|
|
- ((e->state() & ControlButton) ? CTRL : 0) |
|
|
|
|
- ((e->state() & AltButton) ? ALT : 0));
|
|
|
|
-
|
|
|
|
- #ifndef QT_NO_ACCEL
|
|
|
|
- if (mi)
|
|
|
|
- setAccel(modifier + e->key(), mi->id());
|
|
|
|
- #endif
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
*/
|
|
|
|
|
|
|
|
QMenuItem *mi = 0;
|
|
|
|
QPopupMenu *popup;
|
|
|
|
int dy = 0;
|
|
|
|
bool ok_key = TRUE;
|
|
|
|
|
|
|
|
int key = e->key();
|
|
|
|
if ( QApplication::reverseLayout() ) {
|
|
|
|
// in reverse mode opening and closing keys for submenues are reversed
|
|
|
|
if ( key == Key_Left )
|
|
|
|
key = Key_Right;
|
|
|
|
else if ( key == Key_Right )
|
|
|
|
key = Key_Left;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch ( key ) {
|
|
|
|
case Key_Tab:
|
|
|
|
// ignore tab, otherwise it will be passed to the menubar
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Key_Up:
|
|
|
|
dy = -1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Key_Down:
|
|
|
|
dy = 1;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Key_Alt:
|
|
|
|
if ( style().styleHint(QStyle::SH_MenuBar_AltKeyNavigation, this) )
|
|
|
|
byeMenuBar();
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Key_Escape:
|
|
|
|
if ( tornOff ) {
|
|
|
|
close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// just hide one
|
|
|
|
{
|
|
|
|
QMenuData* p = parentMenu;
|
|
|
|
hide();
|
|
|
|
#ifndef QT_NO_MENUBAR
|
|
|
|
if ( p && p->isMenuBar )
|
|
|
|
((QMenuBar*) p)->goodbye( TRUE );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Key_Left:
|
|
|
|
if ( ncols > 1 && actItem >= 0 ) {
|
|
|
|
QRect r( itemGeometry( actItem ) );
|
|
|
|
int newActItem = itemAtPos( QPoint( r.left() - 1, r.center().y() ) );
|
|
|
|
if ( newActItem >= 0 ) {
|
|
|
|
setActiveItem( newActItem );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( parentMenu && parentMenu->isPopupMenu ) {
|
|
|
|
((QPopupMenu *)parentMenu)->hidePopups();
|
|
|
|
if ( singleSingleShot )
|
|
|
|
singleSingleShot->stop();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
ok_key = FALSE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case 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 ) {
|
|
|
|
QRect r( itemGeometry( actItem ) );
|
|
|
|
int newActItem = itemAtPos( QPoint( r.right() + 1, r.center().y() ) );
|
|
|
|
if ( newActItem >= 0 ) {
|
|
|
|
setActiveItem( newActItem );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ok_key = FALSE;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Key_Space:
|
|
|
|
if (! style().styleHint(QStyle::SH_PopupMenu_SpaceActivatesItem, this))
|
|
|
|
break;
|
|
|
|
// for motif, fall through
|
|
|
|
|
|
|
|
case Key_Return:
|
|
|
|
case Key_Enter:
|
|
|
|
{
|
|
|
|
if ( actItem < 0 )
|
|
|
|
break;
|
|
|
|
#ifndef QT_NO_WHATSTHIS
|
|
|
|
bool b = QWhatsThis::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;
|
|
|
|
QGuardedPtr<QSignal> signal = mi->signal();
|
|
|
|
actSig( mi->id(), b );
|
|
|
|
if ( signal && !b )
|
|
|
|
signal->activate();
|
|
|
|
active_popup_menu = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
#ifndef QT_NO_WHATSTHIS
|
|
|
|
case Key_F1:
|
|
|
|
if ( actItem < 0 || e->state() != ShiftButton)
|
|
|
|
break;
|
|
|
|
mi = mitems->at( actItem );
|
|
|
|
if ( !mi->whatsThis().isNull() ){
|
|
|
|
if ( !QWhatsThis::inWhatsThisMode() )
|
|
|
|
QWhatsThis::enterWhatsThisMode();
|
|
|
|
QRect r( itemGeometry( actItem) );
|
|
|
|
QWhatsThis::leaveWhatsThisMode( mi->whatsThis(), mapToGlobal( r.bottomLeft()) );
|
|
|
|
}
|
|
|
|
//fall-through!
|
|
|
|
#endif
|
|
|
|
default:
|
|
|
|
ok_key = FALSE;
|
|
|
|
|
|
|
|
}
|
|
|
|
if ( !ok_key &&
|
|
|
|
( !e->state() || e->state() == AltButton || e->state() == ShiftButton ) &&
|
|
|
|
e->text().length()==1 ) {
|
|
|
|
QChar c = e->text()[0].upper();
|
|
|
|
|
|
|
|
QMenuItemListIt it(*mitems);
|
|
|
|
QMenuItem* first = 0;
|
|
|
|
QMenuItem* currentSelected = 0;
|
|
|
|
QMenuItem* firstAfterCurrent = 0;
|
|
|
|
|
|
|
|
register QMenuItem *m;
|
|
|
|
mi = 0;
|
|
|
|
int indx = 0;
|
|
|
|
int clashCount = 0;
|
|
|
|
while ( (m=it.current()) ) {
|
|
|
|
++it;
|
|
|
|
QString s = m->text();
|
|
|
|
if ( !s.isEmpty() ) {
|
|
|
|
int i = s.find( '&' );
|
|
|
|
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.find( '&', 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 QT_NO_WHATSTHIS
|
|
|
|
bool b = QWhatsThis::inWhatsThisMode();
|
|
|
|
#else
|
|
|
|
const bool b = FALSE;
|
|
|
|
#endif
|
|
|
|
if ( mi->isEnabledAndVisible() || b ) {
|
|
|
|
active_popup_menu = this;
|
|
|
|
QGuardedPtr<QSignal> 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 QT_NO_MENUBAR
|
|
|
|
if ( !ok_key ) { // send to menu bar
|
|
|
|
register QMenuData *top = this; // find top level
|
|
|
|
while ( top->parentMenu )
|
|
|
|
top = top->parentMenu;
|
|
|
|
if ( top->isMenuBar ) {
|
|
|
|
int beforeId = top->actItem;
|
|
|
|
((QMenuBar*)top)->tryKeyEvent( this, e );
|
|
|
|
if ( beforeId != top->actItem )
|
|
|
|
ok_key = TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
if ( actItem < 0 ) {
|
|
|
|
if ( dy > 0 ) {
|
|
|
|
setFirstItemActive();
|
|
|
|
} else if ( dy < 0 ) {
|
|
|
|
QMenuItemListIt it(*mitems);
|
|
|
|
it.toLast();
|
|
|
|
register QMenuItem *mi;
|
|
|
|
int ai = count() - 1;
|
|
|
|
while ( (mi=it.current()) ) {
|
|
|
|
--it;
|
|
|
|
if ( !mi->isSeparator() && mi->id() != QMenuData::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() &&
|
|
|
|
( style().styleHint(QStyle::SH_PopupMenu_AllowActiveAndDisabled, this)
|
|
|
|
|| mi->isEnabledAndVisible() ) )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ( i != actItem )
|
|
|
|
setActiveItem( i );
|
|
|
|
if(d->scroll.scrollable) { //need to scroll to make it visible?
|
|
|
|
QRect r = itemGeometry(actItem);
|
|
|
|
if(r.isNull() || r.height() < itemHeight(mitems->at(actItem))) {
|
|
|
|
bool refresh = FALSE;
|
|
|
|
if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp && dy == -1) { //up
|
|
|
|
if(d->scroll.topScrollableIndex >= 0) {
|
|
|
|
d->scroll.topScrollableIndex--;
|
|
|
|
refresh = TRUE;
|
|
|
|
}
|
|
|
|
} else if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown) { //down
|
|
|
|
QMenuItemListIt it(*mitems);
|
|
|
|
int sh = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
|
|
|
for(int i = 0, y = ((d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp) ? sh : 0); it.current(); i++, ++it) {
|
|
|
|
if(i >= d->scroll.topScrollableIndex) {
|
|
|
|
int itemh = itemHeight(it.current());
|
|
|
|
QSize sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
|
|
|
QSize(0, itemh),
|
|
|
|
QStyleOption(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 Q_OS_WIN32
|
|
|
|
if ( !ok_key &&
|
|
|
|
!( e->key() == Key_Control || e->key() == Key_Shift || e->key() == Key_Meta ) )
|
|
|
|
qApp->beep();
|
|
|
|
#endif // Q_OS_WIN32
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
|
|
|
|
void QPopupMenu::timerEvent( QTimerEvent *e )
|
|
|
|
{
|
|
|
|
QFrame::timerEvent( e );
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
void QPopupMenu::leaveEvent( QEvent * )
|
|
|
|
{
|
|
|
|
d->hasmouse = 0;
|
|
|
|
if ( testWFlags( WStyle_Tool ) && style().styleHint(QStyle::SH_PopupMenu_MouseTracking, this) ) {
|
|
|
|
int lastActItem = actItem;
|
|
|
|
actItem = -1;
|
|
|
|
if ( lastActItem >= 0 )
|
|
|
|
updateRow( lastActItem );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
void QPopupMenu::styleChange( QStyle& old )
|
|
|
|
{
|
|
|
|
QFrame::styleChange( old );
|
|
|
|
setMouseTracking(style().styleHint(QStyle::SH_PopupMenu_MouseTracking, this));
|
|
|
|
style().polishPopupMenu( this );
|
|
|
|
updateSize(TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!\reimp
|
|
|
|
*/
|
|
|
|
void QPopupMenu::enabledChange( bool )
|
|
|
|
{
|
|
|
|
if ( QMenuData::d->aWidget ) // torn-off menu
|
|
|
|
QMenuData::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 layout means (for
|
|
|
|
example, on Windows it will use multiple columns).
|
|
|
|
|
|
|
|
This functions returns the number of columns necessary.
|
|
|
|
|
|
|
|
\sa sizeHint()
|
|
|
|
*/
|
|
|
|
int QPopupMenu::columns() const
|
|
|
|
{
|
|
|
|
return ncols;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This private slot handles the scrolling popupmenu */
|
|
|
|
void QPopupMenu::subScrollTimer() {
|
|
|
|
QPoint pos = QCursor::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 = style().pixelMetric(QStyle::PM_PopupMenuScrollerHeight, this);
|
|
|
|
if(!d->scroll.lastScroll.isValid()) {
|
|
|
|
d->scroll.lastScroll = QTime::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(QTime::currentTime()) < QMAX(0, msecs))
|
|
|
|
return;
|
|
|
|
d->scroll.lastScroll = QTime::currentTime();
|
|
|
|
}
|
|
|
|
if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp && pos.y() <= y() + sh) { //up
|
|
|
|
if(d->scroll.topScrollableIndex > 0) {
|
|
|
|
d->scroll.topScrollableIndex--;
|
|
|
|
updateScrollerState();
|
|
|
|
update(contentsRect());
|
|
|
|
}
|
|
|
|
} else if(d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollDown &&
|
|
|
|
pos.y() >= (y() + contentsRect().height()) - sh) { //down
|
|
|
|
QMenuItemListIt it(*mitems);
|
|
|
|
for(int i = 0, y = contentsRect().y() + sh; it.current(); i++, ++it) {
|
|
|
|
if(i >= d->scroll.topScrollableIndex) {
|
|
|
|
int itemh = itemHeight(it.current());
|
|
|
|
QSize sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this, QSize(0, itemh),
|
|
|
|
QStyleOption(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 QPopupMenu::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;
|
|
|
|
|
|
|
|
QMenuItem *mi = mitems->at(actItem);
|
|
|
|
if ( !mi || !mi->isEnabledAndVisible() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
QPopupMenu *popup = mi->popup();
|
|
|
|
if ( !popup || !popup->isEnabled() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
//avoid circularity
|
|
|
|
if ( popup->isVisible() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
Q_ASSERT( popup->parentMenu == 0 );
|
|
|
|
popup->parentMenu = this; // set parent menu
|
|
|
|
|
|
|
|
emit popup->aboutToShow();
|
|
|
|
supressAboutToShow = TRUE;
|
|
|
|
|
|
|
|
|
|
|
|
QRect r( itemGeometry( actItem ) );
|
|
|
|
QPoint p;
|
|
|
|
QSize ps = popup->sizeHint();
|
|
|
|
// GUI Style
|
|
|
|
int gs = style().styleHint(QStyle::SH_GUIStyle);
|
|
|
|
int arrowHMargin, arrowVMargin;
|
|
|
|
if (gs == GtkStyle) {
|
|
|
|
arrowHMargin = gtkArrowHMargin;
|
|
|
|
arrowVMargin = gtkArrowVMargin;
|
|
|
|
} else {
|
|
|
|
arrowHMargin = motifArrowHMargin;
|
|
|
|
arrowVMargin = motifArrowVMargin;
|
|
|
|
}
|
|
|
|
if( QApplication::reverseLayout() ) {
|
|
|
|
p = QPoint( r.left() + arrowHMargin - ps.width(), r.top() + arrowVMargin );
|
|
|
|
p = mapToGlobal( p );
|
|
|
|
|
|
|
|
bool right = FALSE;
|
|
|
|
if ( ( parentMenu && parentMenu->isPopupMenu &&
|
|
|
|
((QPopupMenu*)parentMenu)->geometry().x() < geometry().x() ) ||
|
|
|
|
p.x() < screenRect( p ).left())
|
|
|
|
right = TRUE;
|
|
|
|
if ( right && (ps.width() > screenRect( p ).right() - mapToGlobal( r.topRight() ).x() ) )
|
|
|
|
right = FALSE;
|
|
|
|
if ( right )
|
|
|
|
p.setX( mapToGlobal( r.topRight() ).x() );
|
|
|
|
} else {
|
|
|
|
p = QPoint( r.right() - arrowHMargin, r.top() + arrowVMargin );
|
|
|
|
p = mapToGlobal( p );
|
|
|
|
|
|
|
|
bool left = FALSE;
|
|
|
|
if ( ( parentMenu && parentMenu->isPopupMenu &&
|
|
|
|
((QPopupMenu*)parentMenu)->geometry().x() > geometry().x() ) ||
|
|
|
|
p.x() + ps.width() > screenRect( p ).right() )
|
|
|
|
left = TRUE;
|
|
|
|
if ( left && (ps.width() > mapToGlobal( r.topLeft() ).x() ) )
|
|
|
|
left = FALSE;
|
|
|
|
if ( left )
|
|
|
|
p.setX( mapToGlobal( r.topLeft() ).x() - ps.width() );
|
|
|
|
}
|
|
|
|
QRect pr = popup->itemGeometry(popup->count() - 1);
|
|
|
|
if (p.y() + ps.height() > screenRect( p ).bottom() &&
|
|
|
|
p.y() - ps.height() + (QCOORD) pr.height() >= screenRect( p ).top())
|
|
|
|
p.setY( p.y() - ps.height() + (QCOORD) pr.height());
|
|
|
|
|
|
|
|
if ( style().styleHint(QStyle::SH_PopupMenu_SloppySubMenus, this )) {
|
|
|
|
QPoint cur = QCursor::pos();
|
|
|
|
if ( r.contains( mapFromGlobal( cur ) ) ) {
|
|
|
|
QPoint pts[4];
|
|
|
|
pts[0] = QPoint( cur.x(), cur.y() - 2 );
|
|
|
|
pts[3] = QPoint( cur.x(), cur.y() + 2 );
|
|
|
|
if ( p.x() >= cur.x() ) {
|
|
|
|
pts[1] = QPoint( geometry().right(), p.y() );
|
|
|
|
pts[2] = QPoint( geometry().right(), p.y() + ps.height() );
|
|
|
|
} else {
|
|
|
|
pts[1] = QPoint( p.x() + ps.width(), p.y() );
|
|
|
|
pts[2] = QPoint( p.x() + ps.width(), p.y() + ps.height() );
|
|
|
|
}
|
|
|
|
QPointArray points( 4 );
|
|
|
|
for( int i = 0; i < 4; i++ )
|
|
|
|
points.setPoint( i, mapFromGlobal( pts[i] ) );
|
|
|
|
d->mouseMoveBuffer = QRegion( points );
|
|
|
|
repaint();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
popupActive = actItem;
|
|
|
|
popup->popup( p );
|
|
|
|
}
|
|
|
|
|
|
|
|
void QPopupMenu::allowAnimation()
|
|
|
|
{
|
|
|
|
preventAnimation = FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QPopupMenu::updateRow( int row )
|
|
|
|
{
|
|
|
|
if ( !isVisible() )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( badSize ) {
|
|
|
|
updateSize();
|
|
|
|
update();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
updateSize();
|
|
|
|
QRect r = itemGeometry( row );
|
|
|
|
if ( !r.isNull() ) // can happen via the scroller
|
|
|
|
repaint( 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
|
|
|
|
QWidget::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 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( QCursor::pos() );
|
|
|
|
\endcode
|
|
|
|
or aligned to a widget:
|
|
|
|
\code
|
|
|
|
exec( somewidget.mapToGlobal(QPoint(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 sizeHint(). It calculates the proper size
|
|
|
|
depending on the menu's current contents.
|
|
|
|
|
|
|
|
\sa popup(), sizeHint()
|
|
|
|
*/
|
|
|
|
|
|
|
|
int QPopupMenu::exec( const QPoint & pos, int indexAtPoint )
|
|
|
|
{
|
|
|
|
snapToMouse = TRUE;
|
|
|
|
if ( !qApp )
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
QPopupMenu* priorSyncMenu = syncMenu;
|
|
|
|
|
|
|
|
syncMenu = this;
|
|
|
|
syncMenuId = -1;
|
|
|
|
|
|
|
|
QGuardedPtr<QPopupMenu> that = this;
|
|
|
|
connectModal( that, TRUE );
|
|
|
|
popup( pos, indexAtPoint );
|
|
|
|
qApp->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 QPopupMenu::connectModal( QPopupMenu* receiver, bool doConnect )
|
|
|
|
{
|
|
|
|
if ( !receiver )
|
|
|
|
return;
|
|
|
|
|
|
|
|
connectModalRecursionSafety = doConnect;
|
|
|
|
|
|
|
|
if ( doConnect )
|
|
|
|
connect( this, SIGNAL(activated(int)),
|
|
|
|
receiver, SLOT(modalActivation(int)) );
|
|
|
|
else
|
|
|
|
disconnect( this, SIGNAL(activated(int)),
|
|
|
|
receiver, SLOT(modalActivation(int)) );
|
|
|
|
|
|
|
|
QMenuItemListIt it(*mitems);
|
|
|
|
register QMenuItem *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(QPoint(0,0)))}. In most
|
|
|
|
situations you'll want to specify the position yourself, for
|
|
|
|
example at the current mouse position:
|
|
|
|
\code
|
|
|
|
exec(QCursor::pos());
|
|
|
|
\endcode
|
|
|
|
or aligned to a widget:
|
|
|
|
\code
|
|
|
|
exec(somewidget.mapToGlobal(QPoint(0,0)));
|
|
|
|
\endcode
|
|
|
|
*/
|
|
|
|
|
|
|
|
int QPopupMenu::exec()
|
|
|
|
{
|
|
|
|
return exec(mapToGlobal(QPoint(0,0)));
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Internal slot used for exec(). */
|
|
|
|
|
|
|
|
void QPopupMenu::modalActivation( int id )
|
|
|
|
{
|
|
|
|
syncMenuId = id;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Sets the currently active item to index \a i and repaints as necessary.
|
|
|
|
*/
|
|
|
|
|
|
|
|
void QPopupMenu::setActiveItem( int i )
|
|
|
|
{
|
|
|
|
int lastActItem = actItem;
|
|
|
|
actItem = i;
|
|
|
|
if ( lastActItem >= 0 )
|
|
|
|
updateRow( lastActItem );
|
|
|
|
if ( i >= 0 && i != lastActItem )
|
|
|
|
updateRow( i );
|
|
|
|
QMenuItem *mi = mitems->at( actItem );
|
|
|
|
if ( !mi )
|
|
|
|
return;
|
|
|
|
|
|
|
|
if ( mi->widget() && mi->widget()->isFocusEnabled() ) {
|
|
|
|
mi->widget()->setFocus();
|
|
|
|
} else {
|
|
|
|
setFocus();
|
|
|
|
QRect mfrect = itemGeometry( actItem );
|
|
|
|
setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE );
|
|
|
|
}
|
|
|
|
if ( mi->id() != -1 )
|
|
|
|
hilitSig( mi->id() );
|
|
|
|
#ifndef QT_NO_WHATSTHIS
|
|
|
|
if (whatsThisItem && whatsThisItem != mi) {
|
|
|
|
qWhatsThisBDH();
|
|
|
|
}
|
|
|
|
whatsThisItem = mi;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
QSize QPopupMenu::sizeHint() const
|
|
|
|
{
|
|
|
|
constPolish();
|
|
|
|
QPopupMenu* that = (QPopupMenu*) this;
|
|
|
|
//We do not need a resize here, just the sizeHint..
|
|
|
|
return that->updateSize(FALSE).expandedTo( QApplication::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 QPopupMenu::idAt( const QPoint& pos ) const
|
|
|
|
{
|
|
|
|
return idAt( itemAtPos( pos ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\fn int QPopupMenu::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 QMenuData::setId(), QMenuData::indexOf()
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
bool QPopupMenu::customWhatsThis() const
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
bool QPopupMenu::focusNextPrevChild( bool next )
|
|
|
|
{
|
|
|
|
register QMenuItem *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() &&
|
|
|
|
( ( style().styleHint(QStyle::SH_PopupMenu_AllowActiveAndDisabled, this)
|
|
|
|
&& mi->isVisible() )
|
|
|
|
|| mi->isEnabledAndVisible() ) )
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if ( i != actItem )
|
|
|
|
setActiveItem( i );
|
|
|
|
}
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
void QPopupMenu::focusInEvent( QFocusEvent * )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
void QPopupMenu::focusOutEvent( QFocusEvent * )
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
class QTearOffMenuItem : public QCustomMenuItem
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
QTearOffMenuItem()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
~QTearOffMenuItem()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
void paint( QPainter* p, const QColorGroup& cg, bool /* act*/,
|
|
|
|
bool /*enabled*/, int x, int y, int w, int h )
|
|
|
|
{
|
|
|
|
p->setPen( QPen( cg.dark(), 1, DashLine ) );
|
|
|
|
p->drawLine( x+2, y+h/2-1, x+w-4, y+h/2-1 );
|
|
|
|
p->setPen( QPen( cg.light(), 1, DashLine ) );
|
|
|
|
p->drawLine( x+2, y+h/2, x+w-4, y+h/2 );
|
|
|
|
}
|
|
|
|
bool fullSpan() const
|
|
|
|
{
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
QSize sizeHint()
|
|
|
|
{
|
|
|
|
return QSize( 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
|
|
|
|
contains 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 QPopupMenu::insertTearOffHandle( int id, int index )
|
|
|
|
{
|
|
|
|
int myid = insertItem( new QTearOffMenuItem, id, index );
|
|
|
|
connectItem( myid, this, SLOT( toggleTearOff() ) );
|
|
|
|
QMenuData::d->aInt = myid;
|
|
|
|
return myid;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*!\internal
|
|
|
|
|
|
|
|
implements tear-off menus
|
|
|
|
*/
|
|
|
|
void QPopupMenu::toggleTearOff()
|
|
|
|
{
|
|
|
|
if ( active_popup_menu && active_popup_menu->tornOff ) {
|
|
|
|
active_popup_menu->close();
|
|
|
|
} else if (QMenuData::d->aWidget ) {
|
|
|
|
delete (QWidget*) QMenuData::d->aWidget; // delete the old one
|
|
|
|
} else {
|
|
|
|
// create a tear off menu
|
|
|
|
QPopupMenu* p = new QPopupMenu( parentWidget(), "tear off menu" );
|
|
|
|
connect( p, SIGNAL( activated(int) ), this, SIGNAL( activated(int) ) );
|
|
|
|
connect( p, SIGNAL( highlighted(int) ), this, SIGNAL( highlighted(int) ) );
|
|
|
|
#ifndef QT_NO_WIDGET_TOPEXTRA
|
|
|
|
p->setCaption( caption() );
|
|
|
|
#endif
|
|
|
|
p->setCheckable( isCheckable() );
|
|
|
|
p->reparent( parentWidget(), WType_TopLevel | WStyle_Tool |
|
|
|
|
WNoAutoErase | WDestructiveClose,
|
|
|
|
geometry().topLeft(), FALSE );
|
|
|
|
p->mitems->setAutoDelete( FALSE );
|
|
|
|
p->tornOff = TRUE;
|
|
|
|
#ifdef Q_WS_X11
|
|
|
|
p->x11SetWindowType( X11WindowTypeMenu );
|
|
|
|
#endif
|
|
|
|
for ( QMenuItemListIt it( *mitems ); it.current(); ++it ) {
|
|
|
|
if ( it.current()->id() != QMenuData::d->aInt && !it.current()->widget() )
|
|
|
|
p->mitems->append( it.current() );
|
|
|
|
}
|
|
|
|
p->show();
|
|
|
|
QMenuData::d->aWidget = p;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\reimp
|
|
|
|
*/
|
|
|
|
void QPopupMenu::activateItemAt( int index )
|
|
|
|
{
|
|
|
|
if ( index >= 0 && index < (int) mitems->count() ) {
|
|
|
|
QMenuItem *mi = mitems->at( index );
|
|
|
|
if ( index != actItem ) // new item activated
|
|
|
|
setActiveItem( index );
|
|
|
|
QPopupMenu *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 QT_NO_WHATSTHIS
|
|
|
|
bool b = QWhatsThis::inWhatsThisMode();
|
|
|
|
#else
|
|
|
|
const bool b = FALSE;
|
|
|
|
#endif
|
|
|
|
if ( !mi->isEnabledAndVisible() ) {
|
|
|
|
#ifndef QT_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;
|
|
|
|
QGuardedPtr<QSignal> signal = mi->signal();
|
|
|
|
actSig( mi->id(), b );
|
|
|
|
if ( signal && !b )
|
|
|
|
signal->activate();
|
|
|
|
active_popup_menu = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if ( tornOff ) {
|
|
|
|
close();
|
|
|
|
} else {
|
|
|
|
QMenuData* p = parentMenu;
|
|
|
|
hide();
|
|
|
|
#ifndef QT_NO_MENUBAR
|
|
|
|
if ( p && p->isMenuBar )
|
|
|
|
((QMenuBar*) p)->goodbye( TRUE );
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
/*! \internal
|
|
|
|
This private function is to update the scroll states in styles that support scrolling. */
|
|
|
|
void
|
|
|
|
QPopupMenu::updateScrollerState()
|
|
|
|
{
|
|
|
|
uint old_scrollable = d->scroll.scrollable;
|
|
|
|
d->scroll.scrollable = QPopupMenuPrivate::Scroll::ScrollNone;
|
|
|
|
if(!style().styleHint(QStyle::SH_PopupMenu_Scrollable, this))
|
|
|
|
return;
|
|
|
|
|
|
|
|
QMenuItem *mi;
|
|
|
|
QMenuItemListIt 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 = style().pixelMetric(QStyle::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 | QPopupMenuPrivate::Scroll::ScrollUp;
|
|
|
|
y += sh;
|
|
|
|
}
|
|
|
|
while ( (mi=it.current()) ) {
|
|
|
|
++it;
|
|
|
|
int myheight = contentsRect().height();
|
|
|
|
QSize sz = style().sizeFromContents(QStyle::CT_PopupMenuItem, this,
|
|
|
|
QSize(0, itemHeight( mi )),
|
|
|
|
QStyleOption(mi,maxPMWidth));
|
|
|
|
if(y + sz.height() >= myheight) {
|
|
|
|
d->scroll.scrollable = d->scroll.scrollable | QPopupMenuPrivate::Scroll::ScrollDown;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
y += sz.height();
|
|
|
|
}
|
|
|
|
if((d->scroll.scrollable & QPopupMenuPrivate::Scroll::ScrollUp) &&
|
|
|
|
!(old_scrollable & QPopupMenuPrivate::Scroll::ScrollUp))
|
|
|
|
d->scroll.topScrollableIndex++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
Calculates the height in pixels of the menu item \a mi.
|
|
|
|
*/
|
|
|
|
int QPopupMenu::menuItemHeight( QMenuItem *mi, QFontMetrics fm )
|
|
|
|
{
|
|
|
|
if ( mi->widget() )
|
|
|
|
return mi->widget()->height();
|
|
|
|
if ( mi->custom() && mi->custom()->fullSpan() )
|
|
|
|
return mi->custom()->sizeHint().height();
|
|
|
|
|
|
|
|
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 = QMAX(h, mi->iconSet()->pixmap( QIconSet::Small,
|
|
|
|
QIconSet::Normal ).height());
|
|
|
|
if ( mi->custom() )
|
|
|
|
h = QMAX(h, mi->custom()->sizeHint().height());
|
|
|
|
|
|
|
|
return h;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // QT_NO_POPUPMENU
|
|
|
|
|