diff options
Diffstat (limited to 'src/widgets')
122 files changed, 87387 insertions, 0 deletions
diff --git a/src/widgets/qaction.cpp b/src/widgets/qaction.cpp new file mode 100644 index 0000000..1e68de0 --- /dev/null +++ b/src/widgets/qaction.cpp @@ -0,0 +1,2144 @@ +/**************************************************************************** +** +** Implementation of QAction class +** +** Copyright (C) 2000-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 "qaction.h" + +#ifndef QT_NO_ACTION + +#include "qtoolbar.h" +#include "qptrlist.h" +#include "qpopupmenu.h" +#include "qaccel.h" +#include "qtoolbutton.h" +#include "qcombobox.h" +#include "qtooltip.h" +#include "qwhatsthis.h" +#include "qstatusbar.h" +#include "qobjectlist.h" + + +/*! + \class QAction qaction.h + \brief The QAction class provides an abstract user interface + action that can appear both in menus and tool bars. + + \ingroup basic + \ingroup application + \mainclass + + In GUI applications many commands can be invoked via a menu + option, a toolbar button and a keyboard accelerator. Since the + same action must be performed regardless of how the action was + invoked, and since the menu and toolbar should be kept in sync, it + is useful to represent a command as an \e action. An action can be + added to a menu and a toolbar and will automatically keep them in + sync. For example, if the user presses a Bold toolbar button the + Bold menu item will automatically be checked. + + A QAction may contain an icon, a menu text, an accelerator, a + status text, a whats this text and a tool tip. Most of these can + be set in the constructor. They can also be set independently with + setIconSet(), setText(), setMenuText(), setToolTip(), + setStatusTip(), setWhatsThis() and setAccel(). + + An action may be a toggle action e.g. a Bold toolbar button, or a + command action, e.g. 'Open File' to invoke an open file dialog. + Toggle actions emit the toggled() signal when their state changes. + Both command and toggle actions emit the activated() signal when + they are invoked. Use setToggleAction() to set an action's toggled + status. To see if an action is a toggle action use + isToggleAction(). A toggle action may be "on", isOn() returns + TRUE, or "off", isOn() returns FALSE. + + Actions are added to widgets (menus or toolbars) using addTo(), + and removed using removeFrom(). + + Once a QAction has been created it should be added to the relevant + menu and toolbar and then connected to the slot which will perform + the action. For example: + + \quotefile action/application.cpp + \skipto QPixmap( fileopen + \printuntil connect + + We create a "File Save" action with a menu text of "&Save" and + \e{Ctrl+S} as the keyboard accelerator. We connect the + fileSaveAction's activated() signal to our own save() slot. Note + that at this point there is no menu or toolbar action, we'll add + them next: + + \skipto new QToolBar + \printline + \skipto fileSaveAction->addTo + \printline + \skipto new QPopupMenu + \printuntil insertItem + \skipto fileSaveAction->addTo + \printline + + We create a toolbar and add our fileSaveAction to it. Similarly we + create a menu, add a top-level menu item, and add our + fileSaveAction. + + We recommend that actions are created as children of the window + that they are used in. In most cases actions will be children of + the application's main window. + + To prevent recursion, don't create an action as a child of a + widget that the action is later added to. +*/ + +class QActionPrivate +{ +public: + QActionPrivate(QAction *act); + ~QActionPrivate(); + QIconSet *iconset; + QString text; + QString menutext; + QString tooltip; + QString statustip; + QString whatsthis; +#ifndef QT_NO_ACCEL + QKeySequence key; + QAccel* accel; + int accelid; +#endif + uint enabled : 1; + uint visible : 1; + uint toggleaction : 1; + uint on : 1; + uint forceDisabled : 1; + uint forceInvisible : 1; +#ifndef QT_NO_TOOLTIP + QToolTipGroup tipGroup; +#endif + QActionGroupPrivate* d_group; + QAction *action; + + struct MenuItem { + MenuItem():popup(0),id(0){} + QPopupMenu* popup; + int id; + }; + // ComboItem is only necessary for actions that are + // in dropdown/exclusive actiongroups. The actiongroup + // will clean this up + struct ComboItem { + ComboItem():combo(0), id(0) {} + QComboBox *combo; + int id; + }; + QPtrList<MenuItem> menuitems; + QPtrList<QToolButton> toolbuttons; + QPtrList<ComboItem> comboitems; + + enum Update { Icons = 1, Visibility = 2, State = 4, EverythingElse = 8 }; + void update( uint upd = EverythingElse ); + + QString menuText() const; + QString toolTip() const; + QString statusTip() const; +}; + +QActionPrivate::QActionPrivate(QAction *act) + : iconset( 0 ), +#ifndef QT_NO_ACCEL + key( 0 ), accel( 0 ), accelid( 0 ), +#endif + enabled( TRUE ), visible( TRUE ), toggleaction( FALSE ), on( FALSE ), + forceDisabled( FALSE ), forceInvisible( FALSE ), +#ifndef QT_NO_TOOLTIP + tipGroup( 0 ), +#endif + d_group( 0 ), action(act) +{ + menuitems.setAutoDelete( TRUE ); + comboitems.setAutoDelete( TRUE ); +#ifndef QT_NO_TOOLTIP + tipGroup.setDelay( FALSE ); +#endif +} + +QActionPrivate::~QActionPrivate() +{ + QPtrListIterator<QToolButton> ittb( toolbuttons ); + QToolButton *tb; + + while ( ( tb = ittb.current() ) ) { + ++ittb; + delete tb; + } + + QPtrListIterator<QActionPrivate::MenuItem> itmi( menuitems); + QActionPrivate::MenuItem* mi; + while ( ( mi = itmi.current() ) ) { + ++itmi; + QPopupMenu* menu = mi->popup; + if ( menu->findItem( mi->id ) ) + menu->removeItem( mi->id ); + } + + QPtrListIterator<QActionPrivate::ComboItem> itci(comboitems); + QActionPrivate::ComboItem* ci; + while ( ( ci = itci.current() ) ) { + ++itci; + QComboBox* combo = ci->combo; + combo->clear(); + QActionGroup *group = ::qt_cast<QActionGroup*>(action->parent()); + QObjectList *siblings = group ? group->queryList("QAction") : 0; + if (siblings) { + QObjectListIt it(*siblings); + while (it.current()) { + QAction *sib = ::qt_cast<QAction*>(it.current()); + ++it; + sib->removeFrom(combo); + } + it = QObjectListIt(*siblings); + while (it.current()) { + QAction *sib = ::qt_cast<QAction*>(it.current()); + ++it; + if (sib == action) + continue; + sib->addTo(combo); + } + } + delete siblings; + } + +#ifndef QT_NO_ACCEL + delete accel; +#endif + delete iconset; +} + +class QActionGroupPrivate +{ +public: + uint exclusive: 1; + uint dropdown: 1; + QPtrList<QAction> actions; + QAction* selected; + QAction* separatorAction; + + struct MenuItem { + MenuItem():popup(0),id(0){} + QPopupMenu* popup; + int id; + }; + + QPtrList<QComboBox> comboboxes; + QPtrList<QToolButton> menubuttons; + QPtrList<MenuItem> menuitems; + QPtrList<QPopupMenu> popupmenus; + + void update( const QActionGroup * ); +}; + +void QActionPrivate::update( uint upd ) +{ + for ( QPtrListIterator<MenuItem> it( menuitems); it.current(); ++it ) { + MenuItem* mi = it.current(); + QString t = menuText(); +#ifndef QT_NO_ACCEL + if ( key ) + t += '\t' + QAccel::keyToString( key ); +#endif + if ( upd & State ) { + mi->popup->setItemEnabled( mi->id, enabled ); + if ( toggleaction ) + mi->popup->setItemChecked( mi->id, on ); + } + if ( upd & Visibility ) + mi->popup->setItemVisible( mi->id, visible ); + + if ( upd & Icons ) + if ( iconset ) + mi->popup->changeItem( mi->id, *iconset, t ); + else + mi->popup->changeItem( mi->id, QIconSet(), t ); + if ( upd & EverythingElse ) { + mi->popup->changeItem( mi->id, t ); + if ( !whatsthis.isEmpty() ) + mi->popup->setWhatsThis( mi->id, whatsthis ); + if ( toggleaction ) { + mi->popup->setCheckable( TRUE ); + mi->popup->setItemChecked( mi->id, on ); + } + } + } + for ( QPtrListIterator<QToolButton> it2(toolbuttons); it2.current(); ++it2 ) { + QToolButton* btn = it2.current(); + if ( upd & State ) { + btn->setEnabled( enabled ); + if ( toggleaction ) + btn->setOn( on ); + } + if ( upd & Visibility ) + visible ? btn->show() : btn->hide(); + if ( upd & Icons ) { + if ( iconset ) + btn->setIconSet( *iconset ); + else + btn->setIconSet( QIconSet() ); + } + if ( upd & EverythingElse ) { + btn->setToggleButton( toggleaction ); + if ( !text.isEmpty() ) + btn->setTextLabel( text, FALSE ); +#ifndef QT_NO_TOOLTIP + QToolTip::remove( btn ); + QToolTip::add( btn, toolTip(), &tipGroup, statusTip() ); +#endif +#ifndef QT_NO_WHATSTHIS + QWhatsThis::remove( btn ); + if ( !whatsthis.isEmpty() ) + QWhatsThis::add( btn, whatsthis ); +#endif + } + } +#ifndef QT_NO_ACCEL + if ( accel ) { + accel->setEnabled( enabled && visible ); + if ( !whatsthis.isEmpty() ) + accel->setWhatsThis( accelid, whatsthis ); + } +#endif + // Only used by actiongroup + for ( QPtrListIterator<ComboItem> it3( comboitems ); it3.current(); ++it3 ) { + ComboItem *ci = it3.current(); + if ( !ci->combo ) + return; + if ( iconset ) + ci->combo->changeItem( iconset->pixmap(), text, ci->id ); + else + ci->combo->changeItem( text, ci->id ); + } +} + +QString QActionPrivate::menuText() const +{ + if ( menutext.isNull() ) { + QString t(text); + t.replace('&', "&&"); + return t; + } + return menutext; +} + +QString QActionPrivate::toolTip() const +{ + if ( tooltip.isNull() ) { +#ifndef QT_NO_ACCEL + if ( accel ) + return text + " (" + QAccel::keyToString( accel->key( accelid )) + ")"; +#endif + return text; + } + return tooltip; +} + +QString QActionPrivate::statusTip() const +{ + if ( statustip.isNull() ) + return toolTip(); + return statustip; +} + +/* + internal: guesses a descriptive text from a menu text + */ +static QString qt_stripMenuText( QString s ) +{ + s.remove( QString::fromLatin1("...") ); + s.remove( QChar('&' ) ); + return s.stripWhiteSpace(); +} + +/*! + Constructs an action called \a name with parent \a parent. + + If \a parent is a QActionGroup, the new action inserts itself into + \a parent. + + For accelerators and status tips to work, \a parent must either be + a widget, or an action group whose parent is a widget. + + \warning To prevent recursion, don't create an action as a child + of a widget that the action is later added to. +*/ +QAction::QAction( QObject* parent, const char* name ) + : QObject( parent, name ) +{ + d = new QActionPrivate(this); + init(); +} + +/*! \obsolete + Constructs an action called \a name with parent \a parent. + + If \a toggle is TRUE the action will be a toggle action, otherwise + it will be a command action. + + If \a parent is a QActionGroup, the new action inserts itself into + \a parent. + + For accelerators and status tips to work, \a parent must either be + a widget, or an action group whose parent is a widget. +*/ +QAction::QAction( QObject* parent, const char* name, bool toggle ) + : QObject( parent, name ) +{ + d = new QActionPrivate(this); + d->toggleaction = toggle; + init(); +} + + +#ifndef QT_NO_ACCEL + +/*! + This constructor creates an action with the following properties: + the icon or iconset \a icon, the menu text \a menuText and + keyboard accelerator \a accel. It is a child of \a parent and + called \a name. + + If \a parent is a QActionGroup, the action automatically becomes + a member of it. + + For accelerators and status tips to work, \a parent must either be + a widget, or an action group whose parent is a widget. + + The action uses a stripped version of \a menuText (e.g. "\&Menu + Option..." becomes "Menu Option") as descriptive text for + toolbuttons. You can override this by setting a specific + description with setText(). The same text and \a accel will be + used for tool tips and status tips unless you provide text for + these using setToolTip() and setStatusTip(). + + Call setToggleAction(TRUE) to make the action a toggle action. + + \warning To prevent recursion, don't create an action as a child + of a widget that the action is later added to. +*/ +QAction::QAction( const QIconSet& icon, const QString& menuText, QKeySequence accel, + QObject* parent, const char* name ) + : QObject( parent, name ) +{ + d = new QActionPrivate(this); + if ( !icon.isNull() ) + setIconSet( icon ); + d->text = qt_stripMenuText( menuText ); + d->menutext = menuText; + setAccel( accel ); + init(); +} + +/*! + This constructor results in an icon-less action with the the menu + text \a menuText and keyboard accelerator \a accel. It is a child + of \a parent and called \a name. + + If \a parent is a QActionGroup, the action automatically becomes + a member of it. + + For accelerators and status tips to work, \a parent must either be + a widget, or an action group whose parent is a widget. + + The action uses a stripped version of \a menuText (e.g. "\&Menu + Option..." becomes "Menu Option") as descriptive text for + toolbuttons. You can override this by setting a specific + description with setText(). The same text and \a accel will be + used for tool tips and status tips unless you provide text for + these using setToolTip() and setStatusTip(). + + Call setToggleAction(TRUE) to make the action a toggle action. + + \warning To prevent recursion, don't create an action as a child + of a widget that the action is later added to. +*/ +QAction::QAction( const QString& menuText, QKeySequence accel, + QObject* parent, const char* name ) + : QObject( parent, name ) +{ + d = new QActionPrivate(this); + d->text = qt_stripMenuText( menuText ); + d->menutext = menuText; + setAccel( accel ); + init(); +} + +/*! \obsolete + This constructor creates an action with the following properties: + the description \a text, the icon or iconset \a icon, the menu + text \a menuText and keyboard accelerator \a accel. It is a child + of \a parent and called \a name. If \a toggle is TRUE the action + will be a toggle action, otherwise it will be a command action. + + If \a parent is a QActionGroup, the action automatically becomes + a member of it. + + For accelerators and status tips to work, \a parent must either be + a widget, or an action group whose parent is a widget. + + The \a text and \a accel will be used for tool tips and status + tips unless you provide specific text for these using setToolTip() + and setStatusTip(). +*/ +QAction::QAction( const QString& text, const QIconSet& icon, const QString& menuText, QKeySequence accel, QObject* parent, const char* name, bool toggle ) + : QObject( parent, name ) +{ + d = new QActionPrivate(this); + d->toggleaction = toggle; + if ( !icon.isNull() ) + setIconSet( icon ); + + d->text = text; + d->menutext = menuText; + setAccel( accel ); + init(); +} + +/*! \obsolete + This constructor results in an icon-less action with the + description \a text, the menu text \a menuText and the keyboard + accelerator \a accel. Its parent is \a parent and it is called \a + name. If \a toggle is TRUE the action will be a toggle action, + otherwise it will be a command action. + + The action automatically becomes a member of \a parent if \a + parent is a QActionGroup. + + For accelerators and status tips to work, \a parent must either be + a widget, or an action group whose parent is a widget. + + The \a text and \a accel will be used for tool tips and status + tips unless you provide specific text for these using setToolTip() + and setStatusTip(). +*/ +QAction::QAction( const QString& text, const QString& menuText, QKeySequence accel, QObject* parent, const char* name, bool toggle ) + : QObject( parent, name ) +{ + d = new QActionPrivate(this); + d->toggleaction = toggle; + d->text = text; + d->menutext = menuText; + setAccel( accel ); + init(); +} +#endif + +/*! + \internal +*/ +void QAction::init() +{ + if ( ::qt_cast<QActionGroup*>(parent()) ) + ((QActionGroup*) parent())->add( this ); // insert into action group +} + +/*! + Destroys the object and frees allocated resources. +*/ + +QAction::~QAction() +{ + delete d; +} + +/*! + \property QAction::iconSet + \brief the action's icon + + The icon is used as the tool button icon and in the menu to the + left of the menu text. There is no default icon. + + If a null icon (QIconSet::isNull() is passed into this function, + the icon of the action is cleared. + + (See the action/toggleaction/toggleaction.cpp example.) + +*/ +void QAction::setIconSet( const QIconSet& icon ) +{ + register QIconSet *i = d->iconset; + if ( !icon.isNull() ) + d->iconset = new QIconSet( icon ); + else + d->iconset = 0; + delete i; + d->update( QActionPrivate::Icons ); +} + +QIconSet QAction::iconSet() const +{ + if ( d->iconset ) + return *d->iconset; + return QIconSet(); +} + +/*! + \property QAction::text + \brief the action's descriptive text + + If \l QMainWindow::usesTextLabel is TRUE, the text appears as a + label in the relevant tool button. It also serves as the default + text in menus and tool tips if these have not been specifically + defined. There is no default text. + + \sa setMenuText() setToolTip() setStatusTip() +*/ +void QAction::setText( const QString& text ) +{ + d->text = text; + d->update(); +} + +QString QAction::text() const +{ + return d->text; +} + + +/*! + \property QAction::menuText + \brief the action's menu text + + If the action is added to a menu the menu option will consist of + the icon (if there is one), the menu text and the accelerator (if + there is one). If the menu text is not explicitly set in the + constructor or by using setMenuText() the action's description + text will be used as the menu text. There is no default menu text. + + \sa text +*/ +void QAction::setMenuText( const QString& text ) +{ + if ( d->menutext == text ) + return; + + d->menutext = text; + d->update(); +} + +QString QAction::menuText() const +{ + return d->menuText(); +} + +/*! + \property QAction::toolTip + \brief the action's tool tip + + This text is used for the tool tip. If no status tip has been set + the tool tip will be used for the status tip. + + If no tool tip is specified the action's text is used, and if that + hasn't been specified the description text is used as the tool tip + text. + + There is no default tool tip text. + + \sa setStatusTip() setAccel() +*/ +void QAction::setToolTip( const QString& tip ) +{ + if ( d->tooltip == tip ) + return; + + d->tooltip = tip; + d->update(); +} + +QString QAction::toolTip() const +{ + return d->toolTip(); +} + +/*! + \property QAction::statusTip + \brief the action's status tip + + The statusTip is displayed on all status bars that this action's + toplevel parent widget provides. + + If no status tip is defined, the action uses the tool tip text. + + There is no default statusTip text. + + \sa setStatusTip() setToolTip() +*/ +//#### Please reimp for QActionGroup! +//#### For consistency reasons even action groups should show +//#### status tips (as they already do with tool tips) +//#### Please change QActionGroup class doc appropriately after +//#### reimplementation. +void QAction::setStatusTip( const QString& tip ) +{ + if ( d->statustip == tip ) + return; + + d->statustip = tip; + d->update(); +} + +QString QAction::statusTip() const +{ + return d->statusTip(); +} + +/*! + \property QAction::whatsThis + \brief the action's "What's This?" help text + + The whats this text is used to provide a brief description of the + action. The text may contain rich text (HTML-like tags -- see + QStyleSheet for the list of supported tags). There is no default + "What's This" text. + + \sa QWhatsThis +*/ +void QAction::setWhatsThis( const QString& whatsThis ) +{ + if ( d->whatsthis == whatsThis ) + return; + d->whatsthis = whatsThis; + d->update(); +} + +QString QAction::whatsThis() const +{ + return d->whatsthis; +} + + +#ifndef QT_NO_ACCEL +/*! + \property QAction::accel + \brief the action's accelerator key + + The keycodes can be found in \l Qt::Key and \l Qt::Modifier. There + is no default accelerator key. +*/ +//#### Please reimp for QActionGroup! +//#### For consistency reasons even QActionGroups should respond to +//#### their accelerators and e.g. open the relevant submenu. +//#### Please change appropriate QActionGroup class doc after +//#### reimplementation. +void QAction::setAccel( const QKeySequence& key ) +{ + if ( d->key == key ) + return; + + d->key = key; + delete d->accel; + d->accel = 0; + + if ( !(int)key ) { + d->update(); + return; + } + + QObject* p = parent(); + while ( p && !p->isWidgetType() ) { + p = p->parent(); + } + if ( p ) { + d->accel = new QAccel( (QWidget*)p, this, "qt_action_accel" ); + d->accelid = d->accel->insertItem( d->key ); + d->accel->connectItem( d->accelid, this, SLOT( internalActivation() ) ); + } +#if defined(QT_CHECK_STATE) + else + qWarning( "QAction::setAccel() (%s) requires widget in parent chain", name() ); +#endif + d->update(); +} + + +QKeySequence QAction::accel() const +{ + return d->key; +} +#endif + + +/*! + \property QAction::toggleAction + \brief whether the action is a toggle action + + A toggle action is one which has an on/off state. For example a + Bold toolbar button is either on or off. An action which is not a + toggle action is a command action; a command action is simply + executed, e.g. file save. This property's default is FALSE. + + In some situations, the state of one toggle action should depend + on the state of others. For example, "Left Align", "Center" and + "Right Align" toggle actions are mutually exclusive. To achieve + exclusive toggling, add the relevant toggle actions to a + QActionGroup with the \l QActionGroup::exclusive property set to + TRUE. +*/ +void QAction::setToggleAction( bool enable ) +{ + if ( enable == (bool)d->toggleaction ) + return; + + if ( !enable ) + d->on = FALSE; + + d->toggleaction = enable; + d->update(); +} + +bool QAction::isToggleAction() const +{ + return d->toggleaction; +} + +/*! + Activates the action and executes all connected slots. + This only works for actions that are not toggle action. + + \sa toggle() +*/ +void QAction::activate() +{ + if ( isToggleAction() ) { +#if defined(QT_CHECK_STATE) + qWarning( "QAction::%s() (%s) Toggle actions " + "can not be activated", "activate", name() ); +#endif + return; + } + emit activated(); +} + +/*! + Toggles the state of a toggle action. + + \sa on, activate(), toggled(), isToggleAction() +*/ +void QAction::toggle() +{ + if ( !isToggleAction() ) { +#if defined(QT_CHECK_STATE) + qWarning( "QAction::%s() (%s) Only toggle actions " + "can be switched", "toggle", name() ); +#endif + return; + } + setOn( !isOn() ); +} + +/*! + \property QAction::on + \brief whether a toggle action is on + + This property is always on (TRUE) for command actions and + \l{QActionGroup}s; setOn() has no effect on them. For action's + where isToggleAction() is TRUE, this property's default value is + off (FALSE). + + \sa toggleAction +*/ +void QAction::setOn( bool enable ) +{ + if ( !isToggleAction() ) { +#if defined(QT_CHECK_STATE) + if ( enable ) + qWarning( "QAction::%s() (%s) Only toggle actions " + "can be switched", "setOn", name() ); +#endif + return; + } + if ( enable == (bool)d->on ) + return; + d->on = enable; + d->update( QActionPrivate::State ); + emit toggled( enable ); +} + +bool QAction::isOn() const +{ + return d->on; +} + +/*! + \property QAction::enabled + \brief whether the action is enabled + + Disabled actions can't be chosen by the user. They don't disappear + from the menu/tool bar but are displayed in a way which indicates + that they are unavailable, e.g. they might be displayed grayed + out. + + What's this? help on disabled actions is still available provided + the \l QAction::whatsThis property is set. +*/ +void QAction::setEnabled( bool enable ) +{ + d->forceDisabled = !enable; + + if ( (bool)d->enabled == enable ) + return; + + d->enabled = enable; + d->update( QActionPrivate::State ); +} + +bool QAction::isEnabled() const +{ + return d->enabled; +} + +/*! + Disables the action if \a disable is TRUE; otherwise + enables the action. + + See the \l enabled documentation for more information. +*/ +void QAction::setDisabled( bool disable ) +{ + setEnabled( !disable ); +} + +/*! + \property QAction::visible + \brief whether the action can be seen (e.g. in menus and toolbars) + + If \e visible is TRUE the action can be seen (e.g. in menus and + toolbars) and chosen by the user; if \e visible is FALSE the + action cannot be seen or chosen by the user. + + Actions which are not visible are \e not grayed out; they do not + appear at all. +*/ +void QAction::setVisible( bool visible ) +{ + d->forceInvisible = !visible; + + if ( (bool)d->visible == visible ) + return; + d->visible = visible; + d->update( QActionPrivate::Visibility ); +#if (QT_VERSION-0 >= 0x040000) +#error "QAction::setVisible function wants to be virtual. Also add virtual change() function" +#endif + if ( d->d_group ) //### this function wants to be virtual in 4.0 + d->d_group->update( (QActionGroup*) this ); +} + +/* + Returns TRUE if the action is visible (e.g. in menus and + toolbars); otherwise returns FALSE. +*/ +bool QAction::isVisible() const +{ + return d->visible; +} + +/*! \internal +*/ +void QAction::internalActivation() +{ + if ( isToggleAction() ) + setOn( !isOn() ); + emit activated(); +} + +/*! \internal +*/ +void QAction::toolButtonToggled( bool on ) +{ + if ( !isToggleAction() ) + return; + setOn( on ); +} + +/*! + Adds this action to widget \a w. + + Currently actions may be added to QToolBar and QPopupMenu widgets. + + An action added to a tool bar is automatically displayed as a tool + button; an action added to a pop up menu appears as a menu option. + + addTo() returns TRUE if the action was added successfully and + FALSE otherwise. (If \a w is not a QToolBar or QPopupMenu the + action will not be added and FALSE will be returned.) + + \sa removeFrom() +*/ +bool QAction::addTo( QWidget* w ) +{ +#ifndef QT_NO_TOOLBAR + if ( ::qt_cast<QToolBar*>(w) ) { + if ( !qstrcmp( name(), "qt_separator_action" ) ) { + ((QToolBar*)w)->addSeparator(); + } else { + QCString bname = name() + QCString( "_action_button" ); + QToolButton* btn = new QToolButton( (QToolBar*) w, bname ); + addedTo( btn, w ); + btn->setToggleButton( d->toggleaction ); + d->toolbuttons.append( btn ); + if ( d->iconset ) + btn->setIconSet( *d->iconset ); + d->update( QActionPrivate::State | QActionPrivate::Visibility | QActionPrivate::EverythingElse ) ; + connect( btn, SIGNAL( clicked() ), this, SIGNAL( activated() ) ); + connect( btn, SIGNAL( toggled(bool) ), this, SLOT( toolButtonToggled(bool) ) ); + connect( btn, SIGNAL( destroyed() ), this, SLOT( objectDestroyed() ) ); +#ifndef QT_NO_TOOLTIP + connect( &(d->tipGroup), SIGNAL(showTip(const QString&)), this, SLOT(showStatusText(const QString&)) ); + connect( &(d->tipGroup), SIGNAL(removeTip()), this, SLOT(clearStatusText()) ); +#endif + } + } else +#endif + if ( ::qt_cast<QPopupMenu*>(w) ) { + QActionPrivate::MenuItem* mi = new QActionPrivate::MenuItem; + mi->popup = (QPopupMenu*) w; + QIconSet* diconset = d->iconset; + if ( !qstrcmp( name(), "qt_separator_action" ) ) + mi->id = ((QPopupMenu*)w)->insertSeparator(); + else if ( diconset ) + mi->id = mi->popup->insertItem( *diconset, QString::fromLatin1("") ); + else + mi->id = mi->popup->insertItem( QString::fromLatin1("") ); + addedTo( mi->popup->indexOf( mi->id ), mi->popup ); + mi->popup->connectItem( mi->id, this, SLOT(internalActivation()) ); + d->menuitems.append( mi ); + d->update( QActionPrivate::State | QActionPrivate::Visibility | QActionPrivate::EverythingElse ) ; + w->topLevelWidget()->className(); + connect( mi->popup, SIGNAL(highlighted(int)), this, SLOT(menuStatusText(int)) ); + connect( mi->popup, SIGNAL(aboutToHide()), this, SLOT(clearStatusText()) ); + connect( mi->popup, SIGNAL( destroyed() ), this, SLOT( objectDestroyed() ) ); + // Makes only sense when called by QActionGroup::addTo + } else if ( ::qt_cast<QComboBox*>(w) ) { + QActionPrivate::ComboItem *ci = new QActionPrivate::ComboItem; + ci->combo = (QComboBox*)w; + connect( ci->combo, SIGNAL( destroyed() ), this, SLOT( objectDestroyed() ) ); + ci->id = ci->combo->count(); + if ( qstrcmp( name(), "qt_separator_action" ) ) { + if ( d->iconset ) + ci->combo->insertItem( d->iconset->pixmap(), text() ); + else + ci->combo->insertItem( text() ); + } else { + ci->id = -1; + } + d->comboitems.append( ci ); + + d->update( QActionPrivate::State | QActionPrivate::EverythingElse ); + } else { + qWarning( "QAction::addTo(), unknown object" ); + return FALSE; + } + return TRUE; +} + +/*! + This function is called from the addTo() function when it has + created a widget (\a actionWidget) for the action in the \a + container. +*/ + +void QAction::addedTo( QWidget *actionWidget, QWidget *container ) +{ + Q_UNUSED( actionWidget ); + Q_UNUSED( container ); +} + +/*! + \overload + + This function is called from the addTo() function when it has + created a menu item at the index position \a index in the popup + menu \a menu. +*/ + +void QAction::addedTo( int index, QPopupMenu *menu ) +{ + Q_UNUSED( index ); + Q_UNUSED( menu ); +} + +/*! + Sets the status message to \a text +*/ +void QAction::showStatusText( const QString& text ) +{ +#ifndef QT_NO_STATUSBAR + // find out whether we are clearing the status bar by the popup that actually set the text + static QPopupMenu *lastmenu = 0; + QObject *s = (QObject*)sender(); + if ( s ) { + QPopupMenu *menu = (QPopupMenu*)s->qt_cast( "QPopupMenu" ); + if ( menu && !!text ) + lastmenu = menu; + else if ( menu && text.isEmpty() ) { + if ( lastmenu && menu != lastmenu ) + return; + lastmenu = 0; + } + } + + QObject* par = parent(); + QObject* lpar = 0; + QStatusBar *bar = 0; + while ( par && !bar ) { + lpar = par; + bar = (QStatusBar*)par->child( 0, "QStatusBar", FALSE ); + par = par->parent(); + } + if ( !bar && lpar ) { + QObjectList *l = lpar->queryList( "QStatusBar" ); + if ( !l ) + return; + // #### hopefully the last one is the one of the mainwindow... + bar = (QStatusBar*)l->last(); + delete l; + } + if ( bar ) { + if ( text.isEmpty() ) + bar->clear(); + else + bar->message( text ); + } +#endif +} + +/*! + Sets the status message to the menu item's status text, or to the + tooltip, if there is no status text. +*/ +void QAction::menuStatusText( int id ) +{ + static int lastId = 0; + QString text; + QPtrListIterator<QActionPrivate::MenuItem> it( d->menuitems); + QActionPrivate::MenuItem* mi; + while ( ( mi = it.current() ) ) { + ++it; + if ( mi->id == id ) { + text = statusTip(); + break; + } + } + + if ( !text.isEmpty() ) + showStatusText( text ); + else if ( id != lastId ) + clearStatusText(); + lastId = id; +} + +/*! + Clears the status text. +*/ +void QAction::clearStatusText() +{ + if (!statusTip().isEmpty()) + showStatusText( QString::null ); +} + +/*! + Removes the action from widget \a w. + + Returns TRUE if the action was removed successfully; otherwise + returns FALSE. + + \sa addTo() +*/ +bool QAction::removeFrom( QWidget* w ) +{ +#ifndef QT_NO_TOOLBAR + if ( ::qt_cast<QToolBar*>(w) ) { + QPtrListIterator<QToolButton> it( d->toolbuttons); + QToolButton* btn; + while ( ( btn = it.current() ) ) { + ++it; + if ( btn->parentWidget() == w ) { + d->toolbuttons.removeRef( btn ); + disconnect( btn, SIGNAL( destroyed() ), this, SLOT( objectDestroyed() ) ); + delete btn; + // no need to disconnect from statusbar + } + } + } else +#endif + if ( ::qt_cast<QPopupMenu*>(w) ) { + QPtrListIterator<QActionPrivate::MenuItem> it( d->menuitems); + QActionPrivate::MenuItem* mi; + while ( ( mi = it.current() ) ) { + ++it; + if ( mi->popup == w ) { + disconnect( mi->popup, SIGNAL(highlighted(int)), this, SLOT(menuStatusText(int)) ); + disconnect( mi->popup, SIGNAL(aboutToHide()), this, SLOT(clearStatusText()) ); + disconnect( mi->popup, SIGNAL( destroyed() ), this, SLOT( objectDestroyed() ) ); + mi->popup->removeItem( mi->id ); + d->menuitems.removeRef( mi ); + } + } + } else if ( ::qt_cast<QComboBox*>(w) ) { + QPtrListIterator<QActionPrivate::ComboItem> it( d->comboitems ); + QActionPrivate::ComboItem *ci; + while ( ( ci = it.current() ) ) { + ++it; + if ( ci->combo == w ) { + disconnect( ci->combo, SIGNAL(destroyed()), this, SLOT(objectDestroyed()) ); + d->comboitems.removeRef( ci ); + } + } + } else { + qWarning( "QAction::removeFrom(), unknown object" ); + return FALSE; + } + return TRUE; +} + +/*! + \internal +*/ +void QAction::objectDestroyed() +{ + const QObject* obj = sender(); + QPtrListIterator<QActionPrivate::MenuItem> it( d->menuitems ); + QActionPrivate::MenuItem* mi; + while ( ( mi = it.current() ) ) { + ++it; + if ( mi->popup == obj ) + d->menuitems.removeRef( mi ); + } + QActionPrivate::ComboItem *ci; + QPtrListIterator<QActionPrivate::ComboItem> it2( d->comboitems ); + while ( ( ci = it2.current() ) ) { + ++it2; + if ( ci->combo == obj ) + d->comboitems.removeRef( ci ); + } + d->toolbuttons.removeRef( (QToolButton*) obj ); +} + +/*! + \fn void QAction::activated() + + This signal is emitted when an action is activated by the user, + e.g. when the user clicks a menu option or a toolbar button or + presses an action's accelerator key combination. + + Connect to this signal for command actions. Connect to the + toggled() signal for toggle actions. +*/ + +/*! + \fn void QAction::toggled(bool on) + + This signal is emitted when a toggle action changes state; command + actions and \l{QActionGroup}s don't emit toggled(). + + The \a on argument denotes the new state: If \a on is TRUE the + toggle action is switched on, and if \a on is FALSE the toggle + action is switched off. + + To trigger a user command depending on whether a toggle action has + been switched on or off connect it to a slot that takes a bool to + indicate the state, e.g. + + \quotefile action/toggleaction/toggleaction.cpp + \skipto QMainWindow * window + \printline QMainWindow * window + \skipto labelonoffaction + \printline labelonoffaction + \skipto connect + \printuntil setUsesTextLabel + + \sa activated() setToggleAction() setOn() +*/ + +void QActionGroupPrivate::update( const QActionGroup* that ) +{ + for ( QPtrListIterator<QAction> it( actions ); it.current(); ++it ) { + if ( that->isEnabled() && !it.current()->d->forceDisabled ) { + it.current()->setEnabled( TRUE ); + } else if ( !that->isEnabled() && it.current()->isEnabled() ) { + it.current()->setEnabled( FALSE ); + it.current()->d->forceDisabled = FALSE; + } + if ( that->isVisible() && !it.current()->d->forceInvisible ) { + it.current()->setVisible( TRUE ); + } else if ( !that->isVisible() && it.current()->isVisible() ) { + it.current()->setVisible( FALSE ); + it.current()->d->forceInvisible = FALSE; + } + } + for ( QPtrListIterator<QComboBox> cb( comboboxes ); cb.current(); ++cb ) { + QComboBox *combobox = cb.current(); + combobox->setEnabled( that->isEnabled() ); + combobox->setShown( that->isVisible() ); + +#ifndef QT_NO_TOOLTIP + QToolTip::remove( combobox ); + if ( !!that->toolTip() ) + QToolTip::add( combobox, that->toolTip() ); +#endif +#ifndef QT_NO_WHATSTHIS + QWhatsThis::remove( combobox ); + if ( !!that->whatsThis() ) + QWhatsThis::add( combobox, that->whatsThis() ); +#endif + + } + for ( QPtrListIterator<QToolButton> mb( menubuttons ); mb.current(); ++mb ) { + QToolButton *button = mb.current(); + button->setEnabled( that->isEnabled() ); + button->setShown( that->isVisible() ); + + if ( !that->text().isNull() ) + button->setTextLabel( that->text() ); + if ( !that->iconSet().isNull() ) + button->setIconSet( that->iconSet() ); + +#ifndef QT_NO_TOOLTIP + QToolTip::remove( mb.current() ); + if ( !!that->toolTip() ) + QToolTip::add( button, that->toolTip() ); +#endif +#ifndef QT_NO_WHATSTHIS + QWhatsThis::remove( button ); + if ( !!that->whatsThis() ) + QWhatsThis::add( button, that->whatsThis() ); +#endif + } + for ( QPtrListIterator<QActionGroupPrivate::MenuItem> pu( menuitems ); pu.current(); ++pu ) { + QWidget* parent = pu.current()->popup->parentWidget(); + if ( ::qt_cast<QPopupMenu*>(parent) ) { + QPopupMenu* ppopup = (QPopupMenu*)parent; + ppopup->setItemEnabled( pu.current()->id, that->isEnabled() ); + ppopup->setItemVisible( pu.current()->id, that->isVisible() ); + } else { + pu.current()->popup->setEnabled( that->isEnabled() ); + } + } + for ( QPtrListIterator<QPopupMenu> pm( popupmenus ); pm.current(); ++pm ) { + QPopupMenu *popup = pm.current(); + QPopupMenu *parent = ::qt_cast<QPopupMenu*>(popup->parentWidget()); + if ( !parent ) + continue; + + int index; + parent->findPopup( popup, &index ); + int id = parent->idAt( index ); + if ( !that->iconSet().isNull() ) + parent->changeItem( id, that->iconSet(), that->menuText() ); + else + parent->changeItem( id, that->menuText() ); + parent->setItemEnabled( id, that->isEnabled() ); +#ifndef QT_NO_ACCEL + parent->setAccel( that->accel(), id ); +#endif + } +} + +/*! + \class QActionGroup qaction.h + \brief The QActionGroup class groups actions together. + + \ingroup basic + \ingroup application + + In some situations it is useful to group actions together. For + example, if you have a left justify action, a right justify action + and a center action, only one of these actions should be active at + any one time, and one simple way of achieving this is to group the + actions together in an action group. + + An action group can also be added to a menu or a toolbar as a + single unit, with all the actions within the action group + appearing as separate menu options and toolbar buttons. + + Here's an example from examples/textedit: + \quotefile textedit/textedit.cpp + \skipto QActionGroup + \printuntil connect + + Here we create a new action group. Since the action group is exclusive + by default, only one of the actions in the group is ever active at any + one time. We then connect the group's selected() signal to our + textAlign() slot. + + \printuntil actionAlignLeft->setToggleAction + + We create a left align action, add it to the toolbar and the menu + and make it a toggle action. We create center and right align + actions in exactly the same way. + + \omit + A QActionGroup emits an activated() signal when one of its actions + is activated. + \endomit + The actions in an action group emit their activated() (and for + toggle actions, toggled()) signals as usual. + + The setExclusive() function is used to ensure that only one action + is active at any one time: it should be used with actions which + have their \c toggleAction set to TRUE. + + Action group actions appear as individual menu options and toolbar + buttons. For exclusive action groups use setUsesDropDown() to + display the actions in a subwidget of any widget the action group + is added to. For example, the actions would appear in a combobox + in a toolbar or as a submenu in a menu. + + Actions can be added to an action group using add(), but normally + they are added by creating the action with the action group as + parent. Actions can have separators dividing them using + addSeparator(). Action groups are added to widgets with addTo(). +*/ + +/*! + Constructs an action group called \a name, with parent \a parent. + + The action group is exclusive by default. Call setExclusive(FALSE) to make + the action group non-exclusive. +*/ +QActionGroup::QActionGroup( QObject* parent, const char* name ) + : QAction( parent, name ) +{ + d = new QActionGroupPrivate; + d->exclusive = TRUE; + d->dropdown = FALSE; + d->selected = 0; + d->separatorAction = 0; + QAction::d->d_group = d; + + connect( this, SIGNAL(selected(QAction*)), SLOT(internalToggle(QAction*)) ); +} + +/*! + Constructs an action group called \a name, with parent \a parent. + + If \a exclusive is TRUE only one toggle action in the group will + ever be active. + + \sa exclusive +*/ +QActionGroup::QActionGroup( QObject* parent, const char* name, bool exclusive ) + : QAction( parent, name ) +{ + d = new QActionGroupPrivate; + d->exclusive = exclusive; + d->dropdown = FALSE; + d->selected = 0; + d->separatorAction = 0; + QAction::d->d_group = d; + + connect( this, SIGNAL(selected(QAction*)), SLOT(internalToggle(QAction*)) ); +} + +/*! + Destroys the object and frees allocated resources. +*/ + +QActionGroup::~QActionGroup() +{ + QPtrListIterator<QActionGroupPrivate::MenuItem> mit( d->menuitems ); + while ( mit.current() ) { + QActionGroupPrivate::MenuItem *mi = mit.current(); + ++mit; + if ( mi->popup ) + mi->popup->disconnect( SIGNAL(destroyed()), this, SLOT(objectDestroyed()) ); + } + + QPtrListIterator<QComboBox> cbit( d->comboboxes ); + while ( cbit.current() ) { + QComboBox *cb = cbit.current(); + ++cbit; + cb->disconnect( SIGNAL(destroyed()), this, SLOT(objectDestroyed()) ); + } + QPtrListIterator<QToolButton> mbit( d->menubuttons ); + while ( mbit.current() ) { + QToolButton *mb = mbit.current(); + ++mbit; + mb->disconnect( SIGNAL(destroyed()), this, SLOT(objectDestroyed()) ); + } + QPtrListIterator<QPopupMenu> pmit( d->popupmenus ); + while ( pmit.current() ) { + QPopupMenu *pm = pmit.current(); + ++pmit; + pm->disconnect( SIGNAL(destroyed()), this, SLOT(objectDestroyed()) ); + } + + delete d->separatorAction; + d->menubuttons.setAutoDelete( TRUE ); + d->comboboxes.setAutoDelete( TRUE ); + d->menuitems.setAutoDelete( TRUE ); + d->popupmenus.setAutoDelete( TRUE ); + delete d; +} + +/*! + \property QActionGroup::exclusive + \brief whether the action group does exclusive toggling + + If exclusive is TRUE only one toggle action in the action group + can ever be active at any one time. If the user chooses another + toggle action in the group the one they chose becomes active and + the one that was active becomes inactive. + + \sa QAction::toggleAction +*/ +void QActionGroup::setExclusive( bool enable ) +{ + d->exclusive = enable; +} + +bool QActionGroup::isExclusive() const +{ + return d->exclusive; +} + +/*! + \property QActionGroup::usesDropDown + \brief whether the group's actions are displayed in a subwidget of + the widgets the action group is added to + + Exclusive action groups added to a toolbar display their actions + in a combobox with the action's \l QAction::text and \l + QAction::iconSet properties shown. Non-exclusive groups are + represented by a tool button showing their \l QAction::iconSet and + -- depending on \l QMainWindow::usesTextLabel() -- text() + property. + + In a popup menu the member actions are displayed in a submenu. + + Changing usesDropDown only affects \e subsequent calls to addTo(). + + Note that setting this property for actions in a combobox causes + calls to their \link QAction::setVisible()\endlink, + \link QAction::setEnabled()\endlink, and + \link QAction::setDisabled()\endlink functions to have no effect. + + This property's default is FALSE. + +*/ +void QActionGroup::setUsesDropDown( bool enable ) +{ + d->dropdown = enable; +} + +bool QActionGroup::usesDropDown() const +{ + return d->dropdown; +} + +/*! + Adds action \a action to this group. + + Normally an action is added to a group by creating it with the + group as parent, so this function is not usually used. + + \sa addTo() +*/ +void QActionGroup::add( QAction* action ) +{ + if ( d->actions.containsRef( action ) ) + return; + + d->actions.append( action ); + + if ( action->whatsThis().isNull() ) + action->setWhatsThis( whatsThis() ); + if ( action->toolTip().isNull() ) + action->setToolTip( toolTip() ); + + if (!action->d->forceDisabled) + action->d->enabled = isEnabled(); + if (!action->d->forceInvisible) + action->d->visible = isVisible(); + + connect( action, SIGNAL( destroyed() ), this, SLOT( childDestroyed() ) ); + connect( action, SIGNAL( activated() ), this, SIGNAL( activated() ) ); + connect( action, SIGNAL( toggled(bool) ), this, SLOT( childToggled(bool) ) ); + + for ( QPtrListIterator<QComboBox> cb( d->comboboxes ); cb.current(); ++cb ) { + action->addTo( cb.current() ); + } + for ( QPtrListIterator<QToolButton> mb( d->menubuttons ); mb.current(); ++mb ) { + QPopupMenu* popup = mb.current()->popup(); + if ( !popup ) + continue; + action->addTo( popup ); + } + for ( QPtrListIterator<QActionGroupPrivate::MenuItem> mi( d->menuitems ); mi.current(); ++mi ) { + QPopupMenu* popup = mi.current()->popup; + if ( !popup ) + continue; + action->addTo( popup ); + } +} + +/*! + Adds a separator to the group. +*/ +void QActionGroup::addSeparator() +{ + if ( !d->separatorAction ) + d->separatorAction = new QAction( 0, "qt_separator_action" ); + d->actions.append( d->separatorAction ); +} + + +/*! \fn void QActionGroup::insert( QAction* a ) + + \obsolete + + Use add() instead, or better still create the action with the action + group as its parent. + */ + +/*! + Adds this action group to the widget \a w. + + If isExclusive() is FALSE or usesDropDown() is FALSE, the actions within + the group are added to the widget individually. For example, if the widget + is a menu, the actions will appear as individual menu options, and + if the widget is a toolbar, the actions will appear as toolbar buttons. + + If both isExclusive() and usesDropDown() are TRUE, the actions + are presented either in a combobox (if \a w is a toolbar) or in a + submenu (if \a w is a menu). + + All actions should be added to the action group \e before the + action group is added to the widget. If actions are added to the + action group \e after the action group has been added to the + widget these later actions will \e not appear. + + \sa setExclusive() setUsesDropDown() removeFrom() +*/ +bool QActionGroup::addTo( QWidget* w ) +{ +#ifndef QT_NO_TOOLBAR + if ( ::qt_cast<QToolBar*>(w) ) { + if ( d->dropdown ) { + if ( !d->exclusive ) { + QPtrListIterator<QAction> it( d->actions); + if ( !it.current() ) + return TRUE; + + QAction *defAction = it.current(); + + QToolButton* btn = new QToolButton( (QToolBar*) w, "qt_actiongroup_btn" ); + addedTo( btn, w ); + connect( btn, SIGNAL(destroyed()), SLOT(objectDestroyed()) ); + d->menubuttons.append( btn ); + + if ( !iconSet().isNull() ) + btn->setIconSet( iconSet() ); + else if ( !defAction->iconSet().isNull() ) + btn->setIconSet( defAction->iconSet() ); + if ( !!text() ) + btn->setTextLabel( text() ); + else if ( !!defAction->text() ) + btn->setTextLabel( defAction->text() ); +#ifndef QT_NO_TOOLTIP + if ( !!toolTip() ) + QToolTip::add( btn, toolTip() ); + else if ( !!defAction->toolTip() ) + QToolTip::add( btn, defAction->toolTip() ); +#endif +#ifndef QT_NO_WHATSTHIS + if ( !!whatsThis() ) + QWhatsThis::add( btn, whatsThis() ); + else if ( !!defAction->whatsThis() ) + QWhatsThis::add( btn, defAction->whatsThis() ); +#endif + + connect( btn, SIGNAL( clicked() ), defAction, SIGNAL( activated() ) ); + connect( btn, SIGNAL( toggled(bool) ), defAction, SLOT( toolButtonToggled(bool) ) ); + connect( btn, SIGNAL( destroyed() ), defAction, SLOT( objectDestroyed() ) ); + + QPopupMenu *menu = new QPopupMenu( btn, "qt_actiongroup_menu" ); + btn->setPopupDelay( 0 ); + btn->setPopup( menu ); + + while( it.current() ) { + it.current()->addTo( menu ); + ++it; + } + d->update( this ); + return TRUE; + } else { + QComboBox *box = new QComboBox( FALSE, w, "qt_actiongroup_combo" ); + addedTo( box, w ); + connect( box, SIGNAL(destroyed()), SLOT(objectDestroyed()) ); + d->comboboxes.append( box ); +#ifndef QT_NO_TOOLTIP + if ( !!toolTip() ) + QToolTip::add( box, toolTip() ); +#endif +#ifndef QT_NO_WHATSTHIS + if ( !!whatsThis() ) + QWhatsThis::add( box, whatsThis() ); +#endif + + int onIndex = 0; + bool foundOn = FALSE; + for ( QPtrListIterator<QAction> it( d->actions); it.current(); ++it ) { + QAction *action = it.current(); + if ( !foundOn ) + foundOn = action->isOn(); + if ( qstrcmp( action->name(), "qt_separator_action" ) && !foundOn ) + onIndex++; + action->addTo( box ); + } + if ( foundOn ) + box->setCurrentItem( onIndex ); + connect( box, SIGNAL(activated(int)), this, SLOT( internalComboBoxActivated(int)) ); + connect( box, SIGNAL(highlighted(int)), this, SLOT( internalComboBoxHighlighted(int)) ); + d->update( this ); + return TRUE; + } + } + } else +#endif + if ( ::qt_cast<QPopupMenu*>(w) ) { + QPopupMenu *popup; + if ( d->dropdown ) { + QPopupMenu *menu = (QPopupMenu*)w; + popup = new QPopupMenu( w, "qt_actiongroup_menu" ); + d->popupmenus.append( popup ); + connect( popup, SIGNAL(destroyed()), SLOT(objectDestroyed()) ); + + int id; + if ( !iconSet().isNull() ) { + if ( menuText().isEmpty() ) + id = menu->insertItem( iconSet(), text(), popup ); + else + id = menu->insertItem( iconSet(), menuText(), popup ); + } else { + if ( menuText().isEmpty() ) + id = menu->insertItem( text(), popup ); + else + id = menu->insertItem( menuText(), popup ); + } + + addedTo( menu->indexOf( id ), menu ); + + QActionGroupPrivate::MenuItem *item = new QActionGroupPrivate::MenuItem; + item->id = id; + item->popup = popup; + d->menuitems.append( item ); + } else { + popup = (QPopupMenu*)w; + } + for ( QPtrListIterator<QAction> it( d->actions); it.current(); ++it ) { + // #### do an addedTo( index, popup, action), need to find out index + it.current()->addTo( popup ); + } + return TRUE; + } + + for ( QPtrListIterator<QAction> it( d->actions); it.current(); ++it ) { + // #### do an addedTo( index, popup, action), need to find out index + it.current()->addTo( w ); + } + + return TRUE; +} + +/*! \reimp +*/ +bool QActionGroup::removeFrom( QWidget* w ) +{ + for ( QPtrListIterator<QAction> it( d->actions); it.current(); ++it ) { + it.current()->removeFrom( w ); + } + +#ifndef QT_NO_TOOLBAR + if ( ::qt_cast<QToolBar*>(w) ) { + QPtrListIterator<QComboBox> cb( d->comboboxes ); + while( cb.current() ) { + QComboBox *box = cb.current(); + ++cb; + if ( box->parentWidget() == w ) + delete box; + } + QPtrListIterator<QToolButton> mb( d->menubuttons ); + while( mb.current() ) { + QToolButton *btn = mb.current(); + ++mb; + if ( btn->parentWidget() == w ) + delete btn; + } + } else +#endif + if ( ::qt_cast<QPopupMenu*>(w) ) { + QPtrListIterator<QActionGroupPrivate::MenuItem> pu( d->menuitems ); + while ( pu.current() ) { + QActionGroupPrivate::MenuItem *mi = pu.current(); + ++pu; + if ( d->dropdown && mi->popup ) + ( (QPopupMenu*)w )->removeItem( mi->id ); + delete mi->popup; + } + } + + return TRUE; +} + +/*! \internal +*/ +void QActionGroup::childToggled( bool b ) +{ + if ( !isExclusive() ) + return; + QAction* s = (QAction*) sender(); + if ( b ) { + if ( s != d->selected ) { + d->selected = s; + for ( QPtrListIterator<QAction> it( d->actions); it.current(); ++it ) { + if ( it.current()->isToggleAction() && it.current() != s ) + it.current()->setOn( FALSE ); + } + emit activated(); + emit selected( s ); + } else if ( !s->isToggleAction() ) { + emit activated(); + } + } else { + if ( s == d->selected ) { + // at least one has to be selected + s->setOn( TRUE ); + } + } +} + +/*! \internal +*/ +void QActionGroup::childDestroyed() +{ + d->actions.removeRef( (QAction*) sender() ); + if ( d->selected == sender() ) + d->selected = 0; +} + +/*! \reimp +*/ +void QActionGroup::setEnabled( bool enable ) +{ + if ( enable == isEnabled() ) + return; + + QAction::setEnabled( enable ); + d->update( this ); +} + +/*! \reimp +*/ +void QActionGroup::setToggleAction( bool toggle ) +{ + for ( QPtrListIterator<QAction> it( d->actions); it.current(); ++it ) + it.current()->setToggleAction( toggle ); + + QAction::setToggleAction( TRUE ); + d->update( this ); +} + +/*! \reimp +*/ +void QActionGroup::setOn( bool on ) +{ + for ( QPtrListIterator<QAction> it( d->actions); it.current(); ++it ) { + QAction *act = it.current(); + if ( act->isToggleAction() ) + act->setOn( on ); + } + + QAction::setOn( on ); + d->update( this ); +} + +/*! \reimp +*/ +void QActionGroup::setIconSet( const QIconSet& icon ) +{ + QAction::setIconSet( icon ); + d->update( this ); +} + +/*! \reimp +*/ +void QActionGroup::setText( const QString& txt ) +{ + if ( txt == text() ) + return; + + QAction::setText( txt ); + d->update( this ); +} + +/*! \reimp +*/ +void QActionGroup::setMenuText( const QString& text ) +{ + if ( text == menuText() ) + return; + + QAction::setMenuText( text ); + d->update( this ); +} + +/*! \reimp +*/ +void QActionGroup::setToolTip( const QString& text ) +{ + if ( text == toolTip() ) + return; + for ( QPtrListIterator<QAction> it( d->actions); it.current(); ++it ) { + if ( it.current()->toolTip().isNull() ) + it.current()->setToolTip( text ); + } + QAction::setToolTip( text ); + d->update( this ); +} + +/*! \reimp +*/ +void QActionGroup::setWhatsThis( const QString& text ) +{ + if ( text == whatsThis() ) + return; + for ( QPtrListIterator<QAction> it( d->actions); it.current(); ++it ) { + if ( it.current()->whatsThis().isNull() ) + it.current()->setWhatsThis( text ); + } + QAction::setWhatsThis( text ); + d->update( this ); +} + +/*! \reimp +*/ +void QActionGroup::childEvent( QChildEvent *e ) +{ + if ( !e->removed() ) + return; + + QAction *action = ::qt_cast<QAction*>(e->child()); + if ( !action ) + return; + + for ( QPtrListIterator<QComboBox> cb( d->comboboxes ); cb.current(); ++cb ) { + for ( int i = 0; i < cb.current()->count(); i++ ) { + if ( cb.current()->text( i ) == action->text() ) { + cb.current()->removeItem( i ); + break; + } + } + } + for ( QPtrListIterator<QToolButton> mb( d->menubuttons ); mb.current(); ++mb ) { + QPopupMenu* popup = mb.current()->popup(); + if ( !popup ) + continue; + action->removeFrom( popup ); + } + for ( QPtrListIterator<QActionGroupPrivate::MenuItem> mi( d->menuitems ); mi.current(); ++mi ) { + QPopupMenu* popup = mi.current()->popup; + if ( !popup ) + continue; + action->removeFrom( popup ); + } +} + +/*! + \fn void QActionGroup::selected( QAction* ) + + This signal is emitted from exclusive groups when toggle actions + change state. + + The argument is the action whose state changed to "on". + + \sa setExclusive(), isOn() QAction::toggled() +*/ + +/*! \internal +*/ +void QActionGroup::internalComboBoxActivated( int index ) +{ + QAction *a = 0; + for ( int i = 0; i <= index && i < (int)d->actions.count(); ++i ) { + a = d->actions.at( i ); + if ( a && !qstrcmp( a->name(), "qt_separator_action" ) ) + index++; + } + a = d->actions.at( index ); + if ( a ) { + if ( a != d->selected ) { + d->selected = a; + for ( QPtrListIterator<QAction> it( d->actions); it.current(); ++it ) { + if ( it.current()->isToggleAction() && it.current() != a ) + it.current()->setOn( FALSE ); + } + if ( a->isToggleAction() ) + a->setOn( TRUE ); + + emit activated(); + if ( a->isToggleAction() ) + emit selected( d->selected ); + emit ((QActionGroup*)a)->activated(); + } else if ( !a->isToggleAction() ) { + emit activated(); + emit ((QActionGroup*)a)->activated(); + } + a->clearStatusText(); + } +} + +/*! \internal +*/ +void QActionGroup::internalComboBoxHighlighted( int index ) +{ + QAction *a = 0; + for ( int i = 0; i <= index && i < (int)d->actions.count(); ++i ) { + a = d->actions.at( i ); + if ( a && !qstrcmp( a->name(), "qt_separator_action" ) ) + index++; + } + a = d->actions.at( index ); + if ( a ) + a->showStatusText(a->statusTip()); + else + clearStatusText(); +} + +/*! \internal +*/ +void QActionGroup::internalToggle( QAction *a ) +{ + int index = d->actions.find( a ); + if ( index == -1 ) + return; + + int lastItem = index; + for ( int i = 0; i < lastItem; i++ ) { + QAction *action = d->actions.at( i ); + if ( !qstrcmp( action->name(), "qt_separator_action" ) ) + index--; + } + + for ( QPtrListIterator<QComboBox> it( d->comboboxes); it.current(); ++it ) + it.current()->setCurrentItem( index ); +} + +/*! \internal +*/ +void QActionGroup::objectDestroyed() +{ + const QObject* obj = sender(); + d->menubuttons.removeRef( (QToolButton*)obj ); + for ( QPtrListIterator<QActionGroupPrivate::MenuItem> mi( d->menuitems ); mi.current(); ++mi ) { + if ( mi.current()->popup == obj ) { + d->menuitems.removeRef( mi.current() ); + break; + } + } + d->popupmenus.removeRef( (QPopupMenu*)obj ); + d->comboboxes.removeRef( (QComboBox*)obj ); +} + +/*! + \internal + + This function is called from the addTo() function when it has + created a widget (\a actionWidget) for the child action \a a in + the \a container. +*/ + +void QActionGroup::addedTo( QWidget *actionWidget, QWidget *container, QAction *a ) +{ + Q_UNUSED( actionWidget ); + Q_UNUSED( container ); + Q_UNUSED( a ); +} + +/*! + \overload + \internal + + This function is called from the addTo() function when it has + created a menu item for the child action at the index position \a + index in the popup menu \a menu. +*/ + +void QActionGroup::addedTo( int index, QPopupMenu *menu, QAction *a ) +{ + Q_UNUSED( index ); + Q_UNUSED( menu ); + Q_UNUSED( a ); +} + +/*! + \reimp + \overload + + This function is called from the addTo() function when it has + created a widget (\a actionWidget) in the \a container. +*/ + +void QActionGroup::addedTo( QWidget *actionWidget, QWidget *container ) +{ + Q_UNUSED( actionWidget ); + Q_UNUSED( container ); +} + +/*! + \reimp + \overload + + This function is called from the addTo() function when it has + created a menu item at the index position \a index in the popup + menu \a menu. +*/ + +void QActionGroup::addedTo( int index, QPopupMenu *menu ) +{ + Q_UNUSED( index ); + Q_UNUSED( menu ); +} + +#endif diff --git a/src/widgets/qaction.h b/src/widgets/qaction.h new file mode 100644 index 0000000..b6ec99c --- /dev/null +++ b/src/widgets/qaction.h @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Definition of QAction class +** +** Created : 000000 +** +** Copyright (C) 2005-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. +** +**********************************************************************/ + +#ifndef QACTION_H +#define QACTION_H + +#ifndef QT_H +#include "qobject.h" +#include "qiconset.h" +#include "qstring.h" +#include "qkeysequence.h" +#endif // QT_H + +#ifndef QT_NO_ACTION + +class QActionPrivate; +class QActionGroupPrivate; +class QStatusBar; +class QPopupMenu; +class QToolTipGroup; + +class Q_EXPORT QAction : public QObject +{ + Q_OBJECT + Q_PROPERTY( bool toggleAction READ isToggleAction WRITE setToggleAction) + Q_PROPERTY( bool on READ isOn WRITE setOn ) + Q_PROPERTY( bool enabled READ isEnabled WRITE setEnabled ) + Q_PROPERTY( QIconSet iconSet READ iconSet WRITE setIconSet ) + Q_PROPERTY( QString text READ text WRITE setText ) + Q_PROPERTY( QString menuText READ menuText WRITE setMenuText ) + Q_PROPERTY( QString toolTip READ toolTip WRITE setToolTip ) + Q_PROPERTY( QString statusTip READ statusTip WRITE setStatusTip ) + Q_PROPERTY( QString whatsThis READ whatsThis WRITE setWhatsThis ) +#ifndef QT_NO_ACCEL + Q_PROPERTY( QKeySequence accel READ accel WRITE setAccel ) +#endif + Q_PROPERTY( bool visible READ isVisible WRITE setVisible ) + +public: + QAction( QObject* parent, const char* name = 0 ); +#ifndef QT_NO_ACCEL + QAction( const QString& menuText, QKeySequence accel, + QObject* parent, const char* name = 0 ); + QAction( const QIconSet& icon, const QString& menuText, QKeySequence accel, + QObject* parent, const char* name = 0 ); + + QAction( const QString& text, const QIconSet& icon, const QString& menuText, QKeySequence accel, + QObject* parent, const char* name = 0, bool toggle = FALSE ); // obsolete + QAction( const QString& text, const QString& menuText, QKeySequence accel, QObject* parent, + const char* name = 0, bool toggle = FALSE ); // obsolete +#endif + QAction( QObject* parent, const char* name , bool toggle ); // obsolete + ~QAction(); + + virtual void setIconSet( const QIconSet& ); + QIconSet iconSet() const; + virtual void setText( const QString& ); + QString text() const; + virtual void setMenuText( const QString& ); + QString menuText() const; + virtual void setToolTip( const QString& ); + QString toolTip() const; + virtual void setStatusTip( const QString& ); + QString statusTip() const; + virtual void setWhatsThis( const QString& ); + QString whatsThis() const; +#ifndef QT_NO_ACCEL + virtual void setAccel( const QKeySequence& key ); + QKeySequence accel() const; +#endif + virtual void setToggleAction( bool ); + + bool isToggleAction() const; + bool isOn() const; + bool isEnabled() const; + bool isVisible() const; + virtual bool addTo( QWidget* ); + virtual bool removeFrom( QWidget* ); + +protected: + virtual void addedTo( QWidget *actionWidget, QWidget *container ); + virtual void addedTo( int index, QPopupMenu *menu ); + +public slots: + void activate(); + void toggle(); + virtual void setOn( bool ); + virtual void setEnabled( bool ); + void setDisabled( bool ); + void setVisible( bool ); + +signals: + void activated(); + void toggled( bool ); + +private slots: + void internalActivation(); + void toolButtonToggled( bool ); + void objectDestroyed(); + void menuStatusText( int id ); + void showStatusText( const QString& ); + void clearStatusText(); + +private: + void init(); + + friend class QActionGroup; + friend class QActionGroupPrivate; + QActionPrivate* d; + +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + QAction( const QAction & ); + QAction &operator=( const QAction & ); +#endif +}; + +class Q_EXPORT QActionGroup : public QAction +{ + Q_OBJECT + Q_PROPERTY( bool exclusive READ isExclusive WRITE setExclusive ) + Q_PROPERTY( bool usesDropDown READ usesDropDown WRITE setUsesDropDown ) + +public: + QActionGroup( QObject* parent, const char* name = 0 ); + QActionGroup( QObject* parent, const char* name , bool exclusive ); // obsolete + ~QActionGroup(); + void setExclusive( bool ); + bool isExclusive() const; + void add( QAction* a); + void addSeparator(); + bool addTo( QWidget* ); + bool removeFrom( QWidget* ); + void setEnabled( bool ); + void setToggleAction( bool toggle ); + void setOn( bool on ); + + void setUsesDropDown( bool enable ); + bool usesDropDown() const; + + void setIconSet( const QIconSet& ); + void setText( const QString& ); + void setMenuText( const QString& ); + void setToolTip( const QString& ); + void setWhatsThis( const QString& ); + +protected: + void childEvent( QChildEvent* ); + virtual void addedTo( QWidget *actionWidget, QWidget *container, QAction *a ); + virtual void addedTo( int index, QPopupMenu *menu, QAction *a ); + virtual void addedTo( QWidget *actionWidget, QWidget *container ); + virtual void addedTo( int index, QPopupMenu *menu ); + +signals: + void selected( QAction* ); + +private slots: + void childToggled( bool ); + void childDestroyed(); + void internalComboBoxActivated( int ); + void internalComboBoxHighlighted( int ); + void internalToggle( QAction* ); + void objectDestroyed(); + +private: + QActionGroupPrivate* d; + +#ifndef QT_NO_COMPAT +public: + void insert( QAction* a ) { add( a ); } +#endif + +private: +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + QActionGroup( const QActionGroup & ); + QActionGroup &operator=( const QActionGroup & ); +#endif +}; + +#endif + +#endif diff --git a/src/widgets/qbutton.cpp b/src/widgets/qbutton.cpp new file mode 100644 index 0000000..57178fb --- /dev/null +++ b/src/widgets/qbutton.cpp @@ -0,0 +1,1008 @@ +/**************************************************************************** +** +** Implementation of QButton widget class +** +** Created : 940206 +** +** 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. +** +**********************************************************************/ + +#undef QT_NO_COMPAT +#include "qbutton.h" +#ifndef QT_NO_BUTTON +#include "qbuttongroup.h" +#include "qbitmap.h" +#include "qpainter.h" +#include "qtimer.h" +#include "qaccel.h" +#include "qpixmapcache.h" +#include "qapplication.h" +#include "qpushbutton.h" +#include "qradiobutton.h" +#include "qguardedptr.h" +#include "../kernel/qinternal_p.h" + +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif + +#define AUTO_REPEAT_DELAY 300 +#define AUTO_REPEAT_PERIOD 100 + +class QButtonData +{ +public: + QButtonData() { +#ifndef QT_NO_BUTTONGROUP + group = 0; +#endif +#ifndef QT_NO_ACCEL + a = 0; +#endif + } +#ifndef QT_NO_BUTTONGROUP + QButtonGroup *group; +#endif + QTimer timer; +#ifndef QT_NO_ACCEL + QAccel *a; +#endif +}; + + +void QButton::ensureData() +{ + if ( !d ) { + d = new QButtonData; + Q_CHECK_PTR( d ); + connect(&d->timer, SIGNAL(timeout()), this, SLOT(autoRepeatTimeout())); + } +} + + +/*! + Returns the group that this button belongs to. + + If the button is not a member of any QButtonGroup, this function + returns 0. + + \sa QButtonGroup +*/ + +QButtonGroup *QButton::group() const +{ +#ifndef QT_NO_BUTTONGROUP + return d ? d->group : 0; +#else + return 0; +#endif +} + + +void QButton::setGroup( QButtonGroup* g ) +{ +#ifndef QT_NO_BUTTONGROUP + ensureData(); + d->group = g; +#endif +} + + +QTimer *QButton::timer() +{ + ensureData(); + return &d->timer; +} + + +/*! + \class QButton qbutton.h + \brief The QButton class is the abstract base class of button + widgets, providing functionality common to buttons. + + \ingroup abstractwidgets + + <b>If you want to create a button use QPushButton.</b> + + The QButton class implements an \e abstract button, and lets + subclasses specify how to reply to user actions and how to draw + the button. + + QButton provides both push and toggle buttons. The QRadioButton + and QCheckBox classes provide only toggle buttons; QPushButton and + QToolButton provide both toggle and push buttons. + + Any button can have either a text or pixmap label. setText() sets + the button to be a text button and setPixmap() sets it to be a + pixmap button. The text/pixmap is manipulated as necessary to + create the "disabled" appearance when the button is disabled. + + QButton provides most of the states used for buttons: + \list + \i isDown() indicates whether the button is \e pressed down. + \i isOn() indicates whether the button is \e on. + Only toggle buttons can be switched on and off (see below). + \i isEnabled() indicates whether the button can be pressed by the + user. + \i setAutoRepeat() sets whether the button will auto-repeat + if the user holds it down. + \i setToggleButton() sets whether the button is a toggle + button or not. + \endlist + + The difference between isDown() and isOn() is as follows: When the + user clicks a toggle button to toggle it on, the button is first + \e pressed and then released into the \e on state. When the user + clicks it again (to toggle it off), the button moves first to the + \e pressed state, then to the \e off state (isOn() and isDown() + are both FALSE). + + Default buttons (as used in many dialogs) are provided by + QPushButton::setDefault() and QPushButton::setAutoDefault(). + + QButton provides five signals: + \list 1 + \i pressed() is emitted when the button is pressed. E.g. with the mouse + or when animateClick() is called. + \i released() is emitted when the button is released. E.g. when the mouse + is released or the cursor is moved outside the widget. + \i clicked() is emitted when the button is first pressed and then + released when the accelerator key is typed, or when + animateClick() is called. + \i toggled(bool) is emitted when the state of a toggle button changes. + \i stateChanged(int) is emitted when the state of a tristate + toggle button changes. + \endlist + + If the button is a text button with an ampersand (\&) in its text, + QButton creates an automatic accelerator key. This code creates a + push button labelled "Ro<u>c</u>k \& Roll" (where the c is + underlined). The button gets an automatic accelerator key, Alt+C: + + \code + QPushButton *p = new QPushButton( "Ro&ck && Roll", this ); + \endcode + + In this example, when the user presses Alt+C the button will call + animateClick(). + + You can also set a custom accelerator using the setAccel() + function. This is useful mostly for pixmap buttons because they + have no automatic accelerator. + + \code + p->setPixmap( QPixmap("print.png") ); + p->setAccel( ALT+Key_F7 ); + \endcode + + All of the buttons provided by Qt (\l QPushButton, \l QToolButton, + \l QCheckBox and \l QRadioButton) can display both text and + pixmaps. + + To subclass QButton, you must reimplement at least drawButton() + (to draw the button's outline) and drawButtonLabel() (to draw its + text or pixmap). It is generally advisable to reimplement + sizeHint() as well, and sometimes hitButton() (to determine + whether a button press is within the button). + + To reduce flickering, QButton::paintEvent() sets up a pixmap that + the drawButton() function draws in. You should not reimplement + paintEvent() for a subclass of QButton unless you want to take + over all drawing. + + \sa QButtonGroup +*/ + + +/*! + \enum QButton::ToggleType + + This enum type defines what a button can do in response to a + mouse/keyboard press: + + \value SingleShot pressing the button causes an action, then the + button returns to the unpressed state. + + \value Toggle pressing the button toggles it between an \c On and + an \c Off state. + + \value Tristate pressing the button cycles between the three + states \c On, \c Off and \c NoChange +*/ + +/*! + \enum QButton::ToggleState + + This enum defines the state of a toggle button. + + \value Off the button is in the "off" state + \value NoChange the button is in the default/unchanged state + \value On the button is in the "on" state +*/ + +/*! + \property QButton::accel + \brief the accelerator associated with the button + + This property is 0 if there is no accelerator set. If you set this + property to 0 then any current accelerator is removed. +*/ + +/*! + \property QButton::autoRepeat + \brief whether autoRepeat is enabled + + If autoRepeat is enabled then the clicked() signal is emitted at + regular intervals if the button is down. This property has no + effect on toggle buttons. autoRepeat is off by default. +*/ + +/*! \property QButton::autoResize + \brief whether autoResize is enabled + \obsolete + + If autoResize is enabled then the button will resize itself + whenever the contents are changed. +*/ + +/*! + \property QButton::down + \brief whether the button is pressed + + If this property is TRUE, the button is pressed down. The signals + pressed() and clicked() are not emitted if you set this property + to TRUE. The default is FALSE. +*/ + +/*! + \property QButton::exclusiveToggle + \brief whether the button is an exclusive toggle + + If this property is TRUE and the button is in a QButtonGroup, the + button can only be toggled off by another one being toggled on. + The default is FALSE. +*/ + +/*! + \property QButton::on + \brief whether the button is toggled + + This property should only be set for toggle buttons. +*/ + +/*! + \fn void QButton::setOn( bool on ) + + Sets the state of this button to On if \a on is TRUE; otherwise to + Off. + + \sa toggleState +*/ + +/*! + \property QButton::pixmap + \brief the pixmap shown on the button + + If the pixmap is monochrome (i.e. it is a QBitmap or its \link + QPixmap::depth() depth\endlink is 1) and it does not have a mask, + this property will set the pixmap to be its own mask. The purpose + of this is to draw transparent bitmaps which are important for + toggle buttons, for example. + + pixmap() returns 0 if no pixmap was set. +*/ + +/*! + \property QButton::text + \brief the text shown on the button + + This property will return a QString::null if the button has no + text. If the text has an ampersand (\&) in it, then an + accelerator is automatically created for it using the character + that follows the '\&' as the accelerator key. Any previous + accelerator will be overwritten, or cleared if no accelerator is + defined by the text. + + There is no default text. +*/ + +/*! + \property QButton::toggleButton + \brief whether the button is a toggle button + + The default value is FALSE. +*/ + +/*! + \fn QButton::setToggleButton( bool b ) + + If \a b is TRUE, this button becomes a toggle button; if \a b is + FALSE, this button becomes a command button. + + \sa toggleButton +*/ + +/*! + \property QButton::toggleState + \brief the state of the toggle button + + If this property is changed then it does not cause the button + to be repainted. +*/ + +/*! + \property QButton::toggleType + \brief the type of toggle on the button + + The default toggle type is \c SingleShot. + + \sa QButton::ToggleType +*/ + +/*! + Constructs a standard button called \a name with parent \a parent, + using the widget flags \a f. + + If \a parent is a QButtonGroup, this constructor calls + QButtonGroup::insert(). +*/ + +QButton::QButton( QWidget *parent, const char *name, WFlags f ) + : QWidget( parent, name, f ) +{ + bpixmap = 0; + toggleTyp = SingleShot; // button is simple + buttonDown = FALSE; // button is up + stat = Off; // button is off + mlbDown = FALSE; // mouse left button up + autoresize = FALSE; // not auto resizing + animation = FALSE; // no pending animateClick + repeat = FALSE; // not in autorepeat mode + d = 0; +#ifndef QT_NO_BUTTONGROUP + if ( ::qt_cast<QButtonGroup*>(parent) ) { + setGroup((QButtonGroup*)parent); + group()->insert( this ); // insert into button group + } +#endif + setFocusPolicy( TabFocus ); +} + +/*! + Destroys the button. + */ +QButton::~QButton() +{ +#ifndef QT_NO_BUTTONGROUP + if ( group() ) + group()->remove( this ); +#endif + delete bpixmap; + delete d; +} + + +/*! + \fn void QButton::pressed() + + This signal is emitted when the button is pressed down. + + \sa released(), clicked() +*/ + +/*! + \fn void QButton::released() + + This signal is emitted when the button is released. + + \sa pressed(), clicked(), toggled() +*/ + +/*! + \fn void QButton::clicked() + + This signal is emitted when the button is activated (i.e. first + pressed down and then released when the mouse cursor is inside the + button), when the accelerator key is typed or when animateClick() + is called. This signal is \e not emitted if you call setDown(). + + The QButtonGroup::clicked() signal does the same job, if you want + to connect several buttons to the same slot. + + \warning Don't launch a model dialog in response to this signal + for a button that has \c autoRepeat turned on. + + \sa pressed(), released(), toggled() autoRepeat down +*/ + +/*! + \fn void QButton::toggled( bool on ) + + This signal is emitted whenever a toggle button changes status. \a + on is TRUE if the button is on, or FALSE if the button is off. + + This may be the result of a user action, toggle() slot activation, + or because setOn() was called. + + \sa clicked() +*/ + +/*! + \fn void QButton::stateChanged( int state ) + + This signal is emitted whenever a toggle button changes state. \a + state is \c On if the button is on, \c NoChange if it is in the + \link QCheckBox::setTristate() "no change" state\endlink or \c Off + if the button is off. + + This may be the result of a user action, toggle() slot activation, + setState(), or because setOn() was called. + + \sa clicked() QButton::ToggleState +*/ + +void QButton::setText( const QString &text ) +{ + if ( btext == text ) + return; + btext = text; +#ifndef QT_NO_ACCEL + setAccel( QAccel::shortcutKey( text ) ); +#endif + + if ( bpixmap ) { + delete bpixmap; + bpixmap = 0; + } + + if ( autoresize ) + adjustSize(); + + update(); + updateGeometry(); + +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::NameChanged ); +#endif +} + +void QButton::setPixmap( const QPixmap &pixmap ) +{ + if ( bpixmap && bpixmap->serialNumber() == pixmap.serialNumber() ) + return; + + bool newSize; + if ( bpixmap ) { + newSize = pixmap.width() != bpixmap->width() || + pixmap.height() != bpixmap->height(); + *bpixmap = pixmap; + } else { + newSize = TRUE; + bpixmap = new QPixmap( pixmap ); + Q_CHECK_PTR( bpixmap ); + } + if ( bpixmap->depth() == 1 && !bpixmap->mask() ) + bpixmap->setMask( *((QBitmap *)bpixmap) ); + if ( !btext.isNull() ) { + btext = QString::null; +#ifndef QT_NO_ACCEL + setAccel( QKeySequence() ); +#endif + } + if ( autoresize && newSize ) + adjustSize(); + if ( autoMask() ) + updateMask(); + update(); + if ( newSize ) + updateGeometry(); +} + + +#ifndef QT_NO_ACCEL +QKeySequence QButton::accel() const +{ + if ( d && d->a ) + return d->a->key( 0 ); + return QKeySequence(); +} + +void QButton::setAccel( const QKeySequence& key ) +{ + if ( d && d->a ) + d->a->clear(); + if ( key.isEmpty() ) + return; + ensureData(); + if ( !d->a ) { + d->a = new QAccel( this, "buttonAccel" ); + connect( d->a, SIGNAL( activated(int) ), this, SLOT( animateClick() ) ); + connect( d->a, SIGNAL( activatedAmbiguously(int) ), this, SLOT( setFocus() ) ); + } + d->a->insertItem( key, 0 ); +} +#endif + +#ifndef QT_NO_COMPAT + +void QButton::setAutoResize( bool enable ) +{ + if ( (bool)autoresize != enable ) { + autoresize = enable; + if ( autoresize ) + adjustSize(); // calls resize which repaints + } +} + +#endif + +void QButton::setAutoRepeat( bool enable ) +{ + repeat = (uint)enable; + if ( repeat && mlbDown ) + timer()->start( AUTO_REPEAT_DELAY, TRUE ); +} + +/*! + Performs an animated click: the button is pressed and released a + short while later. + + The pressed(), released(), clicked(), toggled(), and + stateChanged() signals are emitted as appropriate. + + This function does nothing if the button is \link setEnabled() + disabled. \endlink + + \sa setAccel() +*/ + +void QButton::animateClick() +{ + if ( !isEnabled() || animation ) + return; + animation = TRUE; + buttonDown = TRUE; + repaint( FALSE ); + emit pressed(); + QTimer::singleShot( 100, this, SLOT(animateTimeout()) ); +} + +void QButton::emulateClick() +{ + if ( !isEnabled() || animation ) + return; + animation = TRUE; + buttonDown = TRUE; + emit pressed(); + animateTimeout(); +} + +void QButton::setDown( bool enable ) +{ + if ( d ) + timer()->stop(); + mlbDown = FALSE; // the safe setting + if ( (bool)buttonDown != enable ) { + buttonDown = enable; + repaint( FALSE ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::StateChanged ); +#endif + } +} + +/*! + Sets the toggle state of the button to \a s. \a s can be \c Off, \c + NoChange or \c On. +*/ + +void QButton::setState( ToggleState s ) +{ + if ( !toggleTyp ) { +#if defined(QT_CHECK_STATE) + qWarning( "QButton::setState() / setOn: (%s) Only toggle buttons " + "may be switched", name( "unnamed" ) ); +#endif + return; + } + + if ( (ToggleState)stat != s ) { // changed state + bool was = stat != Off; + stat = s; + if ( autoMask() ) + updateMask(); + repaint( FALSE ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::StateChanged ); +#endif + // ### toggled for tristate makes no sense. Don't emit the signal in 4.0 + if ( was != (stat != Off) ) + emit toggled( stat != Off ); + emit stateChanged( s ); + } +} + + +/*! + Returns TRUE if \a pos is inside the clickable button rectangle; + otherwise returns FALSE. + + By default, the clickable area is the entire widget. Subclasses + may reimplement it, though. +*/ +bool QButton::hitButton( const QPoint &pos ) const +{ + return rect().contains( pos ); +} + +/*! + Draws the button. The default implementation does nothing. + + This virtual function is reimplemented by subclasses to draw real + buttons. At some point, these reimplementations should call + drawButtonLabel(). + + \sa drawButtonLabel(), paintEvent() +*/ +#if (QT_VERSION-0 >= 0x040000) +#error "QButton. Make pure virtual" +#endif +void QButton::drawButton( QPainter * ) +{ + return; +} + +/*! + Draws the button text or pixmap. + + This virtual function is reimplemented by subclasses to draw real + buttons. It is invoked by drawButton(). + + \sa drawButton(), paintEvent() +*/ + +void QButton::drawButtonLabel( QPainter * ) +{ + return; +} + +/*! \reimp */ +void QButton::keyPressEvent( QKeyEvent *e ) +{ + switch ( e->key() ) { + case Key_Enter: + case Key_Return: + { +#ifndef QT_NO_PUSHBUTTON + QPushButton *pb = (QPushButton*)qt_cast( "QPushButton" ); + if ( pb && ( pb->autoDefault() || pb->isDefault() ) ) + emit clicked(); + else +#endif + e->ignore(); + } + break; + case Key_Space: + if ( !e->isAutoRepeat() ) { + setDown( TRUE ); +#ifndef QT_NO_PUSHBUTTON + if ( ::qt_cast<QPushButton*>(this) ) + emit pressed(); + else +#endif + e->ignore(); + } + break; + case Key_Up: + case Key_Left: +#ifndef QT_NO_BUTTONGROUP + if ( group() ) { + group()->moveFocus( e->key() ); + } else +#endif + { + QFocusEvent::setReason(QFocusEvent::Backtab); + focusNextPrevChild( FALSE ); + QFocusEvent::resetReason(); + } + break; + case Key_Right: + case Key_Down: +#ifndef QT_NO_BUTTONGROUP + if ( group() ) { + group()->moveFocus( e->key() ); + } else +#endif + { + QFocusEvent::setReason(QFocusEvent::Tab); + focusNextPrevChild( TRUE ); + QFocusEvent::resetReason(); + } + break; + case Key_Escape: + if ( buttonDown ) { + buttonDown = FALSE; + update(); + break; + } + // fall through + default: + e->ignore(); + } +} + +/*! \reimp */ +void QButton::keyReleaseEvent( QKeyEvent * e) +{ + switch ( e->key() ) { + case Key_Space: + if ( buttonDown && !e->isAutoRepeat() ) { + buttonDown = FALSE; + nextState(); + emit released(); + emit clicked(); + } + break; + default: + e->ignore(); + } +} + +/*! \reimp */ +void QButton::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() != LeftButton ) { + e->ignore(); + return; + } + bool hit = hitButton( e->pos() ); + if ( hit ) { // mouse press on button + mlbDown = TRUE; // left mouse button down + buttonDown = TRUE; + if ( autoMask() ) + updateMask(); + + repaint( FALSE ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::StateChanged ); +#endif + QGuardedPtr<QTimer> t = timer(); + emit pressed(); + if ( t && repeat ) + t->start( AUTO_REPEAT_DELAY, TRUE ); + } +} + +/*! \reimp */ +void QButton::mouseReleaseEvent( QMouseEvent *e) +{ + if ( e->button() != LeftButton ) { + + // clean up apperance if left button has been pressed + if (mlbDown || buttonDown) { + mlbDown = FALSE; + buttonDown = FALSE; + + if ( autoMask() ) + updateMask(); + repaint( FALSE ); + } + + e->ignore(); + return; + } + if ( !mlbDown ) + return; + if ( d ) + timer()->stop(); + + const bool oldButtonDown = buttonDown; + mlbDown = FALSE; // left mouse button up + buttonDown = FALSE; + if ( hitButton( e->pos() ) ) { // mouse release on button + nextState(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::StateChanged ); +#endif + emit released(); + emit clicked(); + } else { + repaint( FALSE ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::StateChanged ); +#endif + if (oldButtonDown) + emit released(); + } +} + +/*! \reimp */ +void QButton::mouseMoveEvent( QMouseEvent *e ) +{ + if ( !((e->state() & LeftButton) && mlbDown) ) { + e->ignore(); + return; // left mouse button is up + } + if ( hitButton(e->pos()) ) { // mouse move in button + if ( !buttonDown ) { + buttonDown = TRUE; + repaint( FALSE ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::StateChanged ); +#endif + emit pressed(); + } + } else { // mouse move outside button + if ( buttonDown ) { + buttonDown = FALSE; + repaint( FALSE ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::StateChanged ); +#endif + emit released(); + } + } +} + + +/*! + Handles paint events for buttons. Small and typically complex + buttons are painted double-buffered to reduce flicker. The + actually drawing is done in the virtual functions drawButton() and + drawButtonLabel(). + + \sa drawButton(), drawButtonLabel() +*/ +void QButton::paintEvent( QPaintEvent *) +{ + QSharedDoubleBuffer buffer( this ); + drawButton( buffer.painter() ); +} + +/*! \reimp */ +void QButton::focusInEvent( QFocusEvent * e) +{ + QWidget::focusInEvent( e ); +} + +/*! \reimp */ +void QButton::focusOutEvent( QFocusEvent * e ) +{ + buttonDown = FALSE; + QWidget::focusOutEvent( e ); +} + +/*! + Internal slot used for auto repeat. +*/ +void QButton::autoRepeatTimeout() +{ + if ( mlbDown && isEnabled() && autoRepeat() ) { + QGuardedPtr<QTimer> t = timer(); + if ( buttonDown ) { + emit released(); + emit clicked(); + emit pressed(); + } + if ( t ) + t->start( AUTO_REPEAT_PERIOD, TRUE ); + } +} + +/*! + Internal slot used for the second stage of animateClick(). +*/ +void QButton::animateTimeout() +{ + if ( !animation ) + return; + animation = FALSE; + buttonDown = FALSE; + nextState(); + emit released(); + emit clicked(); +} + + +void QButton::nextState() +{ + bool t = isToggleButton() && !( isOn() && isExclusiveToggle() ); + bool was = stat != Off; + if ( t ) { + if ( toggleTyp == Tristate ) + stat = ( stat + 1 ) % 3; + else + stat = stat ? Off : On; + } + if ( autoMask() ) + updateMask(); + repaint( FALSE ); + if ( t ) { +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::StateChanged ); +#endif + if ( was != (stat != Off) ) + emit toggled( stat != Off ); + emit stateChanged( stat ); + } +} + +/*! \reimp */ +void QButton::enabledChange( bool e ) +{ + if ( !isEnabled() ) + setDown( FALSE ); + QWidget::enabledChange( e ); +} + + +/*! + Toggles the state of a toggle button. + + \sa isOn(), setOn(), toggled(), isToggleButton() +*/ +void QButton::toggle() +{ + if ( isToggleButton() ) + setOn( !isOn() ); +} + +/*! + Sets the toggle type of the button to \a type. + + \a type can be set to \c SingleShot, \c Toggle and \c Tristate. +*/ +void QButton::setToggleType( ToggleType type ) +{ + toggleTyp = type; + if ( type != Tristate && stat == NoChange ) + setState( On ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + else + QAccessible::updateAccessibility( this, 0, QAccessible::StateChanged ); +#endif +} + +bool QButton::isExclusiveToggle() const +{ +#ifndef QT_NO_BUTTONGROUP + return group() && ( group()->isExclusive() || + group()->isRadioButtonExclusive() && + ::qt_cast<QRadioButton*>(this) ); +#else + return FALSE; +#endif +} + +#endif diff --git a/src/widgets/qbutton.h b/src/widgets/qbutton.h new file mode 100644 index 0000000..c8a5ea1 --- /dev/null +++ b/src/widgets/qbutton.h @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Definition of QButton widget class +** +** Created : 940206 +** +** 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. +** +**********************************************************************/ + +#ifndef QBUTTON_H +#define QBUTTON_H + +#ifndef QT_H +#include "qwidget.h" +#include "qkeysequence.h" +#endif // QT_H + +#ifndef QT_NO_BUTTON + + +class QButtonGroup; +class QToolBar; +class QButtonData; + +class Q_EXPORT QButton : public QWidget +{ + Q_OBJECT + Q_ENUMS( ToggleType ToggleState ) + Q_PROPERTY( QString text READ text WRITE setText ) + Q_PROPERTY( QPixmap pixmap READ pixmap WRITE setPixmap ) + Q_PROPERTY( QKeySequence accel READ accel WRITE setAccel ) + Q_PROPERTY( bool toggleButton READ isToggleButton ) + Q_PROPERTY( ToggleType toggleType READ toggleType ) + Q_PROPERTY( bool down READ isDown WRITE setDown DESIGNABLE false ) + Q_PROPERTY( bool on READ isOn ) + Q_PROPERTY( ToggleState toggleState READ state ) + Q_PROPERTY( bool autoResize READ autoResize WRITE setAutoResize DESIGNABLE false ) + Q_PROPERTY( bool autoRepeat READ autoRepeat WRITE setAutoRepeat ) + Q_PROPERTY( bool exclusiveToggle READ isExclusiveToggle ) + +public: + QButton( QWidget* parent=0, const char* name=0, WFlags f=0 ); + ~QButton(); + + QString text() const; + virtual void setText( const QString &); + const QPixmap *pixmap() const; + virtual void setPixmap( const QPixmap & ); + +#ifndef QT_NO_ACCEL + QKeySequence accel() const; + virtual void setAccel( const QKeySequence& ); +#endif + + bool isToggleButton() const; + + enum ToggleType { SingleShot, Toggle, Tristate }; + ToggleType toggleType() const; + + virtual void setDown( bool ); + bool isDown() const; + + bool isOn() const; + + enum ToggleState { Off, NoChange, On }; + ToggleState state() const; + +#ifndef QT_NO_COMPAT + bool autoResize() const; + void setAutoResize( bool ); +#endif + + bool autoRepeat() const; + virtual void setAutoRepeat( bool ); + bool isExclusiveToggle() const; + + QButtonGroup *group() const; + +public slots: + void animateClick(); + void toggle(); + +signals: + void pressed(); + void released(); + void clicked(); + void toggled( bool ); + void stateChanged( int ); + +protected: + void setToggleButton( bool ); + virtual void setToggleType( ToggleType ); + void setOn( bool ); + virtual void setState( ToggleState ); + + virtual bool hitButton( const QPoint &pos ) const; + virtual void drawButton( QPainter * ); + virtual void drawButtonLabel( QPainter * ); + + void keyPressEvent( QKeyEvent *); + void keyReleaseEvent( QKeyEvent *); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void paintEvent( QPaintEvent * ); + void focusInEvent( QFocusEvent * ); + void focusOutEvent( QFocusEvent * ); + + void enabledChange( bool ); + +private slots: + void animateTimeout(); + void autoRepeatTimeout(); + void emulateClick(); + +private: + QString btext; + QPixmap *bpixmap; + uint toggleTyp : 2; + uint buttonDown : 1; + uint stat : 2; + uint mlbDown : 1; + uint autoresize : 1; + uint animation : 1; + uint repeat : 1; + QButtonData *d; + + friend class QButtonGroup; + friend class QToolBar; + void ensureData(); + virtual void setGroup( QButtonGroup* ); + QTimer *timer(); + void nextState(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QButton( const QButton & ); + QButton &operator=( const QButton & ); +#endif +}; + + +inline QString QButton::text() const +{ + return btext; +} + +inline const QPixmap *QButton::pixmap() const +{ + return bpixmap; +} + +inline bool QButton::isToggleButton() const +{ + return ToggleType(toggleTyp) != SingleShot; +} + +inline bool QButton::isDown() const +{ + return buttonDown; +} + +inline bool QButton::isOn() const +{ + return ToggleState(stat) != Off; +} + +#ifndef QT_NO_COMPAT +inline bool QButton::autoResize() const +{ + return autoresize; +} +#endif + +inline bool QButton::autoRepeat() const +{ + return repeat; +} + +inline QButton::ToggleState QButton::state() const +{ + return ToggleState(stat); +} + +inline void QButton::setToggleButton( bool b ) +{ + setToggleType( b ? Toggle : SingleShot ); +} + +inline void QButton::setOn( bool y ) +{ + setState( y ? On : Off ); +} + +inline QButton::ToggleType QButton::toggleType() const +{ + return ToggleType(toggleTyp); +} + + +#endif // QT_NO_BUTTON + +#endif // QBUTTON_H diff --git a/src/widgets/qbuttongroup.cpp b/src/widgets/qbuttongroup.cpp new file mode 100644 index 0000000..ebcc580 --- /dev/null +++ b/src/widgets/qbuttongroup.cpp @@ -0,0 +1,682 @@ +/**************************************************************************** +** +** Implementation of QButtonGroup class +** +** Created : 950130 +** +** 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 "qbuttongroup.h" +#ifndef QT_NO_BUTTONGROUP +#include "qbutton.h" +#include "qptrlist.h" +#include "qapplication.h" +#include "qradiobutton.h" + + + +/*! + \class QButtonGroup qbuttongroup.h + \brief The QButtonGroup widget organizes QButton widgets in a group. + + \ingroup organizers + \ingroup geomanagement + \ingroup appearance + \mainclass + + A button group widget makes it easier to deal with groups of + buttons. Each button in a button group has a unique identifier. + The button group emits a clicked() signal with this identifier + when a button in the group is clicked. This makes a button group + particularly useful when you have several similar buttons and want + to connect all their clicked() signals to a single slot. + + An \link setExclusive() exclusive\endlink button group switches + off all toggle buttons except the one that was clicked. A button + group is, by default, non-exclusive. Note that all radio buttons + that are inserted into a button group are mutually exclusive even + if the button group is non-exclusive. (See + setRadioButtonExclusive().) + + There are two ways of using a button group: + \list + \i The button group is the parent widget of a number of buttons, + i.e. the button group is the parent argument in the button + constructor. The buttons are assigned identifiers 0, 1, 2, etc., + in the order they are created. A QButtonGroup can display a frame + and a title because it inherits QGroupBox. + \i The button group is an invisible widget and the contained + buttons have some other parent widget. In this usage, each button + must be manually inserted, using insert(), into the button group + and given an identifier. + \endlist + + A button can be removed from the group with remove(). A pointer to + a button with a given id can be obtained using find(). The id of a + button is available using id(). A button can be set \e on with + setButton(). The number of buttons in the group is returned by + count(). + + <img src=qbttngrp-m.png> <img src=qbttngrp-w.png> + + \sa QPushButton, QCheckBox, QRadioButton +*/ + +/*! + \property QButtonGroup::exclusive + \brief whether the button group is exclusive + + If this property is TRUE, then the buttons in the group are + toggled, and to untoggle a button you must click on another button + in the group. The default value is FALSE. +*/ + +/*! + \property QButtonGroup::radioButtonExclusive + \brief whether the radio buttons in the group are exclusive + + If this property is TRUE (the default), the \link QRadioButton + radiobuttons\endlink in the group are treated exclusively. +*/ + +struct QButtonItem +{ + QButton *button; + int id; +}; + + +class QButtonList: public QPtrList<QButtonItem> +{ +public: + QButtonList() {} + ~QButtonList() {} +}; + + +typedef QPtrListIterator<QButtonItem> QButtonListIt; + + +/*! + Constructs a button group with no title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +QButtonGroup::QButtonGroup( QWidget *parent, const char *name ) + : QGroupBox( parent, name ) +{ + init(); +} + +/*! + Constructs a button group with the title \a title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +QButtonGroup::QButtonGroup( const QString &title, QWidget *parent, + const char *name ) + : QGroupBox( title, parent, name ) +{ + init(); +} + +/*! + Constructs a button group with no title. Child widgets will be + arranged in \a strips rows or columns (depending on \a + orientation). + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +QButtonGroup::QButtonGroup( int strips, Orientation orientation, + QWidget *parent, const char *name ) + : QGroupBox( strips, orientation, parent, name ) +{ + init(); +} + +/*! + Constructs a button group with title \a title. Child widgets will + be arranged in \a strips rows or columns (depending on \a + orientation). + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +QButtonGroup::QButtonGroup( int strips, Orientation orientation, + const QString &title, QWidget *parent, + const char *name ) + : QGroupBox( strips, orientation, title, parent, name ) +{ + init(); +} + +/*! + Initializes the button group. +*/ + +void QButtonGroup::init() +{ + buttons = new QButtonList; + Q_CHECK_PTR( buttons ); + buttons->setAutoDelete( TRUE ); + excl_grp = FALSE; + radio_excl = TRUE; +} + +/*! \reimp */ + +QButtonGroup::~QButtonGroup() +{ + QButtonList * tmp = buttons; + QButtonItem *bi = tmp->first(); + buttons = 0; + while( bi ) { + bi->button->setGroup(0); + bi = tmp->next(); + } + delete tmp; +} + +bool QButtonGroup::isExclusive() const +{ + return excl_grp; +} + +void QButtonGroup::setExclusive( bool enable ) +{ + excl_grp = enable; +} + + +/*! + Inserts the \a button with the identifier \a id into the button + group. Returns the button identifier. + + Buttons are normally inserted into a button group automatically by + passing the button group as the parent when the button is + constructed. So it is not necessary to manually insert buttons + that have this button group as their parent widget. An exception + is when you want custom identifiers instead of the default 0, 1, + 2, etc., or if you want the buttons to have some other parent. + + The button is assigned the identifier \a id or an automatically + generated identifier. It works as follows: If \a id >= 0, this + identifier is assigned. If \a id == -1 (default), the identifier + is equal to the number of buttons in the group. If \a id is any + other negative integer, for instance -2, a unique identifier + (negative integer \<= -2) is generated. No button has an id of -1. + + \sa find(), remove(), setExclusive() +*/ + +int QButtonGroup::insert( QButton *button, int id ) +{ + if ( button->group() ) + button->group()->remove( button ); + + static int seq_no = -2; + QButtonItem *bi = new QButtonItem; + Q_CHECK_PTR( bi ); + + if ( id < -1 ) + bi->id = seq_no--; + else if ( id == -1 ) + bi->id = buttons->count(); + else + bi->id = id; + + bi->button = button; + button->setGroup(this); + buttons->append( bi ); + + connect( button, SIGNAL(pressed()) , SLOT(buttonPressed()) ); + connect( button, SIGNAL(released()), SLOT(buttonReleased()) ); + connect( button, SIGNAL(clicked()) , SLOT(buttonClicked()) ); + connect( button, SIGNAL(toggled(bool)) , SLOT(buttonToggled(bool)) ); + + if ( button->isToggleButton() && !button->isOn() && + selected() && (selected()->focusPolicy() & TabFocus) != 0 ) + button->setFocusPolicy( (FocusPolicy)(button->focusPolicy() & + ~TabFocus) ); + + return bi->id; +} + +/*! + Returns the number of buttons in the group. +*/ +int QButtonGroup::count() const +{ + return buttons->count(); +} + +/*! + Removes the \a button from the button group. + + \sa insert() +*/ + +void QButtonGroup::remove( QButton *button ) +{ + if ( !buttons ) + return; + + QButtonListIt it( *buttons ); + QButtonItem *i; + while ( (i=it.current()) != 0 ) { + ++it; + if ( i->button == button ) { + buttons->remove( i ); + button->setGroup(0); + button->disconnect( this ); + return; + } + } +} + + +/*! + Returns the button with the specified identifier \a id, or 0 if + the button was not found. +*/ + +QButton *QButtonGroup::find( int id ) const +{ + // introduce a QButtonListIt if calling anything + for ( QButtonItem *i=buttons->first(); i; i=buttons->next() ) + if ( i->id == id ) + return i->button; + return 0; +} + + +/*! + \fn void QButtonGroup::pressed( int id ) + + This signal is emitted when a button in the group is \link + QButton::pressed() pressed\endlink. The \a id argument is the + button's identifier. + + \sa insert() +*/ + +/*! + \fn void QButtonGroup::released( int id ) + + This signal is emitted when a button in the group is \link + QButton::released() released\endlink. The \a id argument is the + button's identifier. + + \sa insert() +*/ + +/*! + \fn void QButtonGroup::clicked( int id ) + + This signal is emitted when a button in the group is \link + QButton::clicked() clicked\endlink. The \a id argument is the + button's identifier. + + \sa insert() +*/ + + +/*! + \internal + This slot is activated when one of the buttons in the group emits the + QButton::pressed() signal. +*/ + +void QButtonGroup::buttonPressed() +{ + // introduce a QButtonListIt if calling anything + int id = -1; + QButton *bt = (QButton *)sender(); // object that sent the signal + for ( register QButtonItem *i=buttons->first(); i; i=buttons->next() ) + if ( bt == i->button ) { // button was clicked + id = i->id; + break; + } + if ( id != -1 ) + emit pressed( id ); +} + +/*! + \internal + This slot is activated when one of the buttons in the group emits the + QButton::released() signal. +*/ + +void QButtonGroup::buttonReleased() +{ + // introduce a QButtonListIt if calling anything + int id = -1; + QButton *bt = (QButton *)sender(); // object that sent the signal + for ( register QButtonItem *i=buttons->first(); i; i=buttons->next() ) + if ( bt == i->button ) { // button was clicked + id = i->id; + break; + } + if ( id != -1 ) + emit released( id ); +} + +/*! + \internal + This slot is activated when one of the buttons in the group emits the + QButton::clicked() signal. +*/ + +void QButtonGroup::buttonClicked() +{ + // introduce a QButtonListIt if calling anything + int id = -1; + QButton *bt = ::qt_cast<QButton*>(sender()); // object that sent the signal +#if defined(QT_CHECK_NULL) + Q_ASSERT( bt ); +#endif + for ( register QButtonItem *i=buttons->first(); i; i=buttons->next() ) { + if ( bt == i->button ) { // button was clicked + id = i->id; + break; + } + } + if ( id != -1 ) + emit clicked( id ); +} + + +/*! + \internal + This slot is activated when one of the buttons in the group emits the + QButton::toggled() signal. +*/ + +void QButtonGroup::buttonToggled( bool on ) +{ + // introduce a QButtonListIt if calling anything + if ( !on || !excl_grp && !radio_excl ) + return; + QButton *bt = ::qt_cast<QButton*>(sender()); // object that sent the signal +#if defined(QT_CHECK_NULL) + Q_ASSERT( bt ); + Q_ASSERT( bt->isToggleButton() ); +#endif + + if ( !excl_grp && !::qt_cast<QRadioButton*>(bt) ) + return; + QButtonItem * i = buttons->first(); + bool hasTabFocus = FALSE; + + while( i != 0 && hasTabFocus == FALSE ) { + if ( ( excl_grp || ::qt_cast<QRadioButton*>(i->button) ) && + (i->button->focusPolicy() & TabFocus) ) + hasTabFocus = TRUE; + i = buttons->next(); + } + + i = buttons->first(); + while( i ) { + if ( bt != i->button && + i->button->isToggleButton() && + i->button->isOn() && + ( excl_grp || ::qt_cast<QRadioButton*>(i->button) ) ) + i->button->setOn( FALSE ); + if ( ( excl_grp || ::qt_cast<QRadioButton*>(i->button) ) && + i->button->isToggleButton() && + hasTabFocus ) + i->button->setFocusPolicy( (FocusPolicy)(i->button->focusPolicy() & + ~TabFocus) ); + i = buttons->next(); + } + + if ( hasTabFocus ) + bt->setFocusPolicy( (FocusPolicy)(bt->focusPolicy() | TabFocus) ); +} + + + +void QButtonGroup::setButton( int id ) +{ + QButton * b = find( id ); + if ( b ) + b->setOn( TRUE ); +} + +void QButtonGroup::setRadioButtonExclusive( bool on) +{ + radio_excl = on; +} + + +/*! + Moves the keyboard focus according to \a key, and if appropriate + checks the new focus item. + + This function does nothing unless the keyboard focus points to one + of the button group members and \a key is one of \c Key_Up, \c + Key_Down, \c Key_Left and \c Key_Right. +*/ + +void QButtonGroup::moveFocus( int key ) +{ + QWidget * f = qApp->focusWidget(); + + QButtonItem * i; + i = buttons->first(); + while( i && i->button != f ) + i = buttons->next(); + + if ( !i || !i->button ) + return; + + QWidget * candidate = 0; + int bestScore = -1; + + QPoint goal( f->mapToGlobal( f->geometry().center() ) ); + + i = buttons->first(); + while( i && i->button ) { + if ( i->button != f && + i->button->isEnabled() ) { + QPoint p(i->button->mapToGlobal(i->button->geometry().center())); + int score = (p.y() - goal.y())*(p.y() - goal.y()) + + (p.x() - goal.x())*(p.x() - goal.x()); + bool betterScore = score < bestScore || !candidate; + switch( key ) { + case Key_Up: + if ( p.y() < goal.y() && betterScore ) { + if ( QABS( p.x() - goal.x() ) < QABS( p.y() - goal.y() ) ) { + candidate = i->button; + bestScore = score; + } else if ( i->button->x() == f->x() ) { + candidate = i->button; + bestScore = score/2; + } + } + break; + case Key_Down: + if ( p.y() > goal.y() && betterScore ) { + if ( QABS( p.x() - goal.x() ) < QABS( p.y() - goal.y() ) ) { + candidate = i->button; + bestScore = score; + } else if ( i->button->x() == f->x() ) { + candidate = i->button; + bestScore = score/2; + } + } + break; + case Key_Left: + if ( p.x() < goal.x() && betterScore ) { + if ( QABS( p.y() - goal.y() ) < QABS( p.x() - goal.x() ) ) { + candidate = i->button; + bestScore = score; + } else if ( i->button->y() == f->y() ) { + candidate = i->button; + bestScore = score/2; + } + } + break; + case Key_Right: + if ( p.x() > goal.x() && betterScore ) { + if ( QABS( p.y() - goal.y() ) < QABS( p.x() - goal.x() ) ) { + candidate = i->button; + bestScore = score; + } else if ( i->button->y() == f->y() ) { + candidate = i->button; + bestScore = score/2; + } + } + break; + } + } + i = buttons->next(); + } + + QButton *buttoncand = ::qt_cast<QButton*>(candidate); + if ( buttoncand && ::qt_cast<QButton*>(f) && + ((QButton*)f)->isOn() && + buttoncand->isToggleButton() && + ( isExclusive() || ( ::qt_cast<QRadioButton*>(f) && + ::qt_cast<QRadioButton*>(candidate)))) { + if ( f->focusPolicy() & TabFocus ) { + f->setFocusPolicy( (FocusPolicy)(f->focusPolicy() & ~TabFocus) ); + candidate->setFocusPolicy( (FocusPolicy)(candidate->focusPolicy()| + TabFocus) ); + } + buttoncand->setOn( TRUE ); + buttoncand->animateClick(); + buttoncand->animateTimeout(); // ### crude l&f hack + } + + if ( candidate ) { + if (key == Key_Up || key == Key_Left) + QFocusEvent::setReason(QFocusEvent::Backtab); + else + QFocusEvent::setReason(QFocusEvent::Tab); + candidate->setFocus(); + QFocusEvent::resetReason(); + } +} + + +/*! + Returns the selected toggle button if exactly one is selected; + otherwise returns 0. + + \sa selectedId() +*/ + +QButton * QButtonGroup::selected() const +{ + if ( !buttons ) + return 0; + QButtonListIt it( *buttons ); + QButtonItem *i; + QButton *candidate = 0; + + while ( (i = it.current()) != 0 ) { + ++it; + if ( i->button && i->button->isToggleButton() && i->button->isOn() ) { + if ( candidate != 0 ) + return 0; + candidate = i->button; + } + } + return candidate; +} + +/*! + \property QButtonGroup::selectedId + \brief the selected toggle button + + The toggle button is specified as an ID. + + If no toggle button is selected, this property holds -1. + + If setButton() is called on an exclusive group, the button with + the given id will be set to on and all the others will be set to + off. + + \sa selected() +*/ + +int QButtonGroup::selectedId() const +{ + return id( selected() ); +} + + +/*! + Returns the id of \a button, or -1 if \a button is not a member of + this group. + + \sa selectedId(); +*/ + +int QButtonGroup::id( QButton * button ) const +{ + QButtonItem *i = buttons->first(); + while ( i && i->button != button ) + i = buttons->next(); + return i ? i->id : -1; +} + + +/*! + \reimp +*/ +bool QButtonGroup::event( QEvent * e ) +{ + if ( e->type() == QEvent::ChildInserted ) { + QChildEvent * ce = (QChildEvent *) e; + if ( radio_excl && ::qt_cast<QRadioButton*>(ce->child()) ) { + QButton * button = (QButton *) ce->child(); + if ( button->isToggleButton() && !button->isOn() && + selected() && (selected()->focusPolicy() & TabFocus) != 0 ) + button->setFocusPolicy( (FocusPolicy)(button->focusPolicy() & + ~TabFocus) ); + } + } + return QGroupBox::event( e ); +} +#endif diff --git a/src/widgets/qbuttongroup.h b/src/widgets/qbuttongroup.h new file mode 100644 index 0000000..3fd76f4 --- /dev/null +++ b/src/widgets/qbuttongroup.h @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Definition of QButtonGroup class +** +** Created : 950130 +** +** 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. +** +**********************************************************************/ + +#ifndef QBUTTONGROUP_H +#define QBUTTONGROUP_H + +#ifndef QT_H +#include "qgroupbox.h" +#endif // QT_H + +#ifndef QT_NO_BUTTONGROUP + + +class QButton; +class QButtonList; + + +class Q_EXPORT QButtonGroup : public QGroupBox +{ + Q_OBJECT + Q_PROPERTY( bool exclusive READ isExclusive WRITE setExclusive ) + Q_PROPERTY( bool radioButtonExclusive READ isRadioButtonExclusive WRITE setRadioButtonExclusive ) + Q_PROPERTY( int selectedId READ selectedId WRITE setButton ) + +public: + QButtonGroup( QWidget* parent=0, const char* name=0 ); + QButtonGroup( const QString &title, + QWidget* parent=0, const char* name=0 ); + QButtonGroup( int columns, Orientation o, + QWidget* parent=0, const char* name=0 ); + QButtonGroup( int columns, Orientation o, const QString &title, + QWidget* parent=0, const char* name=0 ); + ~QButtonGroup(); + + bool isExclusive() const; + bool isRadioButtonExclusive() const { return radio_excl; } + virtual void setExclusive( bool ); + virtual void setRadioButtonExclusive( bool ); + +public: + int insert( QButton *, int id=-1 ); + void remove( QButton * ); + QButton *find( int id ) const; + int id( QButton * ) const; + int count() const; + + virtual void setButton( int id ); + + virtual void moveFocus( int ); + + QButton *selected() const; + int selectedId() const; + +signals: + void pressed( int id ); + void released( int id ); + void clicked( int id ); + +protected slots: + void buttonPressed(); + void buttonReleased(); + void buttonClicked(); + void buttonToggled( bool on ); + +protected: + bool event( QEvent * e ); + +private: + void init(); + bool excl_grp; + bool radio_excl; + QButtonList *buttons; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QButtonGroup( const QButtonGroup & ); + QButtonGroup &operator=( const QButtonGroup & ); +#endif +}; + + +#endif // QT_NO_BUTTONGROUP + +#endif // QBUTTONGROUP_H diff --git a/src/widgets/qcheckbox.cpp b/src/widgets/qcheckbox.cpp new file mode 100644 index 0000000..640f72a --- /dev/null +++ b/src/widgets/qcheckbox.cpp @@ -0,0 +1,364 @@ +/**************************************************************************** +** +** Implementation of QCheckBox class +** +** Created : 940222 +** +** 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 "qcheckbox.h" +#ifndef QT_NO_CHECKBOX +#include "qpainter.h" +#include "qdrawutil.h" +#include "qpixmap.h" +#include "qpixmapcache.h" +#include "qbitmap.h" +#include "qtextstream.h" +#include "qapplication.h" +#include "qstyle.h" + +/*! + \class QCheckBox qcheckbox.h + \brief The QCheckBox widget provides a checkbox with a text label. + + \ingroup basic + \mainclass + + QCheckBox and QRadioButton are both option buttons. That is, they + can be switched on (checked) or off (unchecked). The classes + differ in how the choices for the user are restricted. Radio + buttons define a "one of many" choice, whereas checkboxes provide + "many of many" choices. + + A QButtonGroup can be used to group check buttons visually. + + Whenever a checkbox is checked or cleared it emits the signal + toggled(). Connect to this signal if you want to trigger an action + each time the checkbox changes state. You can use isChecked() to + query whether or not a checkbox is checked. + + \warning The toggled() signal can not be trusted for tristate + checkboxes. + + In addition to the usual checked and unchecked states, QCheckBox + optionally provides a third state to indicate "no change". This + is useful whenever you need to give the user the option of neither + checking nor unchecking a checkbox. If you need this third state, + enable it with setTristate() and use state() to query the current + toggle state. When a tristate checkbox changes state, it emits the + stateChanged() signal. + + Just like QPushButton, a checkbox can display text or a pixmap. + The text can be set in the constructor or with setText(); the + pixmap is set with setPixmap(). + + \important text(), setText(), text(), pixmap(), setPixmap(), + accel(), setAccel(), isToggleButton(), setDown(), isDown(), + isOn(), state(), autoRepeat(), isExclusiveToggle(), group(), + setAutoRepeat(), toggle(), pressed(), released(), clicked(), + toggled(), state() stateChanged() + + <img src=qchkbox-m.png> <img src=qchkbox-w.png> + + \sa QButton QRadioButton + \link guibooks.html#fowler Fowler: Check Box \endlink +*/ + +/*! + \property QCheckBox::checked + \brief whether the checkbox is checked + + The default is unchecked, i.e. FALSE. +*/ + +/*! + \property QCheckBox::autoMask + \brief whether the checkbox is automatically masked + + \sa QWidget::setAutoMask() +*/ + +/*! + \property QCheckBox::tristate + \brief whether the checkbox is a tri-state checkbox + + The default is two-state, i.e. tri-state is FALSE. +*/ + +/*! + Constructs a checkbox with no text. + + The \a parent and \a name arguments are sent to the QWidget + constructor. +*/ + +QCheckBox::QCheckBox( QWidget *parent, const char *name ) + : QButton( parent, name, WNoAutoErase | WMouseNoMask ) +{ + setToggleButton( TRUE ); + setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) ); +} + +/*! + Constructs a checkbox with text \a text. + + The \a parent and \a name arguments are sent to the QWidget + constructor. +*/ + +QCheckBox::QCheckBox( const QString &text, QWidget *parent, const char *name ) + : QButton( parent, name, WNoAutoErase | WMouseNoMask ) +{ + setText( text ); + setToggleButton( TRUE ); + setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) ); +} + +/*! + Sets the checkbox to the "no change" state. + + \sa setTristate() +*/ +void QCheckBox::setNoChange() +{ + setTristate(TRUE); + setState( NoChange ); +} + +void QCheckBox::setTristate(bool y) +{ + setToggleType( y ? Tristate : Toggle ); +} + +bool QCheckBox::isTristate() const +{ + return toggleType() == Tristate; +} + + +/*!\reimp +*/ +QSize QCheckBox::sizeHint() const +{ + // NB: QRadioButton::sizeHint() is similar + constPolish(); + + QPainter p(this); + QSize sz = style().itemRect(&p, QRect(0, 0, 1, 1), ShowPrefix, FALSE, + pixmap(), text()).size(); + + return (style().sizeFromContents(QStyle::CT_CheckBox, this, sz). + expandedTo(QApplication::globalStrut())); +} + + +/*!\reimp +*/ + +void QCheckBox::drawButton( QPainter *paint ) +{ + QPainter *p = paint; + QRect irect = QStyle::visualRect( style().subRect(QStyle::SR_CheckBoxIndicator, this), this ); + const QColorGroup &cg = colorGroup(); + +#if !defined( QT_NO_TEXTSTREAM ) && !defined( Q_WS_MACX ) +# define SAVE_CHECKBOX_PIXMAPS +#endif +#if defined(SAVE_CHECKBOX_PIXMAPS) + QString pmkey; // pixmap key + int kf = 0; + if ( isDown() ) + kf |= 1; + if ( isEnabled() ) + kf |= 2; + if ( hasFocus() ) + kf |= 4; // active vs. normal colorgroup + if( isActiveWindow() ) + kf |= 8; + if ( hasMouse() ) + kf |= 16; + + kf |= state() << 5; + QTextOStream os(&pmkey); + os << "$qt_check_" << style().className() << "_" + << palette().serialNumber() << "_" << irect.width() << "x" << irect.height() << "_" << kf; + QPixmap *pm = QPixmapCache::find( pmkey ); + if ( pm ) { // pixmap exists + p->drawPixmap( irect.topLeft(), *pm ); + drawButtonLabel( p ); + return; + } + bool use_pm = TRUE; + QPainter pmpaint; + int wx = 0, wy = 0; + if ( use_pm ) { + pm = new QPixmap( irect.size() ); // create new pixmap + Q_CHECK_PTR( pm ); + pm->fill( cg.background() ); + QPainter::redirect(this, pm); + pmpaint.begin(this); + p = &pmpaint; // draw in pixmap + wx = irect.x(); // save x,y coords + wy = irect.y(); + irect.moveTopLeft(QPoint(0, 0)); + p->setBackgroundColor( cg.background() ); + } +#endif + + QStyle::SFlags flags = QStyle::Style_Default; + if ( isEnabled() ) + flags |= QStyle::Style_Enabled; + if ( hasFocus() ) + flags |= QStyle::Style_HasFocus; + if ( isDown() ) + flags |= QStyle::Style_Down; + if ( hasMouse() ) + flags |= QStyle::Style_MouseOver; + if ( state() == QButton::On ) + flags |= QStyle::Style_On; + else if ( state() == QButton::Off ) + flags |= QStyle::Style_Off; + else if ( state() == QButton::NoChange ) + flags |= QStyle::Style_NoChange; + + style().drawControl(QStyle::CE_CheckBox, p, this, irect, cg, flags); + +#if defined(SAVE_CHECKBOX_PIXMAPS) + if ( use_pm ) { + pmpaint.end(); + QPainter::redirect( this, 0 ); + if ( backgroundPixmap() || backgroundMode() == X11ParentRelative ) { + QBitmap bm( pm->size() ); + bm.fill( color0 ); + pmpaint.begin( &bm ); + style().drawControlMask(QStyle::CE_CheckBox, &pmpaint, this, irect); + pmpaint.end(); + pm->setMask( bm ); + } + p = paint; // draw in default device + p->drawPixmap( wx, wy, *pm ); + if (!QPixmapCache::insert(pmkey, pm) ) // save in cache + delete pm; + } +#endif + + drawButtonLabel( paint ); +} + + +/*!\reimp +*/ +void QCheckBox::drawButtonLabel( QPainter *p ) +{ + QRect r = + QStyle::visualRect( style().subRect(QStyle::SR_CheckBoxContents, this), this ); + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (hasFocus()) + flags |= QStyle::Style_HasFocus; + if (isDown()) + flags |= QStyle::Style_Down; + if (state() == QButton::On) + flags |= QStyle::Style_On; + else if (state() == QButton::Off) + flags |= QStyle::Style_Off; + else if (state() == QButton::NoChange) + flags |= QStyle::Style_NoChange; + + style().drawControl(QStyle::CE_CheckBoxLabel, p, this, r, colorGroup(), flags); +} + +/*! + \reimp +*/ +void QCheckBox::resizeEvent( QResizeEvent *e ) +{ + QButton::resizeEvent(e); + if ( isVisible() ) { + QPainter p(this); + QSize isz = style().itemRect(&p, QRect(0, 0, 1, 1), ShowPrefix, FALSE, + pixmap(), text()).size(); + QSize wsz = (style().sizeFromContents(QStyle::CT_CheckBox, this, isz). + expandedTo(QApplication::globalStrut())); + + update(wsz.width(), isz.width(), 0, wsz.height()); + } + if (autoMask()) + updateMask(); +} + +/*! + \reimp +*/ +void QCheckBox::updateMask() +{ + QRect irect = + QStyle::visualRect( style().subRect(QStyle::SR_CheckBoxIndicator, this), this ); + + QBitmap bm(width(), height()); + bm.fill(color0); + + QPainter p( &bm, this ); + style().drawControlMask(QStyle::CE_CheckBox, &p, this, irect); + if ( ! text().isNull() || ( pixmap() && ! pixmap()->isNull() ) ) { + QRect crect = + QStyle::visualRect( style().subRect( QStyle::SR_CheckBoxContents, + this ), this ); + QRect frect = + QStyle::visualRect( style().subRect( QStyle::SR_CheckBoxFocusRect, + this ), this ); + QRect label(crect.unite(frect)); + p.fillRect(label, color1); + } + p.end(); + + setMask(bm); +} + +/*!\reimp*/ +bool QCheckBox::hitButton( const QPoint &pos ) const +{ + QRect r = QStyle::visualRect( style().subRect( QStyle::SR_CheckBoxFocusRect, this ), this ); + if ( qApp->reverseLayout() ) { + r.setRight( width() ); + } else { + r.setLeft( 0 ); + } + return r.contains( pos ); +} + +#endif diff --git a/src/widgets/qcheckbox.h b/src/widgets/qcheckbox.h new file mode 100644 index 0000000..81a92e9 --- /dev/null +++ b/src/widgets/qcheckbox.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Definition of QCheckBox class +** +** Created : 940222 +** +** 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. +** +**********************************************************************/ + +#ifndef QCHECKBOX_H +#define QCHECKBOX_H + +#ifndef QT_H +#include "qbutton.h" +#endif // QT_H + +#ifndef QT_NO_CHECKBOX + +class Q_EXPORT QCheckBox : public QButton +{ + Q_OBJECT + Q_PROPERTY( bool checked READ isChecked WRITE setChecked ) + Q_PROPERTY( bool tristate READ isTristate WRITE setTristate ) + Q_OVERRIDE( bool autoMask DESIGNABLE true SCRIPTABLE true ) + +public: + QCheckBox( QWidget *parent, const char* name=0 ); + QCheckBox( const QString &text, QWidget *parent, const char* name=0 ); + + bool isChecked() const; + + void setNoChange(); + + void setTristate(bool y=TRUE); + bool isTristate() const; + + QSize sizeHint() const; + +public slots: + void setChecked( bool check ); + +protected: + void resizeEvent( QResizeEvent* ); + void drawButton( QPainter * ); + void drawButtonLabel( QPainter * ); + void updateMask(); + bool hitButton( const QPoint &pos ) const; +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QCheckBox( const QCheckBox & ); + QCheckBox &operator=( const QCheckBox & ); +#endif +}; + + +inline bool QCheckBox::isChecked() const +{ return isOn(); } + +inline void QCheckBox::setChecked( bool check ) +{ setOn( check ); } + + +#endif // QT_NO_CHECKBOX + +#endif // QCHECKBOX_H diff --git a/src/widgets/qcombobox.cpp b/src/widgets/qcombobox.cpp new file mode 100644 index 0000000..1da559c --- /dev/null +++ b/src/widgets/qcombobox.cpp @@ -0,0 +1,2334 @@ +/********************************************************************** +** +** Implementation of QComboBox widget class +** +** Created : 940426 +** +** 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 "qcombobox.h" +#ifndef QT_NO_COMBOBOX +#include "qpopupmenu.h" +#include "qlistbox.h" +#include "qpainter.h" +#include "qdrawutil.h" +#include "qstrlist.h" +#include "qpixmap.h" +#include "qtimer.h" +#include "qapplication.h" +#include "qlineedit.h" +#include "qbitmap.h" +#include "qeffects_p.h" +#include "qstringlist.h" +#include "qcombobox.h" +#include "qstyle.h" +#include <limits.h> +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif + +/*! + \class QComboBox qcombobox.h + \brief The QComboBox widget is a combined button and popup list. + + \ingroup basic + \mainclass + + A combobox is a selection widget which displays the current item + and can pop up a list of items. A combobox may be editable in + which case the user can enter arbitrary strings. + + Comboboxes provide a means of showing the user's current choice + out of a list of options in a way that takes up the minimum amount + of screen space. + + QComboBox supports three different display styles: Aqua/Motif 1.x, + Motif 2.0 and Windows. In Motif 1.x, a combobox was called + XmOptionMenu. In Motif 2.0, OSF introduced an improved combobox + and named that XmComboBox. QComboBox provides both. + + QComboBox provides two different constructors. The simplest + constructor creates an "old-style" combobox in Motif (or Aqua) + style: + \code + QComboBox *c = new QComboBox( this, "read-only combobox" ); + \endcode + + The other constructor creates a new-style combobox in Motif style, + and can create both read-only and editable comboboxes: + \code + QComboBox *c1 = new QComboBox( FALSE, this, "read-only combobox" ); + QComboBox *c2 = new QComboBox( TRUE, this, "editable combobox" ); + \endcode + + New-style comboboxes use a list box in both Motif and Windows + styles, and both the content size and the on-screen size of the + list box can be limited with sizeLimit() and setMaxCount() + respectively. Old-style comboboxes use a popup in Aqua and Motif + style, and that popup will happily grow larger than the desktop if + you put enough data into it. + + The two constructors create identical-looking comboboxes in + Windows style. + + Comboboxes can contain pixmaps as well as strings; the + insertItem() and changeItem() functions are suitably overloaded. + For editable comboboxes, the function clearEdit() is provided, + to clear the displayed string without changing the combobox's + contents. + + A combobox emits two signals, activated() and highlighted(), when + a new item has been activated (selected) or highlighted (made + current). Both signals exist in two versions, one with a \c + QString argument and one with an \c int argument. If the user + highlights or activates a pixmap, only the \c int signals are + emitted. Whenever the text of an editable combobox is changed the + textChanged() signal is emitted. + + When the user enters a new string in an editable combobox, the + widget may or may not insert it, and it can insert it in several + locations. The default policy is is \c AtBottom but you can change + this using setInsertionPolicy(). + + It is possible to constrain the input to an editable combobox + using QValidator; see setValidator(). By default, any input is + accepted. + + If the combobox is not editable then it has a default + focusPolicy() of \c TabFocus, i.e. it will not grab focus if + clicked. This differs from both Windows and Motif. If the combobox + is editable then it has a default focusPolicy() of \c StrongFocus, + i.e. it will grab focus if clicked. + + A combobox can be populated using the insert functions, + insertStringList() and insertItem() for example. Items can be + changed with changeItem(). An item can be removed with + removeItem() and all items can be removed with clear(). The text + of the current item is returned by currentText(), and the text of + a numbered item is returned with text(). The current item can be + set with setCurrentItem() or setCurrentText(). The number of items + in the combobox is returned by count(); the maximum number of + items can be set with setMaxCount(). You can allow editing using + setEditable(). For editable comboboxes you can set auto-completion + using setAutoCompletion() and whether or not the user can add + duplicates is set with setDuplicatesEnabled(). + + <img src="qcombo1-m.png">(Motif 1, read-only)<br clear=all> + <img src="qcombo2-m.png">(Motif 2, editable)<br clear=all> + <img src="qcombo3-m.png">(Motif 2, read-only)<br clear=all> + <img src="qcombo1-w.png">(Windows style) + + Depending on the style, QComboBox will use a QListBox or a + QPopupMenu to display the list of items. See setListBox() for + more information. + + \sa QLineEdit QListBox QSpinBox QRadioButton QButtonGroup + \link guibooks.html#fowler GUI Design Handbook: Combo Box,\endlink + \link guibooks.html#fowler GUI Design Handbook: Drop-Down List Box.\endlink +*/ + + +/*! + \enum QComboBox::Policy + + This enum specifies what the QComboBox should do when a new string + is entered by the user. + + \value NoInsertion the string will not be inserted into the + combobox. + + \value AtTop insert the string as the first item in the combobox. + + \value AtCurrent replace the previously selected item with the + string the user has entered. + + \value AtBottom insert the string as the last item in the + combobox. + + \value AfterCurrent insert the string after the previously + selected item. + + \value BeforeCurrent insert the string before the previously + selected item. + + activated() is always emitted when the string is entered. + + If inserting the new string would cause the combobox to breach its + content size limit, the item at the other end of the list is + deleted. The definition of "other end" is + implementation-dependent. +*/ + + +/*! + \fn void QComboBox::activated( int index ) + + This signal is emitted when a new item has been activated + (selected). The \a index is the position of the item in the + combobox. + + This signal is not emitted if the item is changed + programmatically, e.g. using setCurrentItem(). +*/ + +/*! + \overload void QComboBox::activated( const QString &string ) + + This signal is emitted when a new item has been activated + (selected). \a string is the selected string. + + You can also use the activated(int) signal, but be aware that its + argument is meaningful only for selected strings, not for user + entered strings. +*/ + +/*! + \fn void QComboBox::highlighted( int index ) + + This signal is emitted when a new item has been set to be the + current item. The \a index is the position of the item in the + combobox. + + This signal is not emitted if the item is changed + programmatically, e.g. using setCurrentItem(). +*/ + +/*! + \overload void QComboBox::highlighted( const QString &string ) + + This signal is emitted when a new item has been set to be the + current item. \a string is the item's text. + + You can also use the highlighted(int) signal. +*/ + +/*! + \fn void QComboBox::textChanged( const QString &string ) + + This signal is used for editable comboboxes. It is emitted + whenever the contents of the text entry field changes. \a string + contains the new text. +*/ + +/*! + \property QComboBox::autoCompletion + \brief whether auto-completion is enabled + + This property can only be set for editable comboboxes, for + non-editable comboboxes it has no effect. It is FALSE by default. +*/ + +/*! + \property QComboBox::autoMask + \brief whether the combobox is automatically masked + + \sa QWidget::setAutoMask() +*/ + +/*! \property QComboBox::autoResize + \brief whether auto resize is enabled + \obsolete + + If this property is set to TRUE then the combobox will resize itself + whenever its contents change. The default is FALSE. +*/ + +/*! + \property QComboBox::count + \brief the number of items in the combobox +*/ + +/*! + \property QComboBox::currentItem + \brief the index of the current item in the combobox + + Note that the activated() and highlighted() signals are only + emitted when the user changes the current item, not when it is + changed programmatically. +*/ + +/*! + \property QComboBox::currentText + \brief the text of the combobox's current item +*/ + +/*! + \property QComboBox::duplicatesEnabled + \brief whether duplicates are allowed + + If the combobox is editable and the user enters some text in the + combobox's lineedit and presses Enter (and the insertionPolicy() + is not \c NoInsertion), then what happens is this: + \list + \i If the text is not already in the list, the text is inserted. + \i If the text is in the list and this property is TRUE (the + default), the text is inserted. + \i If the text is in the list and this property is FALSE, the text + is \e not inserted; instead the item which has matching text becomes + the current item. + \endlist + + This property only affects user-interaction. You can use + insertItem() to insert duplicates if you wish regardless of this + setting. +*/ + +/*! + \property QComboBox::editable + \brief whether the combobox is editable + + This property's default is FALSE. Note that the combobox will be + cleared if this property is set to TRUE for a 1.x Motif style + combobox. To avoid this, use setEditable() before inserting any + items. Also note that the 1.x version of Motif didn't have any + editable comboboxes, so the combobox will change it's appearance + to a 2.0 style Motif combobox is it is set to be editable. +*/ + +/*! + \property QComboBox::insertionPolicy + \brief the position of the items inserted by the user + + The default insertion policy is \c AtBottom. See \l Policy. +*/ + +/*! + \property QComboBox::maxCount + \brief the maximum number of items allowed in the combobox +*/ + +/*! + \property QComboBox::sizeLimit + \brief the maximum on-screen size of the combobox. + + This property is ignored for both Motif 1.x style and non-editable + comboboxes in Mac style. The default limit is ten + lines. If the number of items in the combobox is or grows larger + than lines, a scrollbar is added. +*/ + +class QComboBoxPopup : public QPopupMenu +{ +public: + QComboBoxPopup( QWidget *parent=0, const char *name=0 ) + : QPopupMenu( parent, name ) + { + } + + int itemHeight( int index ) + { + return QPopupMenu::itemHeight( index ); + } +}; + +static inline QString escapedComboString(const QString &str) +{ + QString stringToReturn = str; + return stringToReturn.replace('&', "&&"); +} + +class QComboBoxPopupItem : public QCustomMenuItem +{ + QListBoxItem *li; + QSize sc; // Size cache optimization +public: + QComboBoxPopupItem(QListBoxItem *i) : QCustomMenuItem(), li(i), sc(0, 0) { } + virtual bool fullSpan() const { return TRUE; } + virtual void paint( QPainter*, const QColorGroup&, bool, bool, int, int, int, int); + virtual QSize sizeHint() { if (sc.isNull()) sc = QSize(li->width(li->listBox()), QMAX(25, li->height(li->listBox()))); return sc; } +}; +void QComboBoxPopupItem::paint( QPainter* p, const QColorGroup&, bool, + bool, int x, int y, int, int) +{ + p->save(); + p->translate(x, y + ((sizeHint().height() / 2) - (li->height(li->listBox()) / 2))); + li->paint(p); + p->restore(); +} + + +class QComboBoxData +{ +public: + QComboBoxData( QComboBox *cb ): ed( 0 ), usingLBox( FALSE ), pop( 0 ), lBox( 0 ), combo( cb ) + { + duplicatesEnabled = TRUE; + cb->setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) ); + } + + inline bool usingListBox() { return usingLBox; } + inline QListBox * listBox() { return lBox; } + inline QComboBoxPopup * popup() { return pop; } + void updateLinedGeometry(); + void setListBox( QListBox *l ); + void setPopupMenu( QComboBoxPopup * pm, bool isPopup=TRUE ); + + int current; + int maxCount; + int sizeLimit; + QComboBox::Policy p; + bool autoresize; + bool poppedUp; + bool mouseWasInsidePopup; + bool arrowPressed; + bool arrowDown; + bool discardNextMousePress; + bool shortClick; + bool useCompletion; + bool completeNow; + int completeAt; + bool duplicatesEnabled; + int fullHeight, currHeight; + + QLineEdit * ed; // /bin/ed rules! + QTimer *completionTimer; + + QSize sizeHint; + +private: + bool usingLBox; + QComboBoxPopup *pop; + QListBox *lBox; + QComboBox *combo; + +}; + +void QComboBoxData::updateLinedGeometry() +{ + if ( !ed || !combo ) + return; + QRect r = QStyle::visualRect( combo->style().querySubControlMetrics(QStyle::CC_ComboBox, combo, + QStyle::SC_ComboBoxEditField), combo ); + + const QPixmap *pix = current < combo->count() ? combo->pixmap( current ) : 0; + if ( pix && pix->width() < r.width() ) + r.setLeft( r.left() + pix->width() + 4 ); + if ( r != ed->geometry() ) + ed->setGeometry( r ); +} + +void QComboBoxData::setListBox( QListBox *l ) +{ + lBox = l; + usingLBox = TRUE; + l->setMouseTracking( TRUE ); +#ifdef Q_WS_X11 + l->x11SetWindowType( QWidget::X11WindowTypeCombo ); + l->x11SetWindowTransient( combo->topLevelWidget()); +#endif +} + +void QComboBoxData::setPopupMenu( QComboBoxPopup * pm, bool isPopup ) +{ + pop = pm; + if(isPopup) + usingLBox = FALSE; +#ifdef Q_WS_X11 + if( pm ) { + pm->x11SetWindowType( QWidget::X11WindowTypeCombo ); + pm->x11SetWindowTransient( combo->topLevelWidget()); + } +#endif +} + +static inline bool checkInsertIndex( const char *method, const char * name, + int count, int *index) +{ + bool range_err = (*index > count); +#if defined(QT_CHECK_RANGE) + if ( range_err ) + qWarning( "QComboBox::%s: (%s) Index %d out of range", + method, name ? name : "<no name>", *index ); +#else + Q_UNUSED( method ) + Q_UNUSED( name ) +#endif + if ( *index < 0 ) // append + *index = count; + return !range_err; +} + + +static inline bool checkIndex( const char *method, const char * name, + int count, int index ) +{ + bool range_err = (index >= count); +#if defined(QT_CHECK_RANGE) + if ( range_err ) + qWarning( "QComboBox::%s: (%s) Index %i out of range", + method, name ? name : "<no name>", index ); +#else + Q_UNUSED( method ) + Q_UNUSED( name ) +#endif + return !range_err; +} + + + +/*! + Constructs a combobox widget with parent \a parent called \a name. + + This constructor creates a popup list if the program uses Motif + (or Aqua) look and feel; this is compatible with Motif 1.x and + Aqua. + + Note: If you use this constructor to create your QComboBox, then + the pixmap() function will always return 0. To workaround this, + use the other constructor. + +*/ + + + +QComboBox::QComboBox( QWidget *parent, const char *name ) + : QWidget( parent, name, WNoAutoErase ) +{ + d = new QComboBoxData( this ); + if ( style().styleHint(QStyle::SH_ComboBox_Popup, this) || + style().styleHint(QStyle::SH_GUIStyle) == Qt::MotifStyle ) { + d->setPopupMenu( new QComboBoxPopup( this, "in-combo" ) ); + d->popup()->setFont( font() ); + connect( d->popup(), SIGNAL(activated(int)), + SLOT(internalActivate(int)) ); + connect( d->popup(), SIGNAL(highlighted(int)), + SLOT(internalHighlight(int)) ); + } else { + setUpListBox(); + } + d->ed = 0; + d->current = 0; + d->maxCount = INT_MAX; + d->sizeLimit = 10; + d->p = AtBottom; + d->autoresize = FALSE; + d->poppedUp = FALSE; + d->arrowDown = FALSE; + d->arrowPressed = FALSE; + d->discardNextMousePress = FALSE; + d->shortClick = FALSE; + d->useCompletion = FALSE; + d->completeAt = 0; + d->completeNow = FALSE; + d->completionTimer = new QTimer( this ); + + setFocusPolicy( TabFocus ); + setBackgroundMode( PaletteButton ); +} + + +/*! + Constructs a combobox with a maximum size and either Motif 2.0 or + Windows look and feel. + + The input field can be edited if \a rw is TRUE, otherwise the user + may only choose one of the items in the combobox. + + The \a parent and \a name arguments are passed on to the QWidget + constructor. +*/ + + +QComboBox::QComboBox( bool rw, QWidget *parent, const char *name ) + : QWidget( parent, name, WNoAutoErase ) +{ + d = new QComboBoxData( this ); + setUpListBox(); + + if(d->popup() && style().styleHint(QStyle::SH_ComboBox_Popup, this)) + d->popup()->setItemChecked(d->current, FALSE); + d->current = 0; + d->maxCount = INT_MAX; + setSizeLimit(10); + d->p = AtBottom; + d->autoresize = FALSE; + d->poppedUp = FALSE; + d->arrowDown = FALSE; + d->discardNextMousePress = FALSE; + d->shortClick = FALSE; + d->useCompletion = FALSE; + d->completeAt = 0; + d->completeNow = FALSE; + d->completionTimer = new QTimer( this ); + + setFocusPolicy( StrongFocus ); + + d->ed = 0; + if ( rw ) + setUpLineEdit(); + setBackgroundMode( PaletteButton, PaletteBase ); +} + + + +/*! + Destroys the combobox. +*/ + +QComboBox::~QComboBox() +{ + delete d; +} + +void QComboBox::setDuplicatesEnabled( bool enable ) +{ + d->duplicatesEnabled = enable; +} + +bool QComboBox::duplicatesEnabled() const +{ + return d->duplicatesEnabled; +} + +int QComboBox::count() const +{ + if ( d->usingListBox() ) + return d->listBox()->count(); + else + return d->popup()->count(); +} + + +/*! + \overload + + Inserts the \a list of strings at position \a index in the + combobox. + + This is only for compatibility since it does not support Unicode + strings. See insertStringList(). +*/ + +void QComboBox::insertStrList( const QStrList &list, int index ) +{ + insertStrList( &list, index ); +} + +/*! + \overload + + Inserts the \a list of strings at position \a index in the + combobox. + + This is only for compatibility since it does not support Unicode + strings. See insertStringList(). +*/ + +void QComboBox::insertStrList( const QStrList *list, int index ) +{ + if ( !list ) { +#if defined(QT_CHECK_NULL) + Q_ASSERT( list != 0 ); +#endif + return; + } + QStrListIterator it( *list ); + const char* tmp; + if ( index < 0 ) + index = count(); + while ( (tmp=it.current()) ) { + ++it; + if ( d->usingListBox() ) + d->listBox()->insertItem( QString::fromLatin1(tmp), index ); + else + d->popup()->insertItem( escapedComboString(QString::fromLatin1(tmp)), index, index ); + if ( index++ == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + currentChanged(); + } + } + if ( index != count() ) + reIndex(); +} + +/*! + Inserts the \a list of strings at position \a index in the + combobox. +*/ + +void QComboBox::insertStringList( const QStringList &list, int index ) +{ + QStringList::ConstIterator it = list.begin(); + if ( index < 0 ) + index = count(); + while ( it != list.end() ) { + if ( d->usingListBox() ) + d->listBox()->insertItem( *it, index ); + else + d->popup()->insertItem( escapedComboString(*it), index, index ); + if ( index++ == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + currentChanged(); + } + ++it; + } + if ( index != count() ) + reIndex(); +} + +/*! + Inserts the array of char * \a strings at position \a index in the + combobox. + + The \a numStrings argument is the number of strings. If \a + numStrings is -1 (default), the \a strings array must be + terminated with 0. + + Example: + \code + static const char* items[] = { "red", "green", "blue", 0 }; + combo->insertStrList( items ); + \endcode + + \sa insertStringList() +*/ + +void QComboBox::insertStrList( const char **strings, int numStrings, int index) +{ + if ( !strings ) { +#if defined(QT_CHECK_NULL) + Q_ASSERT( strings != 0 ); +#endif + return; + } + if ( index < 0 ) + index = count(); + int i = 0; + while ( (numStrings<0 && strings[i]!=0) || i<numStrings ) { + if ( d->usingListBox() ) + d->listBox()->insertItem( QString::fromLatin1(strings[i]), index ); + else + d->popup()->insertItem( escapedComboString(QString::fromLatin1(strings[i])), index, index ); + i++; + if ( index++ == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + currentChanged(); + } + } + if ( index != count() ) + reIndex(); +} + + +/*! + Inserts a text item with text \a t, at position \a index. The item + will be appended if \a index is negative. +*/ + +void QComboBox::insertItem( const QString &t, int index ) +{ + int cnt = count(); + if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->insertItem( t, index ); + else + d->popup()->insertItem( escapedComboString(t), index, index ); + if ( index != cnt ) + reIndex(); + if ( index == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + } + if ( index == d->current ) + currentChanged(); +} + +/*! + \overload + + Inserts a \a pixmap item at position \a index. The item will be + appended if \a index is negative. +*/ + +void QComboBox::insertItem( const QPixmap &pixmap, int index ) +{ + int cnt = count(); + if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->insertItem( pixmap, index ); + else + d->popup()->insertItem( pixmap, index, index ); + if ( index != cnt ) + reIndex(); + if ( index == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + } + if ( index == d->current ) + currentChanged(); +} + +/*! + \overload + + Inserts a \a pixmap item with additional text \a text at position + \a index. The item will be appended if \a index is negative. +*/ + +void QComboBox::insertItem( const QPixmap &pixmap, const QString& text, int index ) +{ + int cnt = count(); + if ( !checkInsertIndex( "insertItem", name(), cnt, &index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->insertItem( pixmap, text, index ); + else + d->popup()->insertItem( pixmap, escapedComboString(text), index, index ); + if ( index != cnt ) + reIndex(); + if ( index == d->current && d->current < count() ) { + if ( d->ed ) { + d->ed->setText( this->text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + } + if ( index == d->current ) + currentChanged(); +} + + +/*! + Removes the item at position \a index. +*/ + +void QComboBox::removeItem( int index ) +{ + int cnt = count(); + if ( !checkIndex( "removeItem", name(), cnt, index ) ) + return; + if ( d->usingListBox() ) { + if ( style().styleHint(QStyle::SH_ComboBox_Popup, this) && d->popup() ) + d->popup()->removeItemAt( index ); + d->listBox()->removeItem( index ); + } else { + d->popup()->removeItemAt( index ); + } + if ( index != cnt-1 ) + reIndex(); + if ( index == d->current ) { + if ( d->ed ) { + QString s = QString::fromLatin1(""); + if (d->current < cnt - 1) + s = text( d->current ); + d->ed->setText( s ); + d->updateLinedGeometry(); + } + else { + if ( d->usingListBox() ) { + d->current = d->listBox()->currentItem(); + } else { + if (d->current > count()-1 && d->current > 0) + d->current--; + } + update(); + } + currentChanged(); + } + else { + if ( !d->ed ) { + if (d->current < cnt - 1) + setCurrentItem( d->current ); + else + setCurrentItem( d->current - 1 ); + } + } + +} + + +/*! + Removes all combobox items. +*/ + +void QComboBox::clear() +{ + if ( d->usingListBox() ) { + if ( style().styleHint(QStyle::SH_ComboBox_Popup, this) && d->popup() ) + d->popup()->clear(); + d->listBox()->resize( 0, 0 ); + d->listBox()->clear(); + } else { + d->popup()->clear(); + } + + if(d->popup() && style().styleHint(QStyle::SH_ComboBox_Popup, this)) + d->popup()->setItemChecked(d->current, FALSE); + d->current = 0; + if ( d->ed ) { + d->ed->setText( QString::fromLatin1("") ); + d->updateLinedGeometry(); + } + currentChanged(); +} + + +QString QComboBox::currentText() const +{ + if ( d->ed ) + return d->ed->text(); + else if ( d->current < count() ) + return text( currentItem() ); + else + return QString::null; +} + +void QComboBox::setCurrentText( const QString& txt ) +{ + int i; + for ( i = 0; i < count(); i++) + if ( text( i ) == txt ) + break; + if ( i < count() ) + setCurrentItem( i ); + else if ( d->ed ) + d->ed->setText( txt ); + else + changeItem( txt, currentItem() ); +} + + +/*! + Returns the text item at position \a index, or QString::null if + the item is not a string. + + \sa currentText() +*/ + +QString QComboBox::text( int index ) const +{ + if ( !checkIndex( "text", name(), count(), index ) ) + return QString::null; + if ( d->usingListBox() ) { + return d->listBox()->text( index ); + } else { + QString retText = d->popup()->text(index); + retText.replace("&&", "&"); + return retText; + } +} + +/*! + Returns the pixmap item at position \a index, or 0 if the item is + not a pixmap. +*/ + +const QPixmap *QComboBox::pixmap( int index ) const +{ + if ( !checkIndex( "pixmap", name(), count(), index ) ) + return 0; + if ( d->usingListBox() ) + return d->listBox()->pixmap( index ); + else + return d->popup()->pixmap( index ); +} + +/*! + Replaces the item at position \a index with the text \a t. +*/ + +void QComboBox::changeItem( const QString &t, int index ) +{ + if ( !checkIndex( "changeItem", name(), count(), index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->changeItem( t, index ); + else + d->popup()->changeItem( t, index ); + if ( index == d->current ) { + if ( d->ed ) { + d->ed->setText( text( d->current ) ); + d->updateLinedGeometry(); + } else + update(); + } +} + +/*! + \overload + + Replaces the item at position \a index with the pixmap \a im, + unless the combobox is editable. + + \sa insertItem() +*/ + +void QComboBox::changeItem( const QPixmap &im, int index ) +{ + if ( !checkIndex( "changeItem", name(), count(), index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->changeItem( im, index ); + else + d->popup()->changeItem( im, index ); + if ( index == d->current ) + update(); +} + +/*! + \overload + + Replaces the item at position \a index with the pixmap \a im and + the text \a t. + + \sa insertItem() +*/ + +void QComboBox::changeItem( const QPixmap &im, const QString &t, int index ) +{ + if ( !checkIndex( "changeItem", name(), count(), index ) ) + return; + if ( d->usingListBox() ) + d->listBox()->changeItem( im, t, index ); + else + d->popup()->changeItem( im, t, index ); + if ( index == d->current ) + update(); +} + + +int QComboBox::currentItem() const +{ + return d->current; +} + +void QComboBox::setCurrentItem( int index ) +{ + if ( index == d->current && !d->ed ) { + return; + } + if ( !checkIndex( "setCurrentItem", name(), count(), index ) ) { + return; + } + + if ( d->usingListBox() && !( listBox()->item(index) && listBox()->item(index)->isSelectable() ) ) + return; + + if(d->popup() && style().styleHint(QStyle::SH_ComboBox_Popup, this)) + d->popup()->setItemChecked(d->current, FALSE); + d->current = index; + d->completeAt = 0; + if ( d->ed ) { + d->ed->setText( text( index ) ); + d->updateLinedGeometry(); + } + // ### We want to keep ListBox's currentItem in sync, even if NOT popuped... + if ( d->usingListBox() && d->listBox() ) { + d->listBox()->setCurrentItem( index ); + } else { + internalHighlight( index ); + // internalActivate( index ); ### this leads to weird behavior, as in 3.0.1 + } + + currentChanged(); +} + +bool QComboBox::autoResize() const +{ + return d->autoresize; +} + +void QComboBox::setAutoResize( bool enable ) +{ + if ( (bool)d->autoresize != enable ) { + d->autoresize = enable; + if ( enable ) + adjustSize(); + } +} + + +/*! + \reimp + + This implementation caches the size hint to avoid resizing when + the contents change dynamically. To invalidate the cached value + call setFont(). +*/ +QSize QComboBox::sizeHint() const +{ + if ( isVisible() && d->sizeHint.isValid() ) + return d->sizeHint; + + constPolish(); + int i, w; + QFontMetrics fm = fontMetrics(); + + int maxW = count() ? 18 : 7 * fm.width(QChar('x')) + 18; + int maxH = QMAX( fm.lineSpacing(), 14 ) + 2; + + if ( !d->usingListBox() ) { + w = d->popup()->sizeHint().width() - 2* d->popup()->frameWidth(); + if ( w > maxW ) + maxW = w; + } else { + for( i = 0; i < count(); i++ ) { + w = d->listBox()->item( i )->width( d->listBox() ); + if ( w > maxW ) + maxW = w; + } + } + + d->sizeHint = (style().sizeFromContents(QStyle::CT_ComboBox, this, + QSize(maxW, maxH)). + expandedTo(QApplication::globalStrut())); + + return d->sizeHint; +} + + +/*! + \internal + Receives activated signals from an internal popup list and emits + the activated() signal. +*/ + +void QComboBox::internalActivate( int index ) +{ + if ( d->current != index ) { + if ( !d->usingListBox() || listBox()->item( index )->isSelectable() ) { + if(d->popup() && style().styleHint(QStyle::SH_ComboBox_Popup, this)) + d->popup()->setItemChecked(d->current, FALSE); + d->current = index; + currentChanged(); + } + } + if ( d->usingListBox() ) + popDownListBox(); + else + d->popup()->removeEventFilter( this ); + d->poppedUp = FALSE; + + QString t( text( index ) ); + if ( d->ed ) { + d->ed->setText( t ); + d->updateLinedGeometry(); + } + emit activated( index ); + emit activated( t ); +} + +/*! + \internal + Receives highlighted signals from an internal popup list and emits + the highlighted() signal. +*/ + +void QComboBox::internalHighlight( int index ) +{ + emit highlighted( index ); + QString t = text( index ); + if ( !t.isNull() ) + emit highlighted( t ); +} + +/*! + \internal + Receives timeouts after a click. Used to decide if a Motif style + popup should stay up or not after a click. +*/ +void QComboBox::internalClickTimeout() +{ + d->shortClick = FALSE; +} + +/*! + Sets the palette for both the combobox button and the combobox + popup list to \a palette. +*/ + +void QComboBox::setPalette( const QPalette &palette ) +{ + QWidget::setPalette( palette ); + if ( d->listBox() ) + d->listBox()->setPalette( palette ); + if ( d->popup() ) + d->popup()->setPalette( palette ); +} + +/*! + Sets the font for both the combobox button and the combobox popup + list to \a font. +*/ + +void QComboBox::setFont( const QFont &font ) +{ + d->sizeHint = QSize(); // invalidate size hint + QWidget::setFont( font ); + if ( d->usingListBox() ) + d->listBox()->setFont( font ); + else + d->popup()->setFont( font ); + if (d->ed) + d->ed->setFont( font ); + if ( d->autoresize ) + adjustSize(); +} + + +/*!\reimp +*/ + +void QComboBox::resizeEvent( QResizeEvent * e ) +{ + if ( d->ed ) + d->updateLinedGeometry(); + if ( d->listBox() ) + d->listBox()->resize( width(), d->listBox()->height() ); + QWidget::resizeEvent( e ); +} + +/*!\reimp +*/ + +void QComboBox::paintEvent( QPaintEvent * ) +{ + QPainter p( this ); + const QColorGroup & g = colorGroup(); + p.setPen(g.text()); + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (hasFocus()) + flags |= QStyle::Style_HasFocus; + + if ( width() < 5 || height() < 5 ) { + qDrawShadePanel( &p, rect(), g, FALSE, 2, + &g.brush( QColorGroup::Button ) ); + return; + } + + bool reverse = QApplication::reverseLayout(); + if ( !d->usingListBox() && + style().styleHint(QStyle::SH_GUIStyle) == Qt::MotifStyle) { // motif 1.x style + int dist, buttonH, buttonW; + dist = 8; + buttonH = 7; + buttonW = 11; + int xPos; + int x0; + int w = width() - dist - buttonW - 1; + if ( reverse ) { + xPos = dist + 1; + x0 = xPos + 4; + } else { + xPos = w; + x0 = 4; + } + qDrawShadePanel( &p, rect(), g, FALSE, + style().pixelMetric(QStyle::PM_DefaultFrameWidth, this), + &g.brush( QColorGroup::Button ) ); + qDrawShadePanel( &p, xPos, (height() - buttonH)/2, + buttonW, buttonH, g, FALSE, + style().pixelMetric(QStyle::PM_DefaultFrameWidth, this) ); + QRect clip( x0, 2, w - 2 - 4 - 5, height() - 4 ); + QString str = d->popup()->text( this->d->current ); + if ( !str.isNull() ) { + p.drawText( clip, AlignCenter | SingleLine, str ); + } + + QPixmap *pix = d->popup()->pixmap( this->d->current ); + QIconSet *iconSet = d->popup()->iconSet( this->d->current ); + if (pix || iconSet) { + QPixmap pm = ( pix ? *pix : iconSet->pixmap() ); + p.setClipRect( clip ); + p.drawPixmap( 4, (height()-pm.height())/2, pm ); + p.setClipping( FALSE ); + } + + if ( hasFocus() ) + p.drawRect( xPos - 5, 4, width() - xPos + 1 , height() - 8 ); + } else if(!d->usingListBox()) { + style().drawComplexControl( QStyle::CC_ComboBox, &p, this, rect(), g, + flags, (uint)QStyle::SC_All, + (d->arrowDown ? + QStyle::SC_ComboBoxArrow : + QStyle::SC_None )); + + QRect re = style().querySubControlMetrics( QStyle::CC_ComboBox, this, + QStyle::SC_ComboBoxEditField ); + re = QStyle::visualRect(re, this); + p.setClipRect( re ); + + QString str = d->popup()->text( this->d->current ); + QPixmap *pix = d->popup()->pixmap( this->d->current ); + if ( !str.isNull() ) { + p.save(); + p.setFont(font()); + QFontMetrics fm(font()); + int x = re.x(), y = re.y() + fm.ascent(); + if( pix ) + x += pix->width() + 5; + p.drawText( x, y, str ); + p.restore(); + } + if ( pix ) { + p.fillRect( re.x(), re.y(), pix->width() + 4, re.height(), + colorGroup().brush( QColorGroup::Base ) ); + p.drawPixmap( re.x() + 2, re.y() + + ( re.height() - pix->height() ) / 2, *pix ); + } + } else { + style().drawComplexControl( QStyle::CC_ComboBox, &p, this, rect(), g, + flags, (uint)QStyle::SC_All, + (d->arrowDown ? + QStyle::SC_ComboBoxArrow : + QStyle::SC_None )); + + QRect re = style().querySubControlMetrics( QStyle::CC_ComboBox, this, + QStyle::SC_ComboBoxEditField ); + re = QStyle::visualRect(re, this); + p.setClipRect( re ); + + if ( !d->ed ) { + QListBoxItem * item = d->listBox()->item( d->current ); + if ( item ) { + int itemh = item->height( d->listBox() ); + p.translate( re.x(), re.y() + (re.height() - itemh)/2 ); + item->paint( &p ); + } + } else if ( d->listBox() && d->listBox()->item( d->current ) ) { + QListBoxItem * item = d->listBox()->item( d->current ); + const QPixmap *pix = item->pixmap(); + if ( pix ) { + p.fillRect( re.x(), re.y(), pix->width() + 4, re.height(), + colorGroup().brush( QColorGroup::Base ) ); + p.drawPixmap( re.x() + 2, re.y() + + ( re.height() - pix->height() ) / 2, *pix ); + } + } + p.setClipping( FALSE ); + } +} + + +/*!\reimp +*/ + +void QComboBox::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() != LeftButton ) + return; + if ( d->discardNextMousePress ) { + d->discardNextMousePress = FALSE; + return; + } + QRect arrowRect = style().querySubControlMetrics( QStyle::CC_ComboBox, this, + QStyle::SC_ComboBoxArrow); + arrowRect = QStyle::visualRect(arrowRect, this); + + // Correction for motif style, where arrow is smaller + // and thus has a rect that doesn't fit the button. + arrowRect.setHeight( QMAX( height() - (2 * arrowRect.y()), arrowRect.height() ) ); + + if ( count() && ( !editable() || arrowRect.contains( e->pos() ) ) ) { + d->arrowPressed = FALSE; + + if ( d->usingListBox() ) { + listBox()->blockSignals( TRUE ); + qApp->sendEvent( listBox(), e ); // trigger the listbox's autoscroll + listBox()->setCurrentItem(d->current); + listBox()->blockSignals( FALSE ); + popup(); + if ( arrowRect.contains( e->pos() ) ) { + d->arrowPressed = TRUE; + d->arrowDown = TRUE; + repaint( FALSE ); + } + } else { + popup(); + } + QTimer::singleShot( 200, this, SLOT(internalClickTimeout())); + d->shortClick = TRUE; + } +} + +/*!\reimp +*/ + +void QComboBox::mouseMoveEvent( QMouseEvent * ) +{ +} + +/*!\reimp +*/ + +void QComboBox::mouseReleaseEvent( QMouseEvent * ) +{ +} + +/*!\reimp +*/ + +void QComboBox::mouseDoubleClickEvent( QMouseEvent *e ) +{ + mousePressEvent( e ); +} + + +/*!\reimp +*/ + +void QComboBox::keyPressEvent( QKeyEvent *e ) +{ + int c = currentItem(); + if ( ( e->key() == Key_F4 && e->state() == 0 ) || + ( e->key() == Key_Down && (e->state() & AltButton) ) || + ( !d->ed && e->key() == Key_Space ) ) { + if ( count() ) { + if ( !d->usingListBox() ) + d->popup()->setActiveItem( this->d->current ); + popup(); + } + return; + } else if ( d->usingListBox() && e->key() == Key_Up ) { + if ( c > 0 ) + setCurrentItem( c-1 ); + } else if ( d->usingListBox() && e->key() == Key_Down ) { + if ( ++c < count() ) + setCurrentItem( c ); + } else if ( d->usingListBox() && e->key() == Key_Home && ( !d->ed || !d->ed->hasFocus() ) ) { + setCurrentItem( 0 ); + } else if ( d->usingListBox() && e->key() == Key_End && ( !d->ed || !d->ed->hasFocus() ) ) { + setCurrentItem( count()-1 ); + } else if ( !d->ed && e->ascii() >= 32 && !e->text().isEmpty() ) { + if ( !d->completionTimer->isActive() ) { + d->completeAt = 0; + c = completionIndex( e->text(), ++c ); + if ( c >= 0 ) { + setCurrentItem( c ); + d->completeAt = e->text().length(); + } + } else { + d->completionTimer->stop(); + QString ct = currentText().left( d->completeAt ) + e->text(); + c = completionIndex( ct, c ); + if ( c < 0 && d->completeAt > 0 ) { + c = completionIndex( e->text(), 0 ); + ct = e->text(); + } + d->completeAt = 0; + if ( c >= 0 ) { + setCurrentItem( c ); + d->completeAt = ct.length(); + } + } + d->completionTimer->start( 400, TRUE ); + } else { + e->ignore(); + return; + } + + c = currentItem(); + if ( count() && !text( c ).isNull() ) + emit activated( text( c ) ); + emit activated( c ); +} + + +/*!\reimp +*/ + +void QComboBox::focusInEvent( QFocusEvent * e ) +{ + QWidget::focusInEvent( e ); + d->completeNow = FALSE; + d->completeAt = 0; +} + +/*!\reimp +*/ + +void QComboBox::focusOutEvent( QFocusEvent * e ) +{ + QWidget::focusOutEvent( e ); + d->completeNow = FALSE; + d->completeAt = 0; +} + +/*!\reimp +*/ +#ifndef QT_NO_WHEELEVENT +void QComboBox::wheelEvent( QWheelEvent *e ) +{ + if ( d->poppedUp ) { + if ( d->usingListBox() ) { + QApplication::sendEvent( d->listBox(), e ); + } + } else { + if ( e->delta() > 0 ) { + int c = currentItem(); + if ( c > 0 ) { + setCurrentItem( c-1 ); + emit activated( currentItem() ); + emit activated( currentText() ); + } + } else { + int c = currentItem(); + if ( ++c < count() ) { + setCurrentItem( c ); + emit activated( currentItem() ); + emit activated( currentText() ); + } + } + e->accept(); + } +} +#endif + +/*! + \internal + Calculates the listbox height needed to contain all items, or as + many as the list box is supposed to contain. +*/ +static int listHeight( QListBox *l, int sl ) +{ + if ( l->count() > 0 ) + return QMIN( l->count(), (uint)sl) * l->item( 0 )->height(l); + else + return l->sizeHint().height(); +} + + +/*! + Pops up the combobox popup list. + + If the list is empty, no items appear. +*/ + +void QComboBox::popup() +{ + if ( !count() || d->poppedUp ) + return; + + if( !d->usingListBox() || style().styleHint(QStyle::SH_ComboBox_Popup, this) ) { + if(d->usingListBox()) { + if(!d->popup()) { + QComboBoxPopup *p = new QComboBoxPopup( this, "in-combo" ); + d->setPopupMenu( p, FALSE ); + p->setFont( font() ); + connect( p, SIGNAL(activated(int)), SLOT(internalActivate(int)) ); + connect( p, SIGNAL(highlighted(int)), SLOT(internalHighlight(int)) ); + } + d->popup()->clear(); + for(unsigned int i = 0; i < d->listBox()->count(); i++) { + QListBoxItem *item = d->listBox()->item(i); + if(item->rtti() == QListBoxText::RTTI) { + d->popup()->insertItem(escapedComboString(item->text()), i, i); + } else if(item->rtti() == QListBoxPixmap::RTTI) { + if(item->pixmap()) + d->popup()->insertItem(QIconSet(*item->pixmap()), escapedComboString(item->text()), i, i); + else + d->popup()->insertItem(escapedComboString(item->text()), i, i); + } else { + d->popup()->insertItem(new QComboBoxPopupItem(item), i, i); + } + } + } + d->popup()->installEventFilter( this ); + if(d->popup() && style().styleHint(QStyle::SH_ComboBox_Popup, this)) + d->popup()->setItemChecked(this->d->current, TRUE); + d->popup()->popup( mapToGlobal( QPoint(0,0) ), this->d->current ); + update(); + } else { + // Send all listbox events to eventFilter(): + QListBox* lb = d->listBox(); + lb->triggerUpdate( TRUE ); + lb->installEventFilter( this ); + d->mouseWasInsidePopup = FALSE; + int w = lb->variableWidth() ? lb->sizeHint().width() : width(); + int h = listHeight( lb, d->sizeLimit ) + 2; + QRect screen = QApplication::desktop()->availableGeometry( this ); + + int sx = screen.x(); // screen pos + int sy = screen.y(); + int sw = screen.width(); // screen width + int sh = screen.height(); // screen height + QPoint pos = mapToGlobal( QPoint(0,height()) ); + // ## Similar code is in QPopupMenu + int x = pos.x(); + int y = pos.y(); + + // the complete widget must be visible + if ( x + w > sx + sw ) + x = sx+sw - w; + if ( x < sx ) + x = sx; + if (y + h > sy+sh && y - h - height() >= 0 ) + y = y - h - height(); + + QRect rect = + style().querySubControlMetrics( QStyle::CC_ComboBox, this, + QStyle::SC_ComboBoxListBoxPopup, + QStyleOption( x, y, w, h ) ); + // work around older styles that don't implement the combobox + // listbox popup subcontrol + if ( rect.isNull() ) + rect.setRect( x, y, w, h ); + lb->setGeometry( rect ); + + lb->raise(); + bool block = lb->signalsBlocked(); + lb->blockSignals( TRUE ); + QListBoxItem* currentLBItem = 0; + if ( editable() && currentText() != text( currentItem() ) ) + currentLBItem = lb->findItem( currentText() ); + + currentLBItem = currentLBItem ? currentLBItem : lb->item( d->current ); + + lb->setCurrentItem( currentLBItem ); + lb->setContentsPos( lb->contentsX(), + lb->viewportToContents( lb->itemRect( currentLBItem ).topLeft() ).y() ); + + // set the current item to also be the selected item if it isn't already + if ( currentLBItem && currentLBItem->isSelectable() && !currentLBItem->isSelected() ) + lb->setSelected( currentLBItem, TRUE ); + lb->blockSignals( block ); + lb->setVScrollBarMode(QScrollView::Auto); + +#ifndef QT_NO_EFFECTS + if ( QApplication::isEffectEnabled( UI_AnimateCombo ) ) { + if ( lb->y() < mapToGlobal(QPoint(0,0)).y() ) + qScrollEffect( lb, QEffects::UpScroll ); + else + qScrollEffect( lb ); + } else +#endif + lb->show(); + } + d->poppedUp = TRUE; +} + + +/*! + \reimp +*/ +void QComboBox::updateMask() +{ + QBitmap bm( size() ); + bm.fill( color0 ); + + { + QPainter p( &bm, this ); + style().drawComplexControlMask(QStyle::CC_ComboBox, &p, this, rect()); + } + + setMask( bm ); +} + +/*! + \internal + Pops down (removes) the combobox popup list box. +*/ +void QComboBox::popDownListBox() +{ + Q_ASSERT( d->usingListBox() ); + d->listBox()->removeEventFilter( this ); + d->listBox()->viewport()->removeEventFilter( this ); + d->listBox()->hide(); + d->listBox()->setCurrentItem( d->current ); + if ( d->arrowDown ) { + d->arrowDown = FALSE; + repaint( FALSE ); + } + d->poppedUp = FALSE; +} + + +/*! + \internal + Re-indexes the identifiers in the popup list. +*/ + +void QComboBox::reIndex() +{ + if ( !d->usingListBox() ) { + int cnt = count(); + while ( cnt-- ) + d->popup()->setId( cnt, cnt ); + } +} + +/*! + \internal + Repaints the combobox. +*/ + +void QComboBox::currentChanged() +{ + if ( d->autoresize ) + adjustSize(); + update(); + +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged ); +#endif +} + +/*! \reimp + + \internal + + The event filter steals events from the popup or listbox when they + are popped up. It makes the popup stay up after a short click in + motif style. In windows style it toggles the arrow button of the + combobox field, and activates an item and takes down the listbox + when the mouse button is released. +*/ + +bool QComboBox::eventFilter( QObject *object, QEvent *event ) +{ + if ( !event ) + return TRUE; + else if ( object == d->ed ) { + if ( event->type() == QEvent::KeyPress ) { + bool isAccepted = ( (QKeyEvent*)event )->isAccepted(); + keyPressEvent( (QKeyEvent *)event ); + if ( ((QKeyEvent *)event)->isAccepted() ) { + d->completeNow = FALSE; + return TRUE; + } else if ( ((QKeyEvent *)event)->key() != Key_End ) { + d->completeNow = TRUE; + d->completeAt = d->ed->cursorPosition(); + } + if ( isAccepted ) + ( (QKeyEvent*)event )->accept(); + else + ( (QKeyEvent*)event )->ignore(); + } else if ( event->type() == QEvent::KeyRelease ) { + keyReleaseEvent( (QKeyEvent *)event ); + return ((QKeyEvent *)event)->isAccepted(); + } else if ( event->type() == QEvent::FocusIn ) { + focusInEvent( (QFocusEvent *)event ); + } else if ( event->type() == QEvent::FocusOut ) { + focusOutEvent( (QFocusEvent *)event ); + } else if ( d->useCompletion && d->completeNow ) { + d->completeNow = FALSE; + if ( !d->ed->text().isNull() && + d->ed->cursorPosition() > d->completeAt && + d->ed->cursorPosition() == (int)d->ed->text().length() ) { + QString ct( d->ed->text() ); + int i = completionIndex( ct, currentItem() ); + if ( i > -1 ) { + QString it = text( i ); + d->ed->validateAndSet( it, ct.length(), + ct.length(), it.length() ); + d->current = i; + // ### sets current item without emitting signals. This is to + // make sure the right item is current if you change current with + // wheel/up/down. While typing current is not valid anyway. Fix properly + // in 4.0. + } + } + } + } else if ( d->usingListBox() && ( object == d->listBox() || + object == d->listBox()->viewport() )) { + QMouseEvent *e = (QMouseEvent*)event; + switch( event->type() ) { + case QEvent::MouseMove: + if ( !d->mouseWasInsidePopup ) { + QPoint pos = e->pos(); + if ( d->listBox()->rect().contains( pos ) ) + d->mouseWasInsidePopup = TRUE; + // Check if arrow button should toggle + if ( d->arrowPressed ) { + QPoint comboPos; + comboPos = mapFromGlobal( d->listBox()->mapToGlobal(pos) ); + QRect arrowRect = + style().querySubControlMetrics( QStyle::CC_ComboBox, this, + QStyle::SC_ComboBoxArrow); + arrowRect = QStyle::visualRect(arrowRect, this); + if ( arrowRect.contains( comboPos ) ) { + if ( !d->arrowDown ) { + d->arrowDown = TRUE; + repaint( FALSE ); + } + } else { + if ( d->arrowDown ) { + d->arrowDown = FALSE; + repaint( FALSE ); + } + } + } + } else if ((e->state() & ( RightButton | LeftButton | MidButton ) ) == 0 && + style().styleHint(QStyle::SH_ComboBox_ListMouseTracking, this)) { + QWidget *mouseW = QApplication::widgetAt( e->globalPos(), TRUE ); + if ( mouseW == d->listBox()->viewport() ) { //### + QMouseEvent m( QEvent::MouseMove, e->pos(), e->globalPos(), + LeftButton, LeftButton ); + QApplication::sendEvent( object, &m ); //### Evil + return TRUE; + } + } + + break; + case QEvent::MouseButtonRelease: + if ( d->listBox()->rect().contains( e->pos() ) ) { + QMouseEvent tmp( QEvent::MouseButtonDblClick, + e->pos(), e->button(), e->state() ) ; + // will hide popup + QApplication::sendEvent( object, &tmp ); + return TRUE; + } else { + if ( d->mouseWasInsidePopup ) { + popDownListBox(); + } else { + d->arrowPressed = FALSE; + if ( d->arrowDown ) { + d->arrowDown = FALSE; + repaint( FALSE ); + } + } + } + break; + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + if ( !d->listBox()->rect().contains( e->pos() ) ) { + QPoint globalPos = d->listBox()->mapToGlobal(e->pos()); + if ( QApplication::widgetAt( globalPos, TRUE ) == this ) { + d->discardNextMousePress = TRUE; + // avoid popping up again + } + popDownListBox(); + return TRUE; + } + break; + case QEvent::KeyPress: + switch( ((QKeyEvent *)event)->key() ) { + case Key_Up: + case Key_Down: + if ( !(((QKeyEvent *)event)->state() & AltButton) ) + break; + case Key_F4: + case Key_Escape: + if ( d->poppedUp ) { + popDownListBox(); + return TRUE; + } + break; + case Key_Enter: + case Key_Return: + // work around QDialog's enter handling + return FALSE; + default: + break; + } + break; + case QEvent::Hide: + popDownListBox(); + break; + default: + break; + } + } else if ( (!d->usingListBox() || style().styleHint(QStyle::SH_ComboBox_Popup, this)) && + object == d->popup() ) { + QMouseEvent *e = (QMouseEvent*)event; + switch ( event->type() ) { + case QEvent::MouseButtonRelease: + if ( d->shortClick ) { + QMouseEvent tmp( QEvent::MouseMove, + e->pos(), e->button(), e->state() ) ; + // highlight item, but don't pop down: + QApplication::sendEvent( object, &tmp ); + return TRUE; + } + break; + case QEvent::MouseButtonDblClick: + case QEvent::MouseButtonPress: + if ( !d->popup()->rect().contains( e->pos() ) ) { + d->poppedUp = FALSE; + d->arrowDown = FALSE; + // remove filter, event will take down popup: + d->popup()->removeEventFilter( this ); + // ### uglehack! + // call internalHighlight so the highlighed signal + // will be emitted at least as often as necessary. + // it may be called more often than necessary + internalHighlight( d->current ); + } + break; + case QEvent::Hide: + d->poppedUp = FALSE; + break; + default: + break; + } + } + return QWidget::eventFilter( object, event ); +} + + +/*! + Returns the index of the first item \e after \a startingAt of + which \a prefix is a case-insensitive prefix. Returns -1 if no + items start with \a prefix. +*/ + +int QComboBox::completionIndex( const QString & prefix, + int startingAt = 0 ) const +{ + int start = startingAt; + if ( start < 0 || start >= count() ) + start = 0; + if ( start >= count() ) + return -1; + QString match = prefix.lower(); + if ( match.length() < 1 ) + return start; + + QString current; + int i = start; + do { + current = text( i ).lower(); + if ( current.startsWith( match ) ) + return i; + i++; + if ( i == count() ) + i = 0; + } while ( i != start ); + return -1; +} + + +int QComboBox::sizeLimit() const +{ + return d ? d->sizeLimit : INT_MAX; +} + +void QComboBox::setSizeLimit( int lines ) +{ + d->sizeLimit = lines; +} + + +int QComboBox::maxCount() const +{ + return d ? d->maxCount : INT_MAX; +} + +void QComboBox::setMaxCount( int count ) +{ + int l = this->count(); + while( --l > count ) + removeItem( l ); + d->maxCount = count; +} + +QComboBox::Policy QComboBox::insertionPolicy() const +{ + return d->p; +} + +void QComboBox::setInsertionPolicy( Policy policy ) +{ + d->p = policy; +} + + + +/*! + Internal slot to keep the line editor up to date. +*/ + +void QComboBox::returnPressed() +{ + QString s( d->ed->text() ); + + if ( s.isEmpty() ) + return; + + int c = 0; + bool doInsert = TRUE; + if ( !d->duplicatesEnabled ) { + for ( int i = 0; i < count(); ++i ) { + if ( s == text( i ) ) { + doInsert = FALSE; + c = i; + break; + } + } + } + + if ( doInsert ) { + if ( insertionPolicy() != NoInsertion ) { + int cnt = count(); + while ( cnt >= d->maxCount ) { + removeItem( --cnt ); + } + } + + switch ( insertionPolicy() ) { + case AtCurrent: + if (count() == 0) + insertItem(s); + else if ( s != text( currentItem() ) ) + changeItem( s, currentItem() ); + emit activated( currentItem() ); + emit activated( s ); + return; + case NoInsertion: + emit activated( s ); + return; + case AtTop: + c = 0; + break; + case AtBottom: + c = count(); + break; + case BeforeCurrent: + c = currentItem(); + break; + case AfterCurrent: + c = count() == 0 ? 0 : currentItem() + 1; + break; + } + insertItem( s, c ); + } + + setCurrentItem( c ); + emit activated( c ); + emit activated( s ); +} + + +/*! \reimp +*/ + +void QComboBox::setEnabled( bool enable ) +{ + if ( !enable ) { + if ( d->usingListBox() ) { + popDownListBox(); + } else { + d->popup()->removeEventFilter( this ); + d->popup()->close(); + d->poppedUp = FALSE; + } + } + QWidget::setEnabled( enable ); +} + + + +/*! + Applies the validator \a v to the combobox so that only text which + is valid according to \a v is accepted. + + This function does nothing if the combobox is not editable. + + \sa validator() clearValidator() QValidator +*/ + +void QComboBox::setValidator( const QValidator * v ) +{ + if ( d && d->ed ) + d->ed->setValidator( v ); +} + + +/*! + Returns the validator which constrains editing for this combobox + if there is one; otherwise returns 0. + + \sa setValidator() clearValidator() QValidator +*/ + +const QValidator * QComboBox::validator() const +{ + return d && d->ed ? d->ed->validator() : 0; +} + + +/*! + This slot is equivalent to setValidator( 0 ). +*/ + +void QComboBox::clearValidator() +{ + if ( d && d->ed ) + d->ed->setValidator( 0 ); +} + + +/*! + Sets the combobox to use \a newListBox instead of the current list + box or popup. As a side effect, it clears the combobox of its + current contents. + + \warning QComboBox assumes that newListBox->text(n) returns + non-null for 0 \<= n \< newListbox->count(). This assumption is + necessary because of the line edit in QComboBox. +*/ + +void QComboBox::setListBox( QListBox * newListBox ) +{ + clear(); + + if ( d->usingListBox() ) { + delete d->listBox(); + } else { + delete d->popup(); + d->setPopupMenu(0, FALSE); + } + + newListBox->reparent( this, WType_Popup, QPoint(0,0), FALSE ); + d->setListBox( newListBox ); + d->listBox()->setFont( font() ); + d->listBox()->setPalette( palette() ); + d->listBox()->setVScrollBarMode(QScrollView::AlwaysOff); + d->listBox()->setHScrollBarMode(QScrollView::AlwaysOff); + d->listBox()->setFrameStyle( QFrame::Box | QFrame::Plain ); + d->listBox()->setLineWidth( 1 ); + d->listBox()->resize( 100, 10 ); + + connect( d->listBox(), SIGNAL(selected(int)), + SLOT(internalActivate(int)) ); + connect( d->listBox(), SIGNAL(highlighted(int)), + SLOT(internalHighlight(int))); +} + + +/*! + Returns the current list box, or 0 if there is no list box. + (QComboBox can use QPopupMenu instead of QListBox.) Provided to + match setListBox(). + + \sa setListBox() +*/ + +QListBox * QComboBox::listBox() const +{ + return d && d->usingListBox() ? d->listBox() : 0; +} + +/*! + Returns the line edit, or 0 if there is no line edit. + + Only editable listboxes have a line editor. +*/ +QLineEdit* QComboBox::lineEdit() const +{ + return d->ed; +} + + + +/*! + Clears the line edit without changing the combobox's contents. + Does nothing if the combobox isn't editable. + + This is particularly useful when using a combobox as a line edit + with history. For example you can connect the combobox's + activated() signal to clearEdit() in order to present the user + with a new, empty line as soon as Enter is pressed. + + \sa setEditText() +*/ + +void QComboBox::clearEdit() +{ + if ( d && d->ed ) + d->ed->clear(); +} + + +/*! + Sets the text in the line edit to \a newText without changing the + combobox's contents. Does nothing if the combobox isn't editable. + + This is useful e.g. for providing a good starting point for the + user's editing and entering the change in the combobox only when + the user presses Enter. + + \sa clearEdit() insertItem() +*/ + +void QComboBox::setEditText( const QString &newText ) +{ + if ( d && d->ed ) { + d->updateLinedGeometry(); + d->ed->setText( newText ); + } +} + +void QComboBox::setAutoCompletion( bool enable ) +{ + d->useCompletion = enable; + d->completeNow = FALSE; +} + + +bool QComboBox::autoCompletion() const +{ + return d->useCompletion; +} + +/*!\reimp + */ +void QComboBox::styleChange( QStyle& s ) +{ + d->sizeHint = QSize(); // invalidate size hint... + if ( d->ed ) + d->updateLinedGeometry(); + QWidget::styleChange( s ); +} + +bool QComboBox::editable() const +{ + return d->ed != 0; +} + +void QComboBox::setEditable( bool y ) +{ + if ( y == editable() ) + return; + if ( y ) { + if ( !d->usingListBox() ) + setUpListBox(); + setUpLineEdit(); + d->ed->show(); + if ( currentItem() ) + setEditText( currentText() ); + } else { + delete d->ed; + d->ed = 0; + } + + setFocusPolicy( StrongFocus ); + updateGeometry(); + update(); +} + + +void QComboBox::setUpListBox() +{ + d->setListBox( new QListBox( this, "in-combo", WType_Popup ) ); + d->listBox()->setFont( font() ); + d->listBox()->setPalette( palette() ); + d->listBox()->setVScrollBarMode( QListBox::AlwaysOff ); + d->listBox()->setHScrollBarMode( QListBox::AlwaysOff ); + d->listBox()->setFrameStyle( QFrame::Box | QFrame::Plain ); + d->listBox()->setLineWidth( 1 ); + d->listBox()->resize( 100, 10 ); + + connect( d->listBox(), SIGNAL(selected(int)), + SLOT(internalActivate(int)) ); + connect( d->listBox(), SIGNAL(highlighted(int)), + SLOT(internalHighlight(int))); +} + + +void QComboBox::setUpLineEdit() +{ + if ( !d->ed ) + setLineEdit( new QLineEdit( this, "combo edit" ) ); +} + +/*! + Sets the line edit to use \a edit instead of the current line edit. +*/ + +void QComboBox::setLineEdit( QLineEdit *edit ) +{ + if ( !edit ) { +#if defined(QT_CHECK_NULL) + Q_ASSERT( edit != 0 ); +#endif + return; + } + + edit->setText( currentText() ); + delete d->ed; + d->ed = edit; + + if ( edit->parent() != this ) + edit->reparent( this, QPoint(0,0), FALSE ); + + connect (edit, SIGNAL( textChanged(const QString&) ), + this, SIGNAL( textChanged(const QString&) ) ); + connect( edit, SIGNAL(returnPressed()), SLOT(returnPressed()) ); + + edit->setFrame( FALSE ); + d->updateLinedGeometry(); + edit->installEventFilter( this ); + setFocusProxy( edit ); + setFocusPolicy( StrongFocus ); + setInputMethodEnabled( TRUE ); + + if ( !d->usingListBox() ) + setUpListBox(); + + if ( isVisible() ) + edit->show(); + + updateGeometry(); + update(); +} + +/*! + \reimp +*/ +void QComboBox::hide() +{ + QWidget::hide(); + + if (listBox()) + listBox()->hide(); + else if (d->popup()) + d->popup()->hide(); +} + +#endif // QT_NO_COMBOBOX diff --git a/src/widgets/qcombobox.h b/src/widgets/qcombobox.h new file mode 100644 index 0000000..dfc9dde --- /dev/null +++ b/src/widgets/qcombobox.h @@ -0,0 +1,206 @@ +/********************************************************************** +** +** Definition of QComboBox class +** +** Created : 950426 +** +** 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. +** +**********************************************************************/ + +#ifndef QCOMBOBOX_H +#define QCOMBOBOX_H + +#ifndef QT_H +#include "qwidget.h" +#endif // QT_H + +#ifndef QT_NO_COMBOBOX + + +class QStrList; +class QStringList; +class QLineEdit; +class QValidator; +class QListBox; +class QComboBoxData; +class QWheelEvent; + +class Q_EXPORT QComboBox : public QWidget +{ + Q_OBJECT + Q_ENUMS( Policy ) + Q_PROPERTY( bool editable READ editable WRITE setEditable ) + Q_PROPERTY( int count READ count ) + Q_PROPERTY( QString currentText READ currentText WRITE setCurrentText DESIGNABLE false ) + Q_PROPERTY( int currentItem READ currentItem WRITE setCurrentItem ) + Q_PROPERTY( bool autoResize READ autoResize WRITE setAutoResize DESIGNABLE false ) + Q_PROPERTY( int sizeLimit READ sizeLimit WRITE setSizeLimit ) + Q_PROPERTY( int maxCount READ maxCount WRITE setMaxCount ) + Q_PROPERTY( Policy insertionPolicy READ insertionPolicy WRITE setInsertionPolicy ) + Q_PROPERTY( bool autoCompletion READ autoCompletion WRITE setAutoCompletion ) + Q_PROPERTY( bool duplicatesEnabled READ duplicatesEnabled WRITE setDuplicatesEnabled ) + Q_OVERRIDE( bool autoMask DESIGNABLE true SCRIPTABLE true ) + +public: + QComboBox( QWidget* parent=0, const char* name=0 ); + QComboBox( bool rw, QWidget* parent=0, const char* name=0 ); + ~QComboBox(); + + int count() const; + + void insertStringList( const QStringList &, int index=-1 ); + void insertStrList( const QStrList &, int index=-1 ); + void insertStrList( const QStrList *, int index=-1 ); + void insertStrList( const char **, int numStrings=-1, int index=-1); + + void insertItem( const QString &text, int index=-1 ); + void insertItem( const QPixmap &pixmap, int index=-1 ); + void insertItem( const QPixmap &pixmap, const QString &text, int index=-1 ); + + void removeItem( int index ); + + int currentItem() const; + virtual void setCurrentItem( int index ); + + QString currentText() const; + virtual void setCurrentText( const QString& ); + + QString text( int index ) const; + const QPixmap *pixmap( int index ) const; + + void changeItem( const QString &text, int index ); + void changeItem( const QPixmap &pixmap, int index ); + void changeItem( const QPixmap &pixmap, const QString &text, int index ); + + bool autoResize() const; + virtual void setAutoResize( bool ); + QSize sizeHint() const; + + void setPalette( const QPalette & ); + void setFont( const QFont & ); + void setEnabled( bool ); + + virtual void setSizeLimit( int ); + int sizeLimit() const; + + virtual void setMaxCount( int ); + int maxCount() const; + + enum Policy { NoInsertion, AtTop, AtCurrent, AtBottom, + AfterCurrent, BeforeCurrent }; + + virtual void setInsertionPolicy( Policy policy ); + Policy insertionPolicy() const; + + virtual void setValidator( const QValidator * ); + const QValidator * validator() const; + + virtual void setListBox( QListBox * ); + QListBox * listBox() const; + + virtual void setLineEdit( QLineEdit *edit ); + QLineEdit* lineEdit() const; + + virtual void setAutoCompletion( bool ); + bool autoCompletion() const; + + bool eventFilter( QObject *object, QEvent *event ); + + void setDuplicatesEnabled( bool enable ); + bool duplicatesEnabled() const; + + bool editable() const; + void setEditable( bool ); + + virtual void popup(); + + void hide(); + +public slots: + void clear(); + void clearValidator(); + void clearEdit(); + virtual void setEditText( const QString &); + +signals: + void activated( int index ); + void highlighted( int index ); + void activated( const QString &); + void highlighted( const QString &); + void textChanged( const QString &); + +private slots: + void internalActivate( int ); + void internalHighlight( int ); + void internalClickTimeout(); + void returnPressed(); + +protected: + void paintEvent( QPaintEvent * ); + void resizeEvent( QResizeEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseDoubleClickEvent( QMouseEvent * ); + void keyPressEvent( QKeyEvent *e ); + void focusInEvent( QFocusEvent *e ); + void focusOutEvent( QFocusEvent *e ); +#ifndef QT_NO_WHEELEVENT + void wheelEvent( QWheelEvent *e ); +#endif + void styleChange( QStyle& ); + + void updateMask(); + +private: + void setUpListBox(); + void setUpLineEdit(); + void popDownListBox(); + void reIndex(); + void currentChanged(); + int completionIndex( const QString &, int ) const; + + QComboBoxData *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QComboBox( const QComboBox & ); + QComboBox &operator=( const QComboBox & ); +#endif +}; + + +#endif // QT_NO_COMBOBOX + +#endif // QCOMBOBOX_H diff --git a/src/widgets/qdatetimeedit.cpp b/src/widgets/qdatetimeedit.cpp new file mode 100644 index 0000000..6f6ca71 --- /dev/null +++ b/src/widgets/qdatetimeedit.cpp @@ -0,0 +1,2842 @@ +/**************************************************************************** +** +** Implementation of date and time edit classes +** +** Created : 001103 +** +** Copyright (C) 2000-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 "qdatetimeedit.h" + +#ifndef QT_NO_DATETIMEEDIT + +#include "../kernel/qinternal_p.h" +#include "../kernel/qrichtext_p.h" +#include "qrangecontrol.h" +#include "qapplication.h" +#include "qpixmap.h" +#include "qapplication.h" +#include "qvaluelist.h" +#include "qstring.h" +#include "qstyle.h" + +#if defined(Q_WS_WIN) +#include "qt_windows.h" +#endif + +#define QDATETIMEEDIT_HIDDEN_CHAR '0' + +class Q_EXPORT QNumberSection +{ +public: + QNumberSection( int selStart = 0, int selEnd = 0, bool separat = TRUE, int actual = -1 ) + : selstart( selStart ), selend( selEnd ), act( actual ), sep( separat ) + {} + int selectionStart() const { return selstart; } + void setSelectionStart( int s ) { selstart = s; } + int selectionEnd() const { return selend; } + void setSelectionEnd( int s ) { selend = s; } + int width() const { return selend - selstart; } + int index() const { return act; } + bool separator() const { return sep; } + Q_DUMMY_COMPARISON_OPERATOR( QNumberSection ) +private: + int selstart :12; + int selend :12; + int act :7; + bool sep :1; +}; + +static QString *lDateSep = 0; +static QString *lTimeSep = 0; +static bool lAMPM = FALSE; +static QString *lAM = 0; +static QString *lPM = 0; +static QDateEdit::Order lOrder = QDateEdit::YMD; +static int refcount = 0; + +static void cleanup() +{ + delete lDateSep; + lDateSep = 0; + delete lTimeSep; + lTimeSep = 0; + delete lAM; + lAM = 0; + delete lPM; + lPM = 0; +} + +/*! +\internal +try to get the order of DMY and the date/time separator from the locale settings +*/ +static void readLocaleSettings() +{ + int dpos, mpos, ypos; + cleanup(); + + lDateSep = new QString(); + lTimeSep = new QString(); + +#if defined(Q_WS_WIN) + QT_WA( { + TCHAR data[10]; + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_SDATE, data, 10 ); + *lDateSep = QString::fromUcs2( (ushort*)data ); + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_STIME, data, 10 ); + *lTimeSep = QString::fromUcs2( (ushort*)data ); + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_ITIME, data, 10 ); + lAMPM = QString::fromUcs2( (ushort*)data ).toInt()==0; + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_S1159, data, 10 ); + QString am = QString::fromUcs2( (ushort*)data ); + if ( !am.isEmpty() ) + lAM = new QString( am ); + GetLocaleInfo( LOCALE_USER_DEFAULT, LOCALE_S2359, data, 10 ); + QString pm = QString::fromUcs2( (ushort*)data ); + if ( !pm.isEmpty() ) + lPM = new QString( pm ); + } , { + char data[10]; + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_SDATE, (char*)&data, 10 ); + *lDateSep = QString::fromLocal8Bit( data ); + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_STIME, (char*)&data, 10 ); + *lTimeSep = QString::fromLocal8Bit( data ); + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_ITIME, (char*)&data, 10 ); + lAMPM = QString::fromLocal8Bit( data ).toInt()==0; + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_S1159, (char*)&data, 10 ); + QString am = QString::fromLocal8Bit( data ); + if ( !am.isEmpty() ) + lAM = new QString( am ); + GetLocaleInfoA( LOCALE_USER_DEFAULT, LOCALE_S2359, (char*)&data, 10 ); + QString pm = QString::fromLocal8Bit( data ); + if ( !pm.isEmpty() ) + lPM = new QString( pm ); + } ); +#else + *lDateSep = "-"; + *lTimeSep = ":"; +#endif + QString d = QDate( 1999, 11, 22 ).toString( Qt::LocalDate ); + dpos = d.find( "22" ); + mpos = d.find( "11" ); + ypos = d.find( "99" ); + if ( dpos > -1 && mpos > -1 && ypos > -1 ) { + // test for DMY, MDY, YMD, YDM + if ( dpos < mpos && mpos < ypos ) { + lOrder = QDateEdit::DMY; + } else if ( mpos < dpos && dpos < ypos ) { + lOrder = QDateEdit::MDY; + } else if ( ypos < mpos && mpos < dpos ) { + lOrder = QDateEdit::YMD; + } else if ( ypos < dpos && dpos < mpos ) { + lOrder = QDateEdit::YDM; + } else { + // cannot determine the dateformat - use the default + return; + } + + // this code needs to change if new formats are added + +#ifndef Q_WS_WIN + QString sep = d.mid( QMIN( dpos, mpos ) + 2, QABS( dpos - mpos ) - 2 ); + if ( d.contains( sep ) == 2 ) { + *lDateSep = sep; + } +#endif + } + +#ifndef Q_WS_WIN + QString t = QTime( 11, 22, 33 ).toString( Qt::LocalDate ); + dpos = t.find( "11" ); + mpos = t.find( "22" ); + ypos = t.find( "33" ); + // We only allow hhmmss + if ( dpos > -1 && dpos < mpos && mpos < ypos ) { + QString sep = t.mid( dpos + 2, mpos - dpos - 2 ); + if ( sep == t.mid( mpos + 2, ypos - mpos - 2 ) ) { + *lTimeSep = sep; + } + } +#endif +} + +static QDateEdit::Order localOrder() { + if ( !lDateSep ) { + readLocaleSettings(); + } + return lOrder; +} + +static QString localDateSep() { + if ( !lDateSep ) { + readLocaleSettings(); + } + return *lDateSep; +} + +static QString localTimeSep() { + if ( !lTimeSep ) { + readLocaleSettings(); + } + return *lTimeSep; +} + +class QDateTimeEditorPrivate +{ +public: + QDateTimeEditorPrivate() + : frm( TRUE ), + parag( new QTextParagraph( 0, 0, 0, FALSE ) ), + focusSec(0) + { + parag->formatter()->setWrapEnabled( FALSE ); + cursor = new QTextCursor( 0 ); + cursor->setParagraph( parag ); + offset = 0; + sep = localDateSep(); + refcount++; + } + ~QDateTimeEditorPrivate() + { + delete parag; + delete cursor; + if ( !--refcount ) + cleanup(); + } + + void appendSection( const QNumberSection& sec ) + { + sections.append( sec ); + + } + void clearSections() + { + sections.clear(); + } + void setSectionSelection( int sec, int selstart, int selend ) + { + if ( sec < 0 || sec > (int)sections.count() ) + return; + sections[sec].setSelectionStart( selstart ); + sections[sec].setSelectionEnd( selend ); + } + uint sectionCount() const { return (uint)sections.count(); } + void setSeparator( const QString& s ) { sep = s; } + QString separator() const { return sep; } + + void setFrame( bool f ) { frm = f; } + bool frame() const { return frm; } + + int focusSection() const { return focusSec; } + int section( const QPoint& p ) + { + cursor->place( p + QPoint( offset, 0 ), parag ); + int idx = cursor->index(); + for ( uint i = 0; i < sections.count(); ++i ) { + if ( idx >= sections[i].selectionStart() && + idx <= sections[i].selectionEnd() ) + return i; + } + return -1; + } + QNumberSection section( int idx ) const + { + return sections[idx]; + } + bool setFocusSection( int idx ) + { + if ( idx > (int)sections.count()-1 || idx < 0 ) + return FALSE; + if ( idx != focusSec ) { + focusSec = idx; + applyFocusSelection(); + return TRUE; + } + return FALSE; + } + + bool inSectionSelection( int idx ) + { + for ( uint i = 0; i < sections.count(); ++i ) { + if ( idx >= sections[i].selectionStart() && + idx <= sections[i].selectionEnd() ) + return TRUE; + } + return FALSE; + } + + void paint( const QString& txt, bool focus, QPainter& p, + const QColorGroup& cg, const QRect& rect, QStyle& style ) + { + int fw = 0; + if ( frm ) + fw = style.pixelMetric(QStyle::PM_DefaultFrameWidth); + + parag->truncate( 0 ); + parag->append( txt ); + if ( !focus ) + parag->removeSelection( QTextDocument::Standard ); + else { + applyFocusSelection(); + } + + /* color all QDATETIMEEDIT_HIDDEN_CHAR chars to background color */ + QTextFormat *fb = parag->formatCollection()->format( p.font(), + cg.base() ); + QTextFormat *nf = parag->formatCollection()->format( p.font(), + cg.text() ); + for ( uint i = 0; i < txt.length(); ++i ) { + parag->setFormat( i, 1, nf ); + if ( inSectionSelection( i ) ) + continue; + if ( txt.at(i) == QDATETIMEEDIT_HIDDEN_CHAR ) + parag->setFormat( i, 1, fb ); + else + parag->setFormat( i, 1, nf ); + } + fb->removeRef(); + nf->removeRef(); + + QRect r( rect.x(), rect.y(), rect.width() - 2 * ( 2 + fw ), rect.height() ); + parag->pseudoDocument()->docRect = r; + parag->invalidate(0); + parag->format(); + + int xoff = 2 + fw - offset; + int yoff = ( rect.height() - parag->rect().height() + 1 ) / 2; + if ( yoff < 0 ) + yoff = 0; + + p.translate( xoff, yoff ); + parag->paint( p, cg, 0, TRUE ); + if ( frm ) + p.translate( -xoff, -yoff ); + } + + void resize( const QSize& size ) { sz = size; } + + int mapSection( int sec ) + { + return sections[sec].index(); + } + +protected: + void applyFocusSelection() + { + if ( focusSec > -1 ) { + int selstart = sections[ focusSec ].selectionStart(); + int selend = sections[ focusSec ].selectionEnd(); + parag->setSelection( QTextDocument::Standard, selstart, selend ); + parag->format(); + if ( parag->at( selstart )->x < offset || + parag->at( selend )->x + parag->string()->width( selend ) > offset + sz.width() ) { + offset = parag->at( selstart )->x; + } + } + } +private: + bool frm; + QTextParagraph *parag; + QTextCursor *cursor; + QSize sz; + int focusSec; + QValueList< QNumberSection > sections; + QString sep; + int offset; +}; + +class QDateTimeEditor : public QWidget +{ + Q_OBJECT +public: + QDateTimeEditor( QDateTimeEditBase * widget, QWidget *parent, + const char * name=0 ); + ~QDateTimeEditor(); + + void setControlWidget( QDateTimeEditBase * widget ); + QDateTimeEditBase * controlWidget() const; + + void setSeparator( const QString& s ); + QString separator() const; + + int focusSection() const; + bool setFocusSection( int s ); + void appendSection( const QNumberSection& sec ); + void clearSections(); + void setSectionSelection( int sec, int selstart, int selend ); + bool eventFilter( QObject *o, QEvent *e ); + int sectionAt( const QPoint &p ); + int mapSection( int sec ); + +protected: + void init(); + bool event( QEvent *e ); + void resizeEvent( QResizeEvent * ); + void paintEvent( QPaintEvent * ); + void mousePressEvent( QMouseEvent *e ); + +private: + QDateTimeEditBase* cw; + QDateTimeEditorPrivate* d; +}; + +class QDateTimeSpinWidget : public QSpinWidget +{ +public: + QDateTimeSpinWidget( QWidget *parent, const char *name ) + : QSpinWidget( parent, name ) + { + } + + void enabledChange(bool notenabled) + { + QDateEdit *de = ::qt_cast<QDateEdit*>(parentWidget()); + if (de && !notenabled) { + setUpEnabled(de->date() < de->maxValue()); + setDownEnabled(de->date() > de->minValue()); + } else { + setUpEnabled(!notenabled); + setDownEnabled(!notenabled); + } + } + +protected: +#ifndef QT_NO_WHEELEVENT + void wheelEvent( QWheelEvent *e ) + { + QDateTimeEditor *editor = (QDateTimeEditor*)editWidget()->qt_cast( "QDateTimeEditor" ); + Q_ASSERT( editor ); + if ( !editor ) + return; + + int section = editor->sectionAt( e->pos() ); + editor->setFocusSection( section ); + + if ( section == -1 ) + return; + QSpinWidget::wheelEvent( e ); + } +#endif +}; + +/*! + Constructs an empty datetime editor with parent \a parent and + called \a name. +*/ +QDateTimeEditor::QDateTimeEditor( QDateTimeEditBase * widget, QWidget *parent, + const char * name ) + : QWidget( parent, name, WNoAutoErase ) +{ + d = new QDateTimeEditorPrivate(); + cw = widget; + init(); +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +QDateTimeEditor::~QDateTimeEditor() +{ + delete d; +} + +/*! \internal + +*/ + +void QDateTimeEditor::init() +{ + setBackgroundMode( PaletteBase ); + setFocusSection( -1 ); + installEventFilter( this ); + setFocusPolicy( WheelFocus ); +} + + +/*! \reimp + +*/ + +bool QDateTimeEditor::event( QEvent *e ) +{ + if ( e->type() == QEvent::FocusIn || e->type() == QEvent::FocusOut ) { + if ( e->type() == QEvent::FocusOut ) + qApp->sendEvent( cw, e ); + update( rect() ); + } else if ( e->type() == QEvent::AccelOverride ) { + QKeyEvent* ke = (QKeyEvent*) e; + switch ( ke->key() ) { + case Key_Delete: + case Key_Backspace: + case Key_Up: + case Key_Down: + case Key_Left: + case Key_Right: + ke->accept(); + default: + break; + } + } + return QWidget::event( e ); +} + +/*! \reimp + +*/ + +void QDateTimeEditor::resizeEvent( QResizeEvent *e ) +{ + d->resize( e->size() ); + QWidget::resizeEvent( e ); +} + + +/*! \reimp + +*/ + +void QDateTimeEditor::paintEvent( QPaintEvent * ) +{ + QString txt; + for ( uint i = 0; i < d->sectionCount(); ++i ) { + txt += cw->sectionFormattedText( i ); + if ( i < d->sectionCount()-1 ) { + if ( d->section( i+1 ).separator() ) + txt += d->separator(); + else + txt += " "; + } + } + + QSharedDoubleBuffer buffer( this ); + const QBrush &bg = + colorGroup().brush( isEnabled() ? QColorGroup::Base : QColorGroup::Background ); + buffer.painter()->fillRect( 0, 0, width(), height(), bg ); + d->paint( txt, hasFocus(), *buffer.painter(), colorGroup(), rect(), + style() ); + buffer.end(); +} + + +/*! + Returns the section index at point \a p. +*/ +int QDateTimeEditor::sectionAt( const QPoint &p ) +{ + return d->section( p ); +} + +int QDateTimeEditor::mapSection( int sec ) +{ + return d->mapSection( sec ); +} + + +/*! \reimp + +*/ + +void QDateTimeEditor::mousePressEvent( QMouseEvent *e ) +{ + QPoint p( e->pos().x(), 0 ); + int sec = sectionAt( p ); + if ( sec != -1 ) { + cw->setFocusSection( sec ); + repaint( rect(), FALSE ); + } +} + +/*! \reimp + +*/ +bool QDateTimeEditor::eventFilter( QObject *o, QEvent *e ) +{ + if ( o == this ) { + if ( e->type() == QEvent::KeyPress ) { + QKeyEvent *ke = (QKeyEvent*)e; + switch ( ke->key() ) { + case Key_Right: + if ( d->focusSection() < (int)d->sectionCount()-1 ) { + if ( cw->setFocusSection( focusSection()+1 ) ) + repaint( rect(), FALSE ); + } + return TRUE; + case Key_Left: + if ( d->focusSection() > 0 ) { + if ( cw->setFocusSection( focusSection()-1 ) ) + repaint( rect(), FALSE ); + } + return TRUE; + case Key_Up: + cw->stepUp(); + return TRUE; + case Key_Down: + cw->stepDown(); + return TRUE; + case Key_Backspace: + if ( ::qt_cast<QDateEdit*>(cw) ) + ((QDateEdit*)cw)->removeFirstNumber( d->focusSection() ); + else if ( ::qt_cast<QTimeEdit*>(cw) ) + ((QTimeEdit*)cw)->removeFirstNumber( d->focusSection() ); + return TRUE; + case Key_Delete: + cw->removeLastNumber( d->focusSection() ); + return TRUE; + case Key_Tab: + case Key_BackTab: { + if ( ke->state() == Qt::ControlButton ) + return FALSE; + + QWidget *w = this; + bool hadDateEdit = FALSE; + while ( w ) { + if ( ::qt_cast<QDateTimeSpinWidget*>(w) && qstrcmp( w->name(), "qt_spin_widget" ) != 0 || + ::qt_cast<QDateTimeEdit*>(w) ) + break; + hadDateEdit = hadDateEdit || ::qt_cast<QDateEdit*>(w); + w = w->parentWidget(); + } + + if ( w ) { + if ( !::qt_cast<QDateTimeEdit*>(w) ) { + w = w->parentWidget(); + } else { + QDateTimeEdit *ed = (QDateTimeEdit*)w; + if ( hadDateEdit && ke->key() == Key_Tab ) { + ed->timeEdit()->setFocus(); + return TRUE; + } else if ( !hadDateEdit && ke->key() == Key_BackTab ) { + ed->dateEdit()->setFocus(); + return TRUE; + } else { + while ( w && !::qt_cast<QDateTimeEdit*>(w) ) + w = w->parentWidget(); + } + } + + qApp->sendEvent( w, e ); + return TRUE; + } + } break; + default: + QString txt = ke->text().lower(); + if ( !txt.isEmpty() && !separator().isEmpty() && txt[0] == separator()[0] ) { + // do the same thing as KEY_RIGHT when the user presses the separator key + if ( d->focusSection() < 2 ) { + if ( cw->setFocusSection( focusSection()+1 ) ) + repaint( rect(), FALSE ); + } + return TRUE; + } else if ( !txt.isEmpty() && ::qt_cast<QTimeEdit*>(cw) && focusSection() == (int) d->sectionCount()-1 ) { + // the first character of the AM/PM indicator toggles if the section has focus + QTimeEdit *te = (QTimeEdit*)cw; + QTime time = te->time(); + if ( lAMPM && lAM && lPM && (te->display()&QTimeEdit::AMPM) ) { + if ( txt[0] == (*lAM).lower()[0] && time.hour() >= 12 ) { + time.setHMS( time.hour()-12, time.minute(), time.second(), time.msec() ); + te->setTime( time ); + } else if ( txt[0] == (*lPM).lower()[0] && time.hour() < 12 ) { + time.setHMS( time.hour()+12, time.minute(), time.second(), time.msec() ); + te->setTime( time ); + } + } + } + + int num = txt[0].digitValue(); + if ( num != -1 ) { + cw->addNumber( d->focusSection(), num ); + return TRUE; + } + } + } + } + return FALSE; +} + + +/*! + Appends the number section \a sec to the editor. +*/ + +void QDateTimeEditor::appendSection( const QNumberSection& sec ) +{ + d->appendSection( sec ); +} + +/*! + Removes all sections from the editor. +*/ + +void QDateTimeEditor::clearSections() +{ + d->clearSections(); +} + +/*! + Sets the selection of \a sec to start at \a selstart and end at \a + selend. +*/ + +void QDateTimeEditor::setSectionSelection( int sec, int selstart, int selend ) +{ + d->setSectionSelection( sec, selstart, selend ); +} + +/*! + Sets the separator for all numbered sections to \a s. Note that + currently, only the first character of \a s is used. +*/ + +void QDateTimeEditor::setSeparator( const QString& s ) +{ + d->setSeparator( s ); + update(); +} + + +/*! + Returns the editor's separator. +*/ + +QString QDateTimeEditor::separator() const +{ + return d->separator(); +} + +/*! + Returns the number of the section that has focus. +*/ + +int QDateTimeEditor::focusSection() const +{ + return d->focusSection(); +} + + +/*! + Sets the focus to section \a sec. If \a sec does not exist, + nothing happens. +*/ + +bool QDateTimeEditor::setFocusSection( int sec ) +{ + return d->setFocusSection( sec ); +} + +/*! \class QDateTimeEditBase + \brief The QDateTimeEditBase class provides an abstraction for date and edit editors. + + Small abstract class that provides some functions that are common + for both QDateEdit and QTimeEdit. It is used internally by + QDateTimeEditor. +*/ + +/*! + \fn QDateTimeEditBase::QDateTimeEditBase(QWidget *, const char*) + \internal +*/ + +/*! + \fn QDateTimeEditBase::setFocusSection(int) + \internal +*/ + +/*! \fn QString QDateTimeEditBase::sectionFormattedText( int sec ) + \internal + + Pure virtual function which returns the formatted text of section \a + sec. + +*/ + +/*! \fn void QDateTimeEditBase::stepUp() + \internal + + Pure virtual slot which is called whenever the user increases the + number in a section by pressing the widget's arrow buttons or the + keyboard's arrow keys. +*/ + +/*! \fn void QDateTimeEditBase::stepDown() + \internal + + Pure virtual slot which is called whenever the user decreases the + number in a section by pressing the widget's arrow buttons or the + keyboard's arrow keys. + +*/ + +/*! \fn void QDateTimeEditBase::addNumber( int sec, int num ) + \internal + + Pure virtual function which is called whenever the user types a number. + \a sec indicates the section where the number should be added. \a + num is the number that was pressed. +*/ + +/*! \fn void QDateTimeEditBase::removeLastNumber( int sec ) + \internal + + Pure virtual function which is called whenever the user tries to + remove the last number from \a sec by pressing the delete key. +*/ + +//////////////// + +class QDateEditPrivate +{ +public: + int y; + int m; + int d; + // remebers the last entry for the day. + // if the day is 31 and you cycle through the months, + // the day will be 31 again if you reach a month with 31 days + // otherwise it will be the highest day in the month + int dayCache; + int yearSection; + int monthSection; + int daySection; + QDateEdit::Order ord; + bool overwrite; + bool adv; + int timerId; + bool typing; + QDate min; + QDate max; + bool changed; + QDateTimeEditor *ed; + QSpinWidget *controls; +}; + + +/*! + \class QDateEdit qdatetimeedit.h + \brief The QDateEdit class provides a date editor. + + \ingroup advanced + \ingroup time + \mainclass + + QDateEdit allows the user to edit dates by using the keyboard or + the arrow keys to increase/decrease date values. The arrow keys + can be used to move from section to section within the QDateEdit + box. Dates appear in accordance with the local date/time settings + or in year, month, day order if the system doesn't provide this + information. It is recommended that the QDateEdit be initialised + with a date, e.g. + + \code + QDateEdit *dateEdit = new QDateEdit( QDate::currentDate(), this ); + dateEdit->setRange( QDate::currentDate().addDays( -365 ), + QDate::currentDate().addDays( 365 ) ); + dateEdit->setOrder( QDateEdit::MDY ); + dateEdit->setAutoAdvance( TRUE ); + \endcode + + Here we've created a new QDateEdit object initialised with today's + date and restricted the valid date range to today plus or minus + 365 days. We've set the order to month, day, year. If the auto + advance property is TRUE (as we've set it here) when the user + completes a section of the date, e.g. enters two digits for the + month, they are automatically taken to the next section. + + The maximum and minimum values for a date value in the date editor + default to the maximum and minimum values for a QDate. You can + change this by calling setMinValue(), setMaxValue() or setRange(). + + Terminology: A QDateEdit widget comprises three 'sections', one + each for the year, month and day. You can change the separator + character using QDateTimeEditor::setSeparator(), by default the + separator will be taken from the systems settings. If that is + not possible, it defaults to "-". + + \img datetimewidgets.png Date Time Widgets + + \sa QDate QTimeEdit QDateTimeEdit +*/ + +/*! + \enum QDateEdit::Order + + This enum defines the order in which the sections that comprise a + date appear. + \value MDY month-day-year + \value DMY day-month-year + \value YMD year-month-day (the default) + \value YDM year-day-month (included for completeness; but should + not be used) +*/ + +/*! + \enum QTimeEdit::Display + + This enum defines the sections that comprise a time + + \value Hours The hours section + \value Minutes The minutes section + \value Seconds The seconds section + \value AMPM The AM/PM section + + The values can be or'ed together to show any combination. +*/ + +/*! + Constructs an empty date editor which is a child of \a parent and + called name \a name. +*/ + +QDateEdit::QDateEdit( QWidget * parent, const char * name ) + : QDateTimeEditBase( parent, name ) +{ + init(); + updateButtons(); +} + +/*! + \overload + + Constructs a date editor with the initial value \a date, parent \a + parent and called \a name. + + The date editor is initialized with \a date. +*/ + +QDateEdit::QDateEdit( const QDate& date, QWidget * parent, const char * name ) + : QDateTimeEditBase( parent, name ) +{ + init(); + setDate( date ); +} + +/*! \internal +*/ +void QDateEdit::init() +{ + d = new QDateEditPrivate(); + d->controls = new QDateTimeSpinWidget( this, qstrcmp( name(), "qt_datetime_dateedit" ) == 0 ? "qt_spin_widget" : "date edit controls" ); + d->ed = new QDateTimeEditor( this, d->controls, "date editor" ); + d->controls->setEditWidget( d->ed ); + setFocusProxy( d->ed ); + connect( d->controls, SIGNAL( stepUpPressed() ), SLOT( stepUp() ) ); + connect( d->controls, SIGNAL( stepDownPressed() ), SLOT( stepDown() ) ); + connect( this, SIGNAL( valueChanged(const QDate&) ), + SLOT( updateButtons() ) ); + d->ed->appendSection( QNumberSection( 0,4 ) ); + d->ed->appendSection( QNumberSection( 5,7 ) ); + d->ed->appendSection( QNumberSection( 8,10 ) ); + + d->yearSection = -1; + d->monthSection = -1; + d->daySection = -1; + + d->y = 0; + d->m = 0; + d->d = 0; + d->dayCache = 0; + setOrder( localOrder() ); + setFocusSection( 0 ); + d->overwrite = TRUE; + d->adv = FALSE; + d->timerId = 0; + d->typing = FALSE; + d->min = QDate( 1752, 9, 14 ); + d->max = QDate( 8000, 12, 31 ); + d->changed = FALSE; + + setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ); + + refcount++; +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +QDateEdit::~QDateEdit() +{ + delete d; + if ( !--refcount ) + cleanup(); +} + +/*! + \property QDateEdit::minValue + + \brief the editor's minimum value + + Setting the minimum date value is equivalent to calling + QDateEdit::setRange( \e d, maxValue() ), where \e d is the minimum + date. The default minimum date is 1752-09-14. + + \sa maxValue setRange() +*/ + +QDate QDateEdit::minValue() const +{ + return d->min; +} + +/*! + \property QDateEdit::maxValue + + \brief the editor's maximum value + + Setting the maximum date value for the editor is equivalent to + calling QDateEdit::setRange( minValue(), \e d ), where \e d is the + maximum date. The default maximum date is 8000-12-31. + + \sa minValue setRange() +*/ + +QDate QDateEdit::maxValue() const +{ + return d->max; +} + + +/*! + Sets the valid input range for the editor to be from \a min to \a + max inclusive. If \a min is invalid no minimum date will be set. + Similarly, if \a max is invalid no maximum date will be set. +*/ + +void QDateEdit::setRange( const QDate& min, const QDate& max ) +{ + if ( min.isValid() ) + d->min = min; + if ( max.isValid() ) + d->max = max; +} + +/*! + Sets the separator to \a s. Note that currently only the first + character of \a s is used. +*/ + +void QDateEdit::setSeparator( const QString& s ) +{ + d->ed->setSeparator( s ); +} + +/*! + Returns the editor's separator. +*/ + +QString QDateEdit::separator() const +{ + return d->ed->separator(); +} + + +/*! + Enables/disables the push buttons according to the min/max date + for this widget. +*/ + +void QDateEdit::updateButtons() +{ + if ( !isEnabled() ) + return; + + bool upEnabled = date() < maxValue(); + bool downEnabled = date() > minValue(); + + d->controls->setUpEnabled( upEnabled ); + d->controls->setDownEnabled( downEnabled ); +} + +/*! \reimp + */ +void QDateEdit::resizeEvent( QResizeEvent * ) +{ + d->controls->resize( width(), height() ); +} + +/*! \reimp + +*/ +QSize QDateEdit::sizeHint() const +{ + constPolish(); + QFontMetrics fm( font() ); + int fw = style().pixelMetric( QStyle::PM_DefaultFrameWidth, this ); + int h = QMAX( fm.lineSpacing(), 14 ) + 2; + int w = 2 + fm.width( '9' ) * 8 + fm.width( d->ed->separator() ) * 2 + d->controls->upRect().width() + fw * 4; + + return QSize( w, QMAX(h + fw * 2,20) ).expandedTo( QApplication::globalStrut() ); +} + +/*! \reimp + +*/ +QSize QDateEdit::minimumSizeHint() const +{ + return sizeHint(); +} + + +/*! + Returns the formatted number for section \a sec. This will + correspond to either the year, month or day section, depending on + the current display order. + + \sa setOrder() +*/ + +QString QDateEdit::sectionFormattedText( int sec ) +{ + QString txt; + txt = sectionText( sec ); + if ( d->typing && sec == d->ed->focusSection() ) + d->ed->setSectionSelection( sec, sectionOffsetEnd( sec ) - txt.length(), + sectionOffsetEnd( sec ) ); + else + d->ed->setSectionSelection( sec, sectionOffsetEnd( sec ) - sectionLength( sec ), + sectionOffsetEnd( sec ) ); + txt = txt.rightJustify( sectionLength( sec ), QDATETIMEEDIT_HIDDEN_CHAR ); + return txt; +} + + +/*! + Returns the desired length (number of digits) of section \a sec. + This will correspond to either the year, month or day section, + depending on the current display order. + + \sa setOrder() +*/ + +int QDateEdit::sectionLength( int sec ) const +{ + int val = 0; + if ( sec == d->yearSection ) { + val = 4; + } else if ( sec == d->monthSection ) { + val = 2; + } else if ( sec == d->daySection ) { + val = 2; + } + return val; +} + +/*! + Returns the text of section \a sec. This will correspond to either + the year, month or day section, depending on the current display + order. + + \sa setOrder() +*/ + +QString QDateEdit::sectionText( int sec ) const +{ + int val = 0; + if ( sec == d->yearSection ) { + val = d->y; + } else if ( sec == d->monthSection ) { + val = d->m; + } else if ( sec == d->daySection ) { + val = d->d; + } + return QString::number( val ); +} + +/*! \internal + + Returns the end of the section offset \a sec. + +*/ + +int QDateEdit::sectionOffsetEnd( int sec ) const +{ + if ( sec == d->yearSection ) { + switch( d->ord ) { + case DMY: + case MDY: + return sectionOffsetEnd( sec-1) + separator().length() + sectionLength( sec ); + case YMD: + case YDM: + return sectionLength( sec ); + } + } else if ( sec == d->monthSection ) { + switch( d->ord ) { + case DMY: + case YDM: + case YMD: + return sectionOffsetEnd( sec-1) + separator().length() + sectionLength( sec ); + case MDY: + return sectionLength( sec ); + } + } else if ( sec == d->daySection ) { + switch( d->ord ) { + case DMY: + return sectionLength( sec ); + case YMD: + case MDY: + case YDM: + return sectionOffsetEnd( sec-1 ) + separator().length() + sectionLength( sec ); + } + } + return 0; +} + + +/*! + \property QDateEdit::order + \brief the order in which the year, month and day appear + + The default order is locale dependent. + + \sa Order +*/ + +void QDateEdit::setOrder( QDateEdit::Order order ) +{ + d->ord = order; + switch( d->ord ) { + case DMY: + d->yearSection = 2; + d->monthSection = 1; + d->daySection = 0; + break; + case MDY: + d->yearSection = 2; + d->monthSection = 0; + d->daySection = 1; + break; + case YMD: + d->yearSection = 0; + d->monthSection = 1; + d->daySection = 2; + break; + case YDM: + d->yearSection = 0; + d->monthSection = 2; + d->daySection = 1; + break; + } + if ( isVisible() ) + d->ed->repaint( d->ed->rect(), FALSE ); +} + + +QDateEdit::Order QDateEdit::order() const +{ + return d->ord; +} + + +/*! \reimp + +*/ +void QDateEdit::stepUp() +{ + int sec = d->ed->focusSection(); + bool accepted = FALSE; + if ( sec == d->yearSection ) { + if ( !outOfRange( d->y+1, d->m, d->d ) ) { + accepted = TRUE; + setYear( d->y+1 ); + } + } else if ( sec == d->monthSection ) { + if ( !outOfRange( d->y, d->m+1, d->d ) ) { + accepted = TRUE; + setMonth( d->m+1 ); + } + } else if ( sec == d->daySection ) { + if ( !outOfRange( d->y, d->m, d->d+1 ) ) { + accepted = TRUE; + setDay( d->d+1 ); + } + } + if ( accepted ) { + d->changed = FALSE; + emit valueChanged( date() ); + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + + + +/*! \reimp + +*/ + +void QDateEdit::stepDown() +{ + int sec = d->ed->focusSection(); + bool accepted = FALSE; + if ( sec == d->yearSection ) { + if ( !outOfRange( d->y-1, d->m, d->d ) ) { + accepted = TRUE; + setYear( d->y-1 ); + } + } else if ( sec == d->monthSection ) { + if ( !outOfRange( d->y, d->m-1, d->d ) ) { + accepted = TRUE; + setMonth( d->m-1 ); + } + } else if ( sec == d->daySection ) { + if ( !outOfRange( d->y, d->m, d->d-1 ) ) { + accepted = TRUE; + setDay( d->d-1 ); + } + } + if ( accepted ) { + d->changed = FALSE; + emit valueChanged( date() ); + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + +/*! + Sets the year to \a year, which must be a valid year. The range + currently supported is from 1752 to 8000. + + \sa QDate +*/ + +void QDateEdit::setYear( int year ) +{ + if ( year < 1752 ) + year = 1752; + if ( year > 8000 ) + year = 8000; + if ( !outOfRange( year, d->m, d->d ) ) { + d->y = year; + setMonth( d->m ); + int tmp = d->dayCache; + setDay( d->dayCache ); + d->dayCache = tmp; + } +} + + +/*! + Sets the month to \a month, which must be a valid month, i.e. + between 1 and 12. +*/ + +void QDateEdit::setMonth( int month ) +{ + if ( month < 1 ) + month = 1; + if ( month > 12 ) + month = 12; + if ( !outOfRange( d->y, month, d->d ) ) { + d->m = month; + int tmp = d->dayCache; + setDay( d->dayCache ); + d->dayCache = tmp; + } +} + + +/*! + Sets the day to \a day, which must be a valid day. The function + will ensure that the \a day set is valid for the month and year. +*/ + +void QDateEdit::setDay( int day ) +{ + if ( day < 1 ) + day = 1; + if ( day > 31 ) + day = 31; + if ( d->m > 0 && d->y > 1752 ) { + while ( !QDate::isValid( d->y, d->m, day ) ) + --day; + if ( !outOfRange( d->y, d->m, day ) ) + d->d = day; + } else if ( d->m > 0 ) { + if ( day > 0 && day < 32 ) { + if ( !outOfRange( d->y, d->m, day ) ) + d->d = day; + } + } + d->dayCache = d->d; +} + + +/*! + \property QDateEdit::date + \brief the editor's date value. + + If the date property is not valid, the editor displays all zeroes + and QDateEdit::date() will return an invalid date. It is strongly + recommended that the editor is given a default date value (e.g. + currentDate()). That way, attempts to set the date property to an + invalid date will fail. + + When changing the date property, if the date is less than + minValue(), or is greater than maxValue(), nothing happens. +*/ + +void QDateEdit::setDate( const QDate& date ) +{ + if ( !date.isValid() ) { + d->y = 0; + d->m = 0; + d->d = 0; + d->dayCache = 0; + } else { + if ( date > maxValue() || date < minValue() ) + return; + d->y = date.year(); + d->m = date.month(); + d->d = date.day(); + d->dayCache = d->d; + emit valueChanged( date ); + } + d->changed = FALSE; + d->ed->repaint( d->ed->rect(), FALSE ); +} + +QDate QDateEdit::date() const +{ + if ( QDate::isValid( d->y, d->m, d->d ) ) + return QDate( d->y, d->m, d->d ); + return QDate(); +} + +/*! \internal + + Returns TRUE if \a y, \a m, \a d is out of range, otherwise returns + FALSE. + + \sa setRange() + +*/ + +bool QDateEdit::outOfRange( int y, int m, int d ) const +{ + if ( QDate::isValid( y, m, d ) ) { + QDate currentDate( y, m, d ); + if ( currentDate > maxValue() || + currentDate < minValue() ) { + //## outOfRange should set overwrite? + return TRUE; + } + return FALSE; + } + return FALSE; /* assume ok */ +} + +/*! \reimp + +*/ + +void QDateEdit::addNumber( int sec, int num ) +{ + if ( sec == -1 ) + return; + killTimer( d->timerId ); + bool overwrite = FALSE; + bool accepted = FALSE; + d->typing = TRUE; + QString txt; + if ( sec == d->yearSection ) { + txt = QString::number( d->y ); + if ( d->overwrite || txt.length() == 4 ) { + accepted = TRUE; + d->y = num; + } else { + txt += QString::number( num ); + if ( txt.length() == 4 ) { + int val = txt.toInt(); + if ( val < 1792 ) + d->y = 1792; + else if ( val > 8000 ) + d->y = 8000; + else if ( outOfRange( val, d->m, d->d ) ) + txt = QString::number( d->y ); + else { + accepted = TRUE; + d->y = val; + } + } else { + accepted = TRUE; + d->y = txt.toInt(); + } + if ( d->adv && txt.length() == 4 ) { + d->ed->setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } + } else if ( sec == d->monthSection ) { + txt = QString::number( d->m ); + if ( d->overwrite || txt.length() == 2 ) { + accepted = TRUE; + d->m = num; + } else { + txt += QString::number( num ); + int temp = txt.toInt(); + if ( temp > 12 ) + temp = num; + if ( outOfRange( d->y, temp, d->d ) ) + txt = QString::number( d->m ); + else { + accepted = TRUE; + d->m = temp; + } + if ( d->adv && txt.length() == 2 ) { + d->ed->setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } + } else if ( sec == d->daySection ) { + txt = QString::number( d->d ); + if ( d->overwrite || txt.length() == 2 ) { + accepted = TRUE; + d->d = num; + d->dayCache = d->d; + } else { + txt += QString::number( num ); + int temp = txt.toInt(); + if ( temp > 31 ) + temp = num; + if ( outOfRange( d->y, d->m, temp ) ) + txt = QString::number( d->d ); + else { + accepted = TRUE; + d->d = temp; + d->dayCache = d->d; + } + if ( d->adv && txt.length() == 2 ) { + d->ed->setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } + } + if ( accepted ) { + d->changed = FALSE; + emit valueChanged( date() ); + } + d->overwrite = overwrite; + d->timerId = startTimer( qApp->doubleClickInterval()*4 ); + d->ed->repaint( d->ed->rect(), FALSE ); +} + + +/*! \reimp + +*/ + +bool QDateEdit::setFocusSection( int s ) +{ + if ( s != d->ed->focusSection() ) { + killTimer( d->timerId ); + d->overwrite = TRUE; + d->typing = FALSE; + fix(); // will emit valueChanged if necessary + } + return d->ed->setFocusSection( s ); +} + + +/*! + Attempts to fix any invalid date entries. + + The rules applied are as follows: + + \list + \i If the year has four digits it is left unchanged. + \i If the year has two digits, the year will be changed to four + digits in the range current year - 70 to current year + 29. + \i If the year has three digits in the range 100..999, the + current millennium, i.e. 2000, will be added giving a year + in the range 2100..2999. + \i If the day or month is 0 then it will be set to 1 or the + minimum valid day\month in the range. + \endlist + +*/ + +void QDateEdit::fix() +{ + bool changed = FALSE; + int currentYear = QDate::currentDate().year(); + int year = d->y; + if ( year < 100 ) { + int currentCentury = currentYear / 100; + year += currentCentury * 100; + if ( currentYear > year ) { + if ( currentYear > year + 70 ) + year += 100; + } else { + if ( year >= currentYear + 30 ) + year -= 100; + } + changed = TRUE; + } else if ( year < 1000 ) { + int currentMillennium = currentYear / 10; + year += currentMillennium * 10; + changed = TRUE; + } else if (d->d == 0) { + d->d = 1; + changed = TRUE; + } else if (d->m == 0) { + d->m = 1; + changed = TRUE; + } + if ( outOfRange( year, d->m, d->d ) ) { + if ( minValue().isValid() && date() < minValue() ) { + d->d = minValue().day(); + d->dayCache = d->d; + d->m = minValue().month(); + d->y = minValue().year(); + } + if ( date() > maxValue() ) { + d->d = maxValue().day(); + d->dayCache = d->d; + d->m = maxValue().month(); + d->y = maxValue().year(); + } + changed = TRUE; + } else if ( changed ) + setYear( year ); + if ( changed ) { + emit valueChanged( date() ); + d->changed = FALSE; + } +} + + +/*! \reimp + +*/ + +bool QDateEdit::event( QEvent *e ) +{ + if( e->type() == QEvent::FocusOut ) { + d->typing = FALSE; + fix(); + // the following can't be done in fix() because fix() called + // from all over the place and it will break the old behaviour + if ( !QDate::isValid( d->y, d->m, d->d ) ) { + d->dayCache = d->d; + int i = d->d; + for ( ; i > 0; i-- ) { + d->d = i; + if ( QDate::isValid( d->y, d->m, d->d ) ) + break; + } + d->changed = TRUE; + } + if ( d->changed ) { + emit valueChanged( date() ); + d->changed = FALSE; + } + } else if ( e->type() == QEvent::LocaleChange ) { + readLocaleSettings(); + d->ed->setSeparator( localDateSep() ); + setOrder( localOrder() ); + } + return QDateTimeEditBase::event( e ); +} + +/*! + \internal + + Function which is called whenever the user tries to + remove the first number from \a sec by pressing the backspace key. +*/ + +void QDateEdit::removeFirstNumber( int sec ) +{ + if ( sec == -1 ) + return; + QString txt; + if ( sec == d->yearSection ) { + txt = QString::number( d->y ); + txt = txt.mid( 1, txt.length() ) + "0"; + d->y = txt.toInt(); + } else if ( sec == d->monthSection ) { + txt = QString::number( d->m ); + txt = txt.mid( 1, txt.length() ) + "0"; + d->m = txt.toInt(); + } else if ( sec == d->daySection ) { + txt = QString::number( d->d ); + txt = txt.mid( 1, txt.length() ) + "0"; + d->d = txt.toInt(); + d->dayCache = d->d; + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + +/*! \reimp + +*/ + +void QDateEdit::removeLastNumber( int sec ) +{ + if ( sec == -1 ) + return; + QString txt; + if ( sec == d->yearSection ) { + txt = QString::number( d->y ); + txt = txt.mid( 0, txt.length()-1 ); + d->y = txt.toInt(); + } else if ( sec == d->monthSection ) { + txt = QString::number( d->m ); + txt = txt.mid( 0, txt.length()-1 ); + d->m = txt.toInt(); + } else if ( sec == d->daySection ) { + txt = QString::number( d->d ); + txt = txt.mid( 0, txt.length()-1 ); + d->d = txt.toInt(); + d->dayCache = d->d; + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + +/*! + \property QDateEdit::autoAdvance + \brief whether the editor automatically advances to the next + section + + If autoAdvance is TRUE, the editor will automatically advance + focus to the next date section if a user has completed a section. + The default is FALSE. +*/ + +void QDateEdit::setAutoAdvance( bool advance ) +{ + d->adv = advance; +} + + +bool QDateEdit::autoAdvance() const +{ + return d->adv; +} + +/*! \reimp +*/ + +void QDateEdit::timerEvent( QTimerEvent * ) +{ + d->overwrite = TRUE; +} + +/*! + \fn void QDateEdit::valueChanged( const QDate& date ) + + This signal is emitted whenever the editor's value changes. The \a + date parameter is the new value. +*/ + +/////////// + +class QTimeEditPrivate +{ +public: + int h; + int m; + int s; + uint display; + bool adv; + bool overwrite; + int timerId; + bool typing; + QTime min; + QTime max; + bool changed; + QDateTimeEditor *ed; + QSpinWidget *controls; +}; + +/*! + \class QTimeEdit qdatetimeedit.h + \brief The QTimeEdit class provides a time editor. + + \ingroup advanced + \ingroup time + \mainclass + + QTimeEdit allows the user to edit times by using the keyboard or + the arrow keys to increase/decrease time values. The arrow keys + can be used to move from section to section within the QTimeEdit + box. The user can automatically be moved to the next section once + they complete a section using setAutoAdvance(). Times appear in + hour, minute, second order. It is recommended that the QTimeEdit + is initialised with a time, e.g. + \code + QTime timeNow = QTime::currentTime(); + QTimeEdit *timeEdit = new QTimeEdit( timeNow, this ); + timeEdit->setRange( timeNow, timeNow.addSecs( 60 * 60 ) ); + \endcode + Here we've created a QTimeEdit widget set to the current time. + We've also set the minimum value to the current time and the + maximum time to one hour from now. + + The maximum and minimum values for a time value in the time editor + default to the maximum and minimum values for a QTime. You can + change this by calling setMinValue(), setMaxValue() or setRange(). + + Terminology: A QTimeWidget consists of three sections, one each + for the hour, minute and second. You can change the separator + character using setSeparator(), by default the separator is read + from the system's settings. + + \img datetimewidgets.png Date Time Widgets + + \sa QTime QDateEdit QDateTimeEdit +*/ + + +/*! + Constructs an empty time edit with parent \a parent and called \a + name. +*/ + +QTimeEdit::QTimeEdit( QWidget * parent, const char * name ) + : QDateTimeEditBase( parent, name ) +{ + init(); +} + +/*! + \overload + + Constructs a time edit with the initial time value, \a time, + parent \a parent and called \a name. +*/ + +QTimeEdit::QTimeEdit( const QTime& time, QWidget * parent, const char * name ) + : QDateTimeEditBase( parent, name ) +{ + init(); + setTime( time ); +} + +/*! \internal + */ + +void QTimeEdit::init() +{ + d = new QTimeEditPrivate(); + d->controls = new QDateTimeSpinWidget( this, qstrcmp( name(), "qt_datetime_timeedit" ) == 0 ? "qt_spin_widget" : "time edit controls" ); + d->ed = new QDateTimeEditor( this, d->controls, "time edit base" ); + d->controls->setEditWidget( d->ed ); + setFocusProxy( d->ed ); + connect( d->controls, SIGNAL( stepUpPressed() ), SLOT( stepUp() ) ); + connect( d->controls, SIGNAL( stepDownPressed() ), SLOT( stepDown() ) ); + + d->ed->appendSection( QNumberSection( 0,0, TRUE, 0 ) ); + d->ed->appendSection( QNumberSection( 0,0, TRUE, 1 ) ); + d->ed->appendSection( QNumberSection( 0,0, TRUE, 2 ) ); + d->ed->setSeparator( localTimeSep() ); + + d->h = 0; + d->m = 0; + d->s = 0; + d->display = Hours | Minutes | Seconds; + if ( lAMPM ) { + d->display |= AMPM; + d->ed->appendSection( QNumberSection( 0,0, FALSE, 3 ) ); + } + d->adv = FALSE; + d->overwrite = TRUE; + d->timerId = 0; + d->typing = FALSE; + d->min = QTime( 0, 0, 0 ); + d->max = QTime( 23, 59, 59 ); + d->changed = FALSE; + + setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ); + + refcount++; +} + +/*! + Destroys the object and frees any allocated resources. +*/ + +QTimeEdit::~QTimeEdit() +{ + delete d; + if ( !--refcount ) + cleanup(); +} + +/*! + \property QTimeEdit::minValue + \brief the minimum time value + + Setting the minimum time value is equivalent to calling + QTimeEdit::setRange( \e t, maxValue() ), where \e t is the minimum + time. The default minimum time is 00:00:00. + + \sa maxValue setRange() +*/ + +QTime QTimeEdit::minValue() const +{ + return d->min; +} + +/*! + \property QTimeEdit::maxValue + \brief the maximum time value + + Setting the maximum time value is equivalent to calling + QTimeEdit::setRange( minValue(), \e t ), where \e t is the maximum + time. The default maximum time is 23:59:59. + + \sa minValue setRange() +*/ + +QTime QTimeEdit::maxValue() const +{ + return d->max; +} + + +/*! + Sets the valid input range for the editor to be from \a min to \a + max inclusive. If \a min is invalid no minimum time is set. + Similarly, if \a max is invalid no maximum time is set. +*/ + +void QTimeEdit::setRange( const QTime& min, const QTime& max ) +{ + if ( min.isValid() ) + d->min = min; + if ( max.isValid() ) + d->max = max; +} + +/*! + \property QTimeEdit::display + \brief the sections that are displayed in the time edit + + The value can be any combination of the values in the Display enum. + By default, the widget displays hours, minutes and seconds. +*/ +void QTimeEdit::setDisplay( uint display ) +{ + if ( d->display == display ) + return; + + d->ed->clearSections(); + d->display = display; + if ( d->display & Hours ) + d->ed->appendSection( QNumberSection( 0,0, TRUE, 0 ) ); + if ( d->display & Minutes ) + d->ed->appendSection( QNumberSection( 0,0, TRUE, 1 ) ); + if ( d->display & Seconds ) + d->ed->appendSection( QNumberSection( 0,0, TRUE, 2 ) ); + if ( d->display & AMPM ) + d->ed->appendSection( QNumberSection( 0,0, FALSE, 3 ) ); + + d->ed->setFocusSection( 0 ); + d->ed->update(); +} + +uint QTimeEdit::display() const +{ + return d->display; +} + +/*! + \property QTimeEdit::time + \brief the editor's time value. + + When changing the time property, if the time is less than + minValue(), or is greater than maxValue(), nothing happens. +*/ + +void QTimeEdit::setTime( const QTime& time ) +{ + if ( !time.isValid() ) { + d->h = 0; + d->m = 0; + d->s = 0; + } else { + if ( time > maxValue() || time < minValue() ) + return; + d->h = time.hour(); + d->m = time.minute(); + d->s = time.second(); + emit valueChanged( time ); + } + d->changed = FALSE; + d->ed->repaint( d->ed->rect(), FALSE ); +} + +QTime QTimeEdit::time() const +{ + if ( QTime::isValid( d->h, d->m, d->s ) ) + return QTime( d->h, d->m, d->s ); + return QTime(); +} + +/*! + \property QTimeEdit::autoAdvance + \brief whether the editor automatically advances to the next + section + + If autoAdvance is TRUE, the editor will automatically advance + focus to the next time section if a user has completed a section. + The default is FALSE. +*/ + +void QTimeEdit::setAutoAdvance( bool advance ) +{ + d->adv = advance; +} + +bool QTimeEdit::autoAdvance() const +{ + return d->adv; +} + +/*! + Sets the separator to \a s. Note that currently only the first + character of \a s is used. +*/ + +void QTimeEdit::setSeparator( const QString& s ) +{ + d->ed->setSeparator( s ); +} + +/*! + Returns the editor's separator. +*/ + +QString QTimeEdit::separator() const +{ + return d->ed->separator(); +} + + +/*! + \fn void QTimeEdit::valueChanged( const QTime& time ) + + This signal is emitted whenever the editor's value changes. The \a + time parameter is the new value. +*/ + +/*! \reimp + +*/ + +bool QTimeEdit::event( QEvent *e ) +{ + if ( e->type() == QEvent::FocusOut ) { + d->typing = FALSE; + if ( d->changed ) { + emit valueChanged( time() ); + d->changed = FALSE; + } + } else if ( e->type() == QEvent::LocaleChange ) { + readLocaleSettings(); + d->ed->setSeparator( localTimeSep() ); + } + return QDateTimeEditBase::event( e ); +} + +/*! \reimp + +*/ + +void QTimeEdit::timerEvent( QTimerEvent * ) +{ + d->overwrite = TRUE; +} + + +/*! \reimp + +*/ + +void QTimeEdit::stepUp() +{ + if (minValue() > maxValue()) { + return; + } + int sec = d->ed->mapSection( d->ed->focusSection() ); + bool accepted = TRUE; + switch( sec ) { + case 0: + do { + d->h = (d->h + 1) % 24; + } while (outOfRange(d->h, d->m, d->s)); + break; + case 1: + do { + d->m = (d->m + 1) % 60; + } while (outOfRange(d->h, d->m, d->s)); + break; + case 2: + do { + d->s = (d->s + 1) % 60; + } while (outOfRange(d->h, d->m, d->s)); + break; + case 3: + if ( d->h < 12 ) + setHour( d->h+12 ); + else + setHour( d->h-12 ); + break; + default: + accepted = FALSE; +#ifdef QT_CHECK_RANGE + qWarning( "QTimeEdit::stepUp: Focus section out of range!" ); +#endif + break; + } + if ( accepted ) { + d->changed = FALSE; + emit valueChanged( time() ); + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + + +/*! \reimp + +*/ + +void QTimeEdit::stepDown() +{ + if (minValue() > maxValue()) { + return; + } + + int sec = d->ed->mapSection( d->ed->focusSection() ); + bool accepted = TRUE; + switch( sec ) { + case 0: + do { + if (--d->h < 0) + d->h = 23; + } while (outOfRange(d->h, d->m, d->s)); + break; + case 1: + do { + if (--d->m < 0) + d->m = 59; + } while (outOfRange(d->h, d->m, d->s)); + break; + case 2: + do { + if (--d->s < 0) + d->s = 59; + } while (outOfRange(d->h, d->m, d->s)); + break; + case 3: + if ( d->h > 11 ) + setHour( d->h-12 ); + else + setHour( d->h+12 ); + break; + default: + accepted = FALSE; +#ifdef QT_CHECK_RANGE + qWarning( "QTimeEdit::stepDown: Focus section out of range!" ); +#endif + break; + } + if ( accepted ) { + d->changed = FALSE; + emit valueChanged( time() ); + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + + +/*! + Returns the formatted number for section \a sec. This will + correspond to either the hour, minute or second section, depending + on \a sec. +*/ + +QString QTimeEdit::sectionFormattedText( int sec ) +{ + QString txt; + txt = sectionText( sec ); + txt = txt.rightJustify( 2, QDATETIMEEDIT_HIDDEN_CHAR ); + int offset = sec*2+sec*separator().length() + txt.length(); + if ( d->typing && sec == d->ed->focusSection() ) + d->ed->setSectionSelection( sec, offset - txt.length(), offset ); + else + d->ed->setSectionSelection( sec, offset - txt.length(), offset ); + + return txt; +} + + +/*! \reimp + +*/ + +bool QTimeEdit::setFocusSection( int sec ) +{ + if ( sec != d->ed->focusSection() ) { + killTimer( d->timerId ); + d->overwrite = TRUE; + d->typing = FALSE; + QString txt = sectionText( sec ); + txt = txt.rightJustify( 2, QDATETIMEEDIT_HIDDEN_CHAR ); + int offset = sec*2+sec*separator().length() + txt.length(); + d->ed->setSectionSelection( sec, offset - txt.length(), offset ); + if ( d->changed ) { + emit valueChanged( time() ); + d->changed = FALSE; + } + } + return d->ed->setFocusSection( sec ); +} + + +/*! + Sets the hour to \a h, which must be a valid hour, i.e. in the + range 0..24. +*/ + +void QTimeEdit::setHour( int h ) +{ + if ( h < 0 ) + h = 0; + if ( h > 23 ) + h = 23; + d->h = h; +} + + +/*! + Sets the minute to \a m, which must be a valid minute, i.e. in the + range 0..59. +*/ + +void QTimeEdit::setMinute( int m ) +{ + if ( m < 0 ) + m = 0; + if ( m > 59 ) + m = 59; + d->m = m; +} + + +/*! + Sets the second to \a s, which must be a valid second, i.e. in the + range 0..59. +*/ + +void QTimeEdit::setSecond( int s ) +{ + if ( s < 0 ) + s = 0; + if ( s > 59 ) + s = 59; + d->s = s; +} + + +/*! \internal + + Returns the text of section \a sec. + +*/ + +QString QTimeEdit::sectionText( int sec ) +{ + sec = d->ed->mapSection( sec ); + + QString txt; + switch( sec ) { + case 0: + if ( !(d->display & AMPM) || ( d->h < 13 && d->h ) ) { // I wished the day stared at 0:00 for everybody + txt = QString::number( d->h ); + } else { + if ( d->h ) + txt = QString::number( d->h - 12 ); + else + txt = "12"; + } + break; + case 1: + txt = QString::number( d->m ); + break; + case 2: + txt = QString::number( d->s ); + break; + case 3: + if ( d->h < 12 ) { + if ( lAM ) + txt = *lAM; + else + txt = QString::fromLatin1( "AM" ); + } else { + if ( lPM ) + txt = *lPM; + else + txt = QString::fromLatin1( "PM" ); + } + break; + default: + break; + } + return txt; +} + + +/*! \internal + Returns TRUE if \a h, \a m, and \a s are out of range. + */ + +bool QTimeEdit::outOfRange( int h, int m, int s ) const +{ + if ( QTime::isValid( h, m, s ) ) { + QTime currentTime( h, m, s ); + if ( currentTime > maxValue() || + currentTime < minValue() ) + return TRUE; + else + return FALSE; + } + return TRUE; +} + +/*! \reimp + +*/ + +void QTimeEdit::addNumber( int sec, int num ) +{ + if ( sec == -1 ) + return; + sec = d->ed->mapSection( sec ); + killTimer( d->timerId ); + bool overwrite = FALSE; + bool accepted = FALSE; + d->typing = TRUE; + QString txt; + + switch( sec ) { + case 0: + txt = ( d->display & AMPM && d->h > 12 ) ? + QString::number( d->h - 12 ) : QString::number( d->h ); + + if ( d->overwrite || txt.length() == 2 ) { + if ( d->display & AMPM && num == 0 ) + break; // Don't process 0 in 12 hour clock mode + if ( d->display & AMPM && d->h > 11 ) + num += 12; + if ( !outOfRange( num, d->m, d->s ) ) { + accepted = TRUE; + d->h = num; + } + } else { + txt += QString::number( num ); + int temp = txt.toInt(); + + if ( d->display & AMPM ) { + if ( temp == 12 ) { + if ( d->h < 12 ) { + temp = 0; + } + accepted = TRUE; + } else if ( outOfRange( temp + 12, d->m, d->s ) ) { + txt = QString::number( d->h ); + } else { + if ( d->h > 11 ) { + temp += 12; + } + accepted = TRUE; + } + } else if ( !(d->display & AMPM) && outOfRange( temp, d->m, d->s ) ) { + txt = QString::number( d->h ); + } else { + accepted = TRUE; + } + + if ( accepted ) + d->h = temp; + + if ( d->adv && txt.length() == 2 ) { + setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } + break; + + case 1: + txt = QString::number( d->m ); + if ( d->overwrite || txt.length() == 2 ) { + if ( !outOfRange( d->h, num, d->s ) ) { + accepted = TRUE; + d->m = num; + } + } else { + txt += QString::number( num ); + int temp = txt.toInt(); + if ( temp > 59 ) + temp = num; + if ( outOfRange( d->h, temp, d->s ) ) + txt = QString::number( d->m ); + else { + accepted = TRUE; + d->m = temp; + } + if ( d->adv && txt.length() == 2 ) { + setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } + break; + + case 2: + txt = QString::number( d->s ); + if ( d->overwrite || txt.length() == 2 ) { + if ( !outOfRange( d->h, d->m, num ) ) { + accepted = TRUE; + d->s = num; + } + } else { + txt += QString::number( num ); + int temp = txt.toInt(); + if ( temp > 59 ) + temp = num; + if ( outOfRange( d->h, d->m, temp ) ) + txt = QString::number( d->s ); + else { + accepted = TRUE; + d->s = temp; + } + if ( d->adv && txt.length() == 2 ) { + setFocusSection( d->ed->focusSection()+1 ); + overwrite = TRUE; + } + } + break; + + case 3: + break; + + default: + break; + } + d->changed = !accepted; + if ( accepted ) + emit valueChanged( time() ); + d->overwrite = overwrite; + d->timerId = startTimer( qApp->doubleClickInterval()*4 ); + d->ed->repaint( d->ed->rect(), FALSE ); +} + + +/*! + \internal + + Function which is called whenever the user tries to + remove the first number from \a sec by pressing the backspace key. +*/ + +void QTimeEdit::removeFirstNumber( int sec ) +{ + if ( sec == -1 ) + return; + sec = d->ed->mapSection( sec ); + QString txt; + switch( sec ) { + case 0: + txt = QString::number( d->h ); + break; + case 1: + txt = QString::number( d->m ); + break; + case 2: + txt = QString::number( d->s ); + break; + } + txt = txt.mid( 1, txt.length() ) + "0"; + switch( sec ) { + case 0: + d->h = txt.toInt(); + break; + case 1: + d->m = txt.toInt(); + break; + case 2: + d->s = txt.toInt(); + break; + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + +/*! \reimp + +*/ +void QTimeEdit::removeLastNumber( int sec ) +{ + if ( sec == -1 ) + return; + sec = d->ed->mapSection( sec ); + QString txt; + switch( sec ) { + case 0: + txt = QString::number( d->h ); + break; + case 1: + txt = QString::number( d->m ); + break; + case 2: + txt = QString::number( d->s ); + break; + } + txt = txt.mid( 0, txt.length()-1 ); + switch( sec ) { + case 0: + d->h = txt.toInt(); + break; + case 1: + d->m = txt.toInt(); + break; + case 2: + d->s = txt.toInt(); + break; + } + d->ed->repaint( d->ed->rect(), FALSE ); +} + +/*! \reimp + */ +void QTimeEdit::resizeEvent( QResizeEvent * ) +{ + d->controls->resize( width(), height() ); +} + +/*! \reimp +*/ +QSize QTimeEdit::sizeHint() const +{ + constPolish(); + QFontMetrics fm( font() ); + int fw = style().pixelMetric( QStyle::PM_DefaultFrameWidth, this ); + int h = fm.lineSpacing() + 2; + int w = 2 + fm.width( '9' ) * 6 + fm.width( d->ed->separator() ) * 2 + + d->controls->upRect().width() + fw * 4; + if ( d->display & AMPM ) { + if ( lAM ) + w += fm.width( *lAM ) + 4; + else + w += fm.width( QString::fromLatin1( "AM" ) ) + 4; + } + + return QSize( w, QMAX(h + fw * 2,20) ).expandedTo( QApplication::globalStrut() ); +} + +/*! \reimp +*/ +QSize QTimeEdit::minimumSizeHint() const +{ + return sizeHint(); +} + +/*! + \internal + Enables/disables the push buttons according to the min/max time + for this widget. +*/ + +// ### Remove in 4.0? + +void QTimeEdit::updateButtons() +{ + if ( !isEnabled() ) + return; + + bool upEnabled = time() < maxValue(); + bool downEnabled = time() > minValue(); + + d->controls->setUpEnabled( upEnabled ); + d->controls->setDownEnabled( downEnabled ); +} + + +class QDateTimeEditPrivate +{ +public: + bool adv; +}; + +/*! + \class QDateTimeEdit qdatetimeedit.h + \brief The QDateTimeEdit class combines a QDateEdit and QTimeEdit + widget into a single widget for editing datetimes. + + \ingroup advanced + \ingroup time + \mainclass + + QDateTimeEdit consists of a QDateEdit and QTimeEdit widget placed + side by side and offers the functionality of both. The user can + edit the date and time by using the keyboard or the arrow keys to + increase/decrease date or time values. The Tab key can be used to + move from section to section within the QDateTimeEdit widget, and + the user can be moved automatically when they complete a section + using setAutoAdvance(). The datetime can be set with + setDateTime(). + + The date format is read from the system's locale settings. It is + set to year, month, day order if that is not possible. See + QDateEdit::setOrder() to change this. Times appear in the order + hours, minutes, seconds using the 24 hour clock. + + It is recommended that the QDateTimeEdit is initialised with a + datetime, e.g. + \code + QDateTimeEdit *dateTimeEdit = new QDateTimeEdit( QDateTime::currentDateTime(), this ); + dateTimeEdit->dateEdit()->setRange( QDateTime::currentDate(), + QDateTime::currentDate().addDays( 7 ) ); + \endcode + Here we've created a new QDateTimeEdit set to the current date and + time, and set the date to have a minimum date of now and a maximum + date of a week from now. + + Terminology: A QDateEdit widget consists of three 'sections', one + each for the year, month and day. Similarly a QTimeEdit consists + of three sections, one each for the hour, minute and second. The + character that separates each date section is specified with + setDateSeparator(); similarly setTimeSeparator() is used for the + time sections. + + \img datetimewidgets.png Date Time Widgets + + \sa QDateEdit QTimeEdit +*/ + +/*! + Constructs an empty datetime edit with parent \a parent and called + \a name. +*/ +QDateTimeEdit::QDateTimeEdit( QWidget * parent, const char * name ) + : QWidget( parent, name ) +{ + init(); +} + + +/*! + \overload + + Constructs a datetime edit with the initial value \a datetime, + parent \a parent and called \a name. +*/ +QDateTimeEdit::QDateTimeEdit( const QDateTime& datetime, + QWidget * parent, const char * name ) + : QWidget( parent, name ) +{ + init(); + setDateTime( datetime ); +} + + + +/*! + Destroys the object and frees any allocated resources. +*/ + +QDateTimeEdit::~QDateTimeEdit() +{ + delete d; +} + + +/*! + \reimp + + Intercepts and handles resize events which have special meaning + for the QDateTimeEdit. +*/ + +void QDateTimeEdit::resizeEvent( QResizeEvent * ) +{ + int dw = de->sizeHint().width(); + int tw = te->sizeHint().width(); + int w = width(); + int h = height(); + int extra = w - ( dw + tw ); + + if ( tw + extra < 0 ) { + dw = w; + } else { + dw += 9 * extra / 16; + } + tw = w - dw; + + de->setGeometry( 0, 0, dw, h ); + te->setGeometry( dw, 0, tw, h ); +} + +/*! \reimp +*/ + +QSize QDateTimeEdit::minimumSizeHint() const +{ + QSize dsh = de->minimumSizeHint(); + QSize tsh = te->minimumSizeHint(); + return QSize( dsh.width() + tsh.width(), + QMAX( dsh.height(), tsh.height() ) ); +} + +/*! \internal + */ + +void QDateTimeEdit::init() +{ + d = new QDateTimeEditPrivate(); + de = new QDateEdit( this, "qt_datetime_dateedit" ); + te = new QTimeEdit( this, "qt_datetime_timeedit" ); + d->adv = FALSE; + connect( de, SIGNAL( valueChanged(const QDate&) ), + this, SLOT( newValue(const QDate&) ) ); + connect( te, SIGNAL( valueChanged(const QTime&) ), + this, SLOT( newValue(const QTime&) ) ); + setFocusProxy( de ); + setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ); +} + +/*! \reimp + */ + +QSize QDateTimeEdit::sizeHint() const +{ + constPolish(); + QSize dsh = de->sizeHint(); + QSize tsh = te->sizeHint(); + return QSize( dsh.width() + tsh.width(), + QMAX( dsh.height(), tsh.height() ) ); +} + +/*! + \property QDateTimeEdit::dateTime + \brief the editor's datetime value + + The datetime edit's datetime which may be an invalid datetime. +*/ + +void QDateTimeEdit::setDateTime( const QDateTime & dt ) +{ + if ( dt.isValid() ) { + de->setDate( dt.date() ); + te->setTime( dt.time() ); + emit valueChanged( dt ); + } +} + +QDateTime QDateTimeEdit::dateTime() const +{ + return QDateTime( de->date(), te->time() ); +} + +/*! + \fn void QDateTimeEdit::valueChanged( const QDateTime& datetime ) + + This signal is emitted every time the date or time changes. The \a + datetime argument is the new datetime. +*/ + + +/*! \internal + + Re-emits the value \a d. + */ + +void QDateTimeEdit::newValue( const QDate& ) +{ + QDateTime dt = dateTime(); + emit valueChanged( dt ); +} + +/*! \internal + \overload + Re-emits the value \a t. + */ + +void QDateTimeEdit::newValue( const QTime& ) +{ + QDateTime dt = dateTime(); + emit valueChanged( dt ); +} + + +/*! + Sets the auto advance property of the editor to \a advance. If set + to TRUE, the editor will automatically advance focus to the next + date or time section if the user has completed a section. +*/ + +void QDateTimeEdit::setAutoAdvance( bool advance ) +{ + de->setAutoAdvance( advance ); + te->setAutoAdvance( advance ); +} + +/*! + Returns TRUE if auto-advance is enabled, otherwise returns FALSE. + + \sa setAutoAdvance() +*/ + +bool QDateTimeEdit::autoAdvance() const +{ + return de->autoAdvance(); +} + +/*! + \fn QDateEdit* QDateTimeEdit::dateEdit() + + Returns the internal widget used for editing the date part of the + datetime. +*/ + +/*! + \fn QTimeEdit* QDateTimeEdit::timeEdit() + + Returns the internal widget used for editing the time part of the + datetime. +*/ + +#include "qdatetimeedit.moc" + +#endif diff --git a/src/widgets/qdatetimeedit.h b/src/widgets/qdatetimeedit.h new file mode 100644 index 0000000..b61781e --- /dev/null +++ b/src/widgets/qdatetimeedit.h @@ -0,0 +1,293 @@ +/**************************************************************************** +** +** Definition of date and time edit classes +** +** Created : 001103 +** +** Copyright (C) 2005-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. +** +**********************************************************************/ + +#ifndef QDATETIMEEDIT_H +#define QDATETIMEEDIT_H + +#ifndef QT_H +#include "qwidget.h" +#include "qstring.h" +#include "qdatetime.h" +#endif // QT_H + +#ifndef QT_NO_DATETIMEEDIT + +class Q_EXPORT QDateTimeEditBase : public QWidget +{ + Q_OBJECT +public: + QDateTimeEditBase( QWidget* parent=0, const char* name=0 ) + : QWidget( parent, name ) {} + + virtual bool setFocusSection( int sec ) = 0; + virtual QString sectionFormattedText( int sec ) = 0; + virtual void addNumber( int sec, int num ) = 0; + virtual void removeLastNumber( int sec ) = 0; + +public slots: + virtual void stepUp() = 0; + virtual void stepDown() = 0; + +private: +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + QDateTimeEditBase( const QDateTimeEditBase & ); + QDateTimeEditBase &operator=( const QDateTimeEditBase & ); +#endif +}; + +class QDateEditPrivate; + +class Q_EXPORT QDateEdit : public QDateTimeEditBase +{ + Q_OBJECT + Q_ENUMS( Order ) + Q_PROPERTY( Order order READ order WRITE setOrder ) + Q_PROPERTY( QDate date READ date WRITE setDate ) + Q_PROPERTY( bool autoAdvance READ autoAdvance WRITE setAutoAdvance ) + Q_PROPERTY( QDate maxValue READ maxValue WRITE setMaxValue ) + Q_PROPERTY( QDate minValue READ minValue WRITE setMinValue ) + +public: + QDateEdit( QWidget* parent=0, const char* name=0 ); + QDateEdit( const QDate& date, QWidget* parent=0, const char* name=0 ); + ~QDateEdit(); + + enum Order { DMY, MDY, YMD, YDM }; + + QSize sizeHint() const; + QSize minimumSizeHint() const; + +public slots: + virtual void setDate( const QDate& date ); + +public: + QDate date() const; + virtual void setOrder( Order order ); + Order order() const; + virtual void setAutoAdvance( bool advance ); + bool autoAdvance() const; + + virtual void setMinValue( const QDate& d ) { setRange( d, maxValue() ); } + QDate minValue() const; + virtual void setMaxValue( const QDate& d ) { setRange( minValue(), d ); } + QDate maxValue() const; + virtual void setRange( const QDate& min, const QDate& max ); + QString separator() const; + virtual void setSeparator( const QString& s ); + + // Make removeFirstNumber() virtual in QDateTimeEditBase in 4.0 + void removeFirstNumber( int sec ); + +signals: + void valueChanged( const QDate& date ); + +protected: + bool event( QEvent *e ); + void timerEvent( QTimerEvent * ); + void resizeEvent( QResizeEvent * ); + void stepUp(); + void stepDown(); + QString sectionFormattedText( int sec ); + void addNumber( int sec, int num ); + + void removeLastNumber( int sec ); + bool setFocusSection( int s ); + + virtual void setYear( int year ); + virtual void setMonth( int month ); + virtual void setDay( int day ); + virtual void fix(); + virtual bool outOfRange( int y, int m, int d ) const; + +protected slots: + void updateButtons(); + +private: + void init(); + int sectionOffsetEnd( int sec ) const; + int sectionLength( int sec ) const; + QString sectionText( int sec ) const; + QDateEditPrivate* d; + +#if defined(Q_DISABLE_COPY) + QDateEdit( const QDateEdit & ); + QDateEdit &operator=( const QDateEdit & ); +#endif +}; + +class QTimeEditPrivate; + +class Q_EXPORT QTimeEdit : public QDateTimeEditBase +{ + Q_OBJECT + Q_SETS( Display ) + Q_PROPERTY( QTime time READ time WRITE setTime ) + Q_PROPERTY( bool autoAdvance READ autoAdvance WRITE setAutoAdvance ) + Q_PROPERTY( QTime maxValue READ maxValue WRITE setMaxValue ) + Q_PROPERTY( QTime minValue READ minValue WRITE setMinValue ) + Q_PROPERTY( Display display READ display WRITE setDisplay ) + +public: + enum Display { + Hours = 0x01, + Minutes = 0x02, + Seconds = 0x04, + /*Reserved = 0x08,*/ + AMPM = 0x10 + }; + + QTimeEdit( QWidget* parent=0, const char* name=0 ); + QTimeEdit( const QTime& time, QWidget* parent=0, const char* name=0 ); + ~QTimeEdit(); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + +public slots: + virtual void setTime( const QTime& time ); + +public: + QTime time() const; + virtual void setAutoAdvance( bool advance ); + bool autoAdvance() const; + + virtual void setMinValue( const QTime& d ) { setRange( d, maxValue() ); } + QTime minValue() const; + virtual void setMaxValue( const QTime& d ) { setRange( minValue(), d ); } + QTime maxValue() const; + virtual void setRange( const QTime& min, const QTime& max ); + QString separator() const; + virtual void setSeparator( const QString& s ); + + uint display() const; + void setDisplay( uint disp ); + + // Make removeFirstNumber() virtual in QDateTimeEditBase in 4.0 + void removeFirstNumber( int sec ); + +signals: + void valueChanged( const QTime& time ); + +protected: + bool event( QEvent *e ); + void timerEvent( QTimerEvent *e ); + void resizeEvent( QResizeEvent * ); + void stepUp(); + void stepDown(); + QString sectionFormattedText( int sec ); + void addNumber( int sec, int num ); + void removeLastNumber( int sec ); + bool setFocusSection( int s ); + + virtual bool outOfRange( int h, int m, int s ) const; + virtual void setHour( int h ); + virtual void setMinute( int m ); + virtual void setSecond( int s ); + +protected slots: + void updateButtons(); + +private: + void init(); + QString sectionText( int sec ); + QTimeEditPrivate* d; + +#if defined(Q_DISABLE_COPY) + QTimeEdit( const QTimeEdit & ); + QTimeEdit &operator=( const QTimeEdit & ); +#endif +}; + + +class QDateTimeEditPrivate; + +class Q_EXPORT QDateTimeEdit : public QWidget +{ + Q_OBJECT + Q_PROPERTY( QDateTime dateTime READ dateTime WRITE setDateTime ) + +public: + QDateTimeEdit( QWidget* parent=0, const char* name=0 ); + QDateTimeEdit( const QDateTime& datetime, QWidget* parent=0, + const char* name=0 ); + ~QDateTimeEdit(); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + +public slots: + virtual void setDateTime( const QDateTime & dt ); + +public: + QDateTime dateTime() const; + + QDateEdit* dateEdit() { return de; } + QTimeEdit* timeEdit() { return te; } + + virtual void setAutoAdvance( bool advance ); + bool autoAdvance() const; + +signals: + void valueChanged( const QDateTime& datetime ); + +protected: + // ### make init() private in Qt 4.0 + void init(); + void resizeEvent( QResizeEvent * ); + +protected slots: + // ### make these two functions private in Qt 4.0, + // and merge them into one with no parameter + void newValue( const QDate& d ); + void newValue( const QTime& t ); + +private: + QDateEdit* de; + QTimeEdit* te; + QDateTimeEditPrivate* d; + +#if defined(Q_DISABLE_COPY) + QDateTimeEdit( const QDateTimeEdit & ); + QDateTimeEdit &operator=( const QDateTimeEdit & ); +#endif +}; + +#endif +#endif diff --git a/src/widgets/qdial.cpp b/src/widgets/qdial.cpp new file mode 100644 index 0000000..40041cd --- /dev/null +++ b/src/widgets/qdial.cpp @@ -0,0 +1,976 @@ +/**************************************************************************** +** +** Implementation of the dial widget +** +** Created : 979899 +** +** 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 "qdial.h" + +#ifndef QT_NO_DIAL + +#include "qpainter.h" +#include "qpointarray.h" +#include "qcolor.h" +#include "qapplication.h" +#include "qregion.h" +#include "qbitmap.h" +#include "qstyle.h" +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif + +#include <math.h> // sin(), cos(), atan() +//### Forutsetter linking med math lib - Jfr kommentar i qpainter_x11.cpp! + +static const double m_pi = 3.14159265358979323846; +static const double rad_factor = 180.0 / m_pi; + + +class QDialPrivate +{ +public: + QDialPrivate() + { + wrapping = FALSE; + tracking = TRUE; + doNotEmit = FALSE; + target = 3.7; + mousePressed = FALSE; + } + + bool wrapping; + bool tracking; + bool doNotEmit; + double target; + QRect eraseArea; + bool eraseAreaValid; + bool showNotches; + bool onlyOutside; + bool mousePressed; + + QPointArray lines; +}; + + +/*! + \class QDial qdial.h + + \brief The QDial class provides a rounded range control (like a speedometer or potentiometer). + + \ingroup basic + \mainclass + + QDial is used when the user needs to control a value within a + program-definable range, and the range either wraps around + (typically, 0..359 degrees) or the dialog layout needs a square + widget. + + Both API- and UI-wise, the dial is very similar to a \link QSlider + slider. \endlink Indeed, when wrapping() is FALSE (the default) + there is no real difference between a slider and a dial. They + have the same signals, slots and member functions, all of which do + the same things. Which one you use depends only on your taste + and on the application. + + The dial initially emits valueChanged() signals continuously while + the slider is being moved; you can make it emit the signal less + often by calling setTracking(FALSE). dialMoved() is emitted + continuously even when tracking() is FALSE. + + The slider also emits dialPressed() and dialReleased() signals + when the mouse button is pressed and released. But note that the + dial's value can change without these signals being emitted; the + keyboard and wheel can be used to change the value. + + Unlike the slider, QDial attempts to draw a "nice" number of + notches rather than one per lineStep(). If possible, the number + of notches drawn is one per lineStep(), but if there aren't enough + pixels to draw every one, QDial will draw every second, third + etc., notch. notchSize() returns the number of units per notch, + hopefully a multiple of lineStep(); setNotchTarget() sets the + target distance between neighbouring notches in pixels. The + default is 3.75 pixels. + + Like the slider, the dial makes the QRangeControl functions + setValue(), addLine(), subtractLine(), addPage() and + subtractPage() available as slots. + + The dial's keyboard interface is fairly simple: The left/up and + right/down arrow keys move by lineStep(), page up and page down by + pageStep() and Home and End to minValue() and maxValue(). + + <img src=qdial-m.png> <img src=qdial-w.png> + + \sa QScrollBar QSpinBox + \link guibooks.html#fowler GUI Design Handbook: Slider\endlink +*/ + + + + +/*! + Constructs a dial called \a name with parent \a parent. \a f is + propagated to the QWidget constructor. It has the default range of + a QRangeControl. +*/ + +QDial::QDial( QWidget *parent, const char *name, WFlags f ) + : QWidget( parent, name, f | WNoAutoErase ), QRangeControl() +{ + d = new QDialPrivate; + d->eraseAreaValid = FALSE; + d->showNotches = FALSE; + d->onlyOutside = FALSE; + setFocusPolicy( QWidget::WheelFocus ); +} + + + +/*! + Constructs a dial called \a name with parent \a parent. The dial's + value can never be smaller than \a minValue or greater than \a + maxValue. Its page step size is \a pageStep, and its initial value + is \a value. + + \a value is forced to be within the legal range. +*/ + +QDial::QDial( int minValue, int maxValue, int pageStep, int value, + QWidget *parent, const char *name ) + : QWidget( parent, name, WNoAutoErase ), + QRangeControl( minValue, maxValue, 1, pageStep, value ) +{ + d = new QDialPrivate; + d->eraseAreaValid = FALSE; + d->showNotches = FALSE; + d->onlyOutside = FALSE; + setFocusPolicy( QWidget::WheelFocus ); +} + +/*! + Destroys the dial. +*/ +QDial::~QDial() +{ + delete d; +} + + +void QDial::setTracking( bool enable ) +{ + d->tracking = enable; +} + + +/*! + \property QDial::tracking + \brief whether tracking is enabled + + If TRUE (the default), tracking is enabled. This means that the + arrow can be moved using the mouse; otherwise the arrow cannot be + moved with the mouse. +*/ + +bool QDial::tracking() const +{ + return d ? d->tracking : TRUE; +} + +void QDial::setValue( int newValue ) +{ // ### set doNotEmit? Matthias? + QRangeControl::setValue( newValue ); +} + + +/*! + Increments the dial's value() by one lineStep(). +*/ + +void QDial::addLine() +{ + QRangeControl::addLine(); +} + + +/*! + Decrements the dial's value() by one lineStep(). +*/ + +void QDial::subtractLine() +{ + QRangeControl::subtractLine(); +} + + +/*! \reimp */ + +void QDial::resizeEvent( QResizeEvent * e ) +{ + d->lines.resize( 0 ); + QWidget::resizeEvent( e ); +} + + +/*! + \reimp +*/ + +void QDial::paintEvent( QPaintEvent * e ) +{ + repaintScreen( &e->rect() ); +} + +/*! + Paints the dial using clip region \a cr. +*/ + +void QDial::repaintScreen( const QRect *cr ) +{ + QPainter p; + p.begin( this ); + + bool resetClipping = FALSE; + + // calculate clip-region for erasing background + if ( cr ) { + p.setClipRect( *cr ); + } else if ( !d->onlyOutside && d->eraseAreaValid ) { + QRegion reg = d->eraseArea; + double a; + reg = reg.subtract( calcArrow( a ) ); + p.setClipRegion( reg ); + resetClipping = TRUE; + } + + QRect br( calcDial() ); + p.setPen( NoPen ); + // if ( style() == MotifStyle ) + // p.setBrush( colorGroup().brush( QColorGroup::Mid ) ); + // else { + QBrush b; + if ( colorGroup().brush( QColorGroup::Light ).pixmap() ) + b = QBrush( colorGroup().brush( QColorGroup::Light ) ); + else + b = QBrush( colorGroup().light(), Dense4Pattern ); + p.setBrush( b ); + p.setBackgroundMode( OpaqueMode ); + // } + + QRect te = br; + te.setWidth(te.width()+2); + te.setHeight(te.height()+2); + // erase background of dial + if ( !d->onlyOutside ) { + p.drawEllipse( te ); + } + + // erase remaining space around the dial + QRegion remaining( 0, 0, width(), height() ); + remaining = remaining.subtract( QRegion( te, QRegion::Ellipse ) ); + if ( p.hasClipping() ) + remaining = remaining.intersect( p.clipRegion() ); + erase(remaining); + + if ( resetClipping ) { + if ( cr ) + p.setClipRect( *cr ); + else + p.setClipRect( QRect( 0, 0, width(), height() ) ); + } + + // draw notches + if ( d->showNotches ) { + calcLines(); + p.setPen( colorGroup().foreground() ); + p.drawLineSegments( d->lines ); + } + + // calculate and paint arrow + p.setPen( QPen( colorGroup().dark() ) ); + p.drawArc( te, 60 * 16, 180 * 16 ); + p.setPen( QPen( colorGroup().light() ) ); + p.drawArc( te, 240 * 16, 180 * 16 ); + + double a; + QPointArray arrow( calcArrow( a ) ); + QRect ea( arrow.boundingRect() ); + d->eraseArea = ea; + d->eraseAreaValid = TRUE; + + p.setPen( NoPen ); + p.setBrush( colorGroup().brush( QColorGroup::Button ) ); + if ( !d->onlyOutside ) + p.drawPolygon( arrow ); + + a = angle( QPoint( width() / 2, height() / 2 ), arrow[ 0 ] ); + p.setBrush( Qt::NoBrush ); + + // that's still a hack... + if ( a <= 0 || a > 200 ) { + p.setPen( colorGroup().light() ); + p.drawLine( arrow[ 2 ], arrow[ 0 ] ); + p.drawLine( arrow[ 1 ], arrow[ 2 ] ); + p.setPen( colorGroup().dark() ); + p.drawLine( arrow[ 0 ], arrow[ 1 ] ); + } else if ( a > 0 && a < 45 ) { + p.setPen( colorGroup().light() ); + p.drawLine( arrow[ 2 ], arrow[ 0 ] ); + p.setPen( colorGroup().dark() ); + p.drawLine( arrow[ 1 ], arrow[ 2 ] ); + p.drawLine( arrow[ 0 ], arrow[ 1 ] ); + } else if ( a >= 45 && a < 135 ) { + p.setPen( colorGroup().dark() ); + p.drawLine( arrow[ 2 ], arrow[ 0 ] ); + p.drawLine( arrow[ 1 ], arrow[ 2 ] ); + p.setPen( colorGroup().light() ); + p.drawLine( arrow[ 0 ], arrow[ 1 ] ); + } else if ( a >= 135 && a < 200 ) { + p.setPen( colorGroup().dark() ); + p.drawLine( arrow[ 2 ], arrow[ 0 ] ); + p.setPen( colorGroup().light() ); + p.drawLine( arrow[ 0 ], arrow[ 1 ] ); + p.drawLine( arrow[ 1 ], arrow[ 2 ] ); + } + + // draw focus rect around the dial + if ( hasFocus() ) { + p.setClipping( FALSE ); + br.setWidth( br.width() + 2 ); + br.setHeight( br.height() + 2 ); + if ( d->showNotches ) { + int r = QMIN( width(), height() ) / 2; + br.moveBy( -r / 6, - r / 6 ); + br.setWidth( br.width() + r / 3 ); + br.setHeight( br.height() + r / 3 ); + } + // strange, but else we get redraw errors on Windows + p.end(); + p.begin( this ); + p.save(); + p.setPen( QPen( colorGroup().background() ) ); + p.setBrush( NoBrush ); + p.drawRect( br ); + p.restore(); + style().drawPrimitive( QStyle::PE_FocusRect, &p, br, colorGroup()); + } + p.end(); +} + + +/*! + \reimp +*/ + +void QDial::keyPressEvent( QKeyEvent * e ) +{ + switch ( e->key() ) { + case Key_Left: case Key_Down: + subtractLine(); + break; + case Key_Right: case Key_Up: + addLine(); + break; + case Key_Prior: + subtractPage(); + break; + case Key_Next: + addPage(); + break; + case Key_Home: + setValue( minValue() ); + break; + case Key_End: + setValue( maxValue() ); + break; + default: + e->ignore(); + break; + } +} + + +/*! + \reimp +*/ + +void QDial::mousePressEvent( QMouseEvent * e ) +{ + d->mousePressed = TRUE; + setValue( valueFromPoint( e->pos() ) ); + emit dialPressed(); +} + + +/*! + \reimp +*/ + +void QDial::mouseReleaseEvent( QMouseEvent * e ) +{ + d->mousePressed = FALSE; + setValue( valueFromPoint( e->pos() ) ); + emit dialReleased(); +} + + +/*! + \reimp +*/ + +void QDial::mouseMoveEvent( QMouseEvent * e ) +{ + if ( !d->mousePressed ) + return; + if ( !d->tracking || (e->state() & LeftButton) == 0 ) + return; + d->doNotEmit = TRUE; + setValue( valueFromPoint( e->pos() ) ); + emit dialMoved( value() ); + d->doNotEmit = FALSE; +} + + +/*! + \reimp +*/ +#ifndef QT_NO_WHEELEVENT +void QDial::wheelEvent( QWheelEvent *e ) +{ + setValue( value() - e->delta() / 120 ); +} +#endif + +/*! + \reimp +*/ + +void QDial::focusInEvent( QFocusEvent * ) +{ + d->onlyOutside = TRUE; + repaintScreen(); + d->onlyOutside = FALSE; +} + + +/*! + \reimp +*/ + +void QDial::focusOutEvent( QFocusEvent * ) +{ + d->onlyOutside = TRUE; + repaintScreen(); + d->onlyOutside = FALSE; +} + +/*! + Reimplemented to ensure the display is correct and to emit the + valueChanged(int) signal when appropriate. +*/ + +void QDial::valueChange() +{ + d->lines.resize( 0 ); + repaintScreen(); + if ( d->tracking || !d->doNotEmit ) { + emit valueChanged( value() ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged ); +#endif + } +} + + +/*! + Reimplemented to ensure tick-marks are consistent with the new range. +*/ + +void QDial::rangeChange() +{ + d->lines.resize( 0 ); + repaintScreen(); +} + + +/*! + \internal +*/ + +int QDial::valueFromPoint( const QPoint & p ) const +{ + double yy = (double)height()/2.0 - p.y(); + double xx = (double)p.x() - width()/2.0; + double a = (xx || yy) ? atan2(yy, xx) : 0; + + if ( a < m_pi/-2 ) + a = a + m_pi*2; + + int dist = 0; + int minv = minValue(), maxv = maxValue(); + + if ( minValue() < 0 ) { + dist = -minValue(); + minv = 0; + maxv = maxValue() + dist; + } + + int r = maxv - minv; + int v; + if ( d->wrapping ) + v = (int)(0.5 + minv + r*(m_pi*3/2-a)/(2*m_pi)); + else + v = (int)(0.5 + minv + r*(m_pi*4/3-a)/(m_pi*10/6)); + + if ( dist > 0 ) + v -= dist; + + return bound( v ); +} + + +/*! + \internal +*/ + +double QDial::angle( const QPoint &p1, const QPoint &p2 ) const +{ + double _angle = 0.0; + + if ( p1.x() == p2.x() ) { + if ( p1.y() < p2.y() ) + _angle = 270.0; + else + _angle = 90.0; + } else { + double x1, x2, y1, y2; + + if ( p1.x() <= p2.x() ) { + x1 = p1.x(); y1 = p1.y(); + x2 = p2.x(); y2 = p2.y(); + } else { + x2 = p1.x(); y2 = p1.y(); + x1 = p2.x(); y1 = p2.y(); + } + + double m = -( y2 - y1 ) / ( x2 - x1 ); + _angle = atan( m ) * rad_factor; + + if ( p1.x() < p2.x() ) + _angle = 180.0 - _angle; + else + _angle = -_angle; + } + + return _angle; +} + +void QDial::setWrapping( bool enable ) +{ + if ( d->wrapping == enable ) + return; + d->lines.resize( 0 ); + d->wrapping = enable; + d->eraseAreaValid = FALSE; + repaintScreen(); +} + + +/*! + \property QDial::wrapping + \brief whether wrapping is enabled + + If TRUE, wrapping is enabled. This means that the arrow can be + turned around 360°. Otherwise there is some space at the bottom of + the dial which is skipped by the arrow. + + This property's default is FALSE. +*/ + +bool QDial::wrapping() const +{ + return d->wrapping; +} + + +/*! + \property QDial::notchSize + \brief the current notch size + + The notch size is in range control units, not pixels, and if + possible it is a multiple of lineStep() that results in an + on-screen notch size near notchTarget(). + + \sa notchTarget() lineStep() +*/ + +int QDial::notchSize() const +{ + // radius of the arc + int r = QMIN( width(), height() )/2; + // length of the whole arc + int l = (int)(r*(d->wrapping ? 6 : 5)*m_pi/6); + // length of the arc from minValue() to minValue()+pageStep() + if ( maxValue() > minValue()+pageStep() ) + l = (int)(0.5 + l * pageStep() / (maxValue()-minValue())); + // length of a lineStep() arc + l = l * lineStep() / pageStep(); + if ( l < 1 ) + l = 1; + // how many times lineStep can be draw in d->target pixels + l = (int)(0.5 + d->target / l); + // we want notchSize() to be a non-zero multiple of lineStep() + if ( !l ) + l = 1; + return lineStep() * l; +} + +void QDial::setNotchTarget( double target ) +{ + d->lines.resize( 0 ); + d->target = target; + d->eraseAreaValid = FALSE; + d->onlyOutside = TRUE; + repaintScreen(); + d->onlyOutside = FALSE; +} + + +/*! + \property QDial::notchTarget + \brief the target number of pixels between notches + + The notch target is the number of pixels QDial attempts to put + between each notch. + + The actual size may differ from the target size. +*/ + +double QDial::notchTarget() const +{ + return d->target; +} + + +/*! + Increments the dial's value() by one pageStep() of steps. +*/ + +void QDial::addPage() +{ + QRangeControl::addPage(); +} + + +/*! + Decrements the dial's value() by one pageStep() of steps. +*/ + +void QDial::subtractPage() +{ + QRangeControl::subtractPage(); +} + + +/*! + \fn void QDial::valueChanged( int value ) + + This signal is emitted whenever the dial's \a value changes. The + frequency of this signal is influenced by setTracking(). +*/ + +/*! + \fn void QDial::dialPressed() + + This signal is emitted when the user begins mouse interaction with + the dial. + + \sa dialReleased() +*/ + +/*! + \fn void QDial::dialMoved( int value ) + + This signal is emitted whenever the dial \a value changes. The + frequency of this signal is \e not influenced by setTracking(). + + \sa valueChanged() +*/ + +/*! + \fn void QDial::dialReleased() + + This signal is emitted when the user ends mouse interaction with + the dial. + + \sa dialPressed() +*/ + +void QDial::setNotchesVisible( bool b ) +{ + d->showNotches = b; + d->eraseAreaValid = FALSE; + d->onlyOutside = TRUE; + repaintScreen(); + d->onlyOutside = FALSE; +} + +/*! + \property QDial::notchesVisible + \brief whether the notches are shown + + If TRUE, the notches are shown. If FALSE (the default) notches are + not shown. +*/ +bool QDial::notchesVisible() const +{ + return d->showNotches; +} + +/*! + \reimp +*/ + +QSize QDial::minimumSizeHint() const +{ + return QSize( 50, 50 ); +} + +/*! + \reimp +*/ + +QSize QDial::sizeHint() const +{ + return QSize( 100, 100 ).expandedTo( QApplication::globalStrut() ); +} + + + +/*! + \internal +*/ + +QPointArray QDial::calcArrow( double &a ) const +{ + int r = QMIN( width(), height() ) / 2; + if ( maxValue() == minValue() ) + a = m_pi / 2; + else if ( d->wrapping ) + a = m_pi * 3 / 2 - ( value() - minValue() ) * 2 * m_pi / ( maxValue() - minValue() ); + else + a = ( m_pi * 8 - ( value() - minValue() ) * 10 * m_pi / ( maxValue() - minValue() ) ) / 6; + + int xc = width() / 2; + int yc = height() / 2; + + int len = r - calcBigLineSize() - 5; + if ( len < 5 ) + len = 5; + int back = len / 4; + if ( back < 1 ) + back = 1; + + QPointArray arrow( 3 ); + arrow[0] = QPoint( (int)( 0.5 + xc + len * cos(a) ), + (int)( 0.5 + yc -len * sin( a ) ) ); + arrow[1] = QPoint( (int)( 0.5 + xc + back * cos( a + m_pi * 5 / 6 ) ), + (int)( 0.5 + yc - back * sin( a + m_pi * 5 / 6 ) ) ); + arrow[2] = QPoint( (int)( 0.5 + xc + back * cos( a - m_pi * 5 / 6 ) ), + (int)( 0.5 + yc - back * sin( a - m_pi * 5 / 6 ) ) ); + return arrow; +} + +/*! + \internal +*/ + +QRect QDial::calcDial() const +{ + double r = QMIN( width(), height() ) / 2.0; + double d_ = r / 6.0; + double dx = d_ + ( width() - 2 * r ) / 2.0 + 1; + double dy = d_ + ( height() - 2 * r ) / 2.0 + 1; + return QRect( int(dx), int(dy), + int(r * 2 - 2 * d_ - 2), int(r * 2 - 2 * d_ - 2) ); +} + +/*! + \internal +*/ + +int QDial::calcBigLineSize() const +{ + int r = QMIN( width(), height() ) / 2; + int bigLineSize = r / 6; + if ( bigLineSize < 4 ) + bigLineSize = 4; + if ( bigLineSize > r / 2 ) + bigLineSize = r / 2; + return bigLineSize; +} + +/*! + \internal +*/ + +void QDial::calcLines() +{ + if ( !d->lines.size() ) { + double r = QMIN( width(), height() ) / 2.0; + int bigLineSize = calcBigLineSize(); + double xc = width() / 2.0; + double yc = height() / 2.0; + int ns = notchSize(); + int notches = ( maxValue() + ns - 1 - minValue() ) / ns; + d->lines.resize( 2 + 2 * notches ); + int smallLineSize = bigLineSize / 2; + int i; + for( i = 0; i <= notches; i++ ) { + double angle = d->wrapping + ? m_pi * 3 / 2 - i * 2 * m_pi / notches + : (m_pi * 8 - i * 10 * m_pi / notches) / 6; + + double s = sin( angle ); // sin/cos aren't defined as const... + double c = cos( angle ); + if ( i == 0 || ( ((ns * i ) % pageStep() ) == 0 ) ) { + d->lines[2*i] = QPoint( (int)( xc + ( r - bigLineSize ) * c ), + (int)( yc - ( r - bigLineSize ) * s ) ); + d->lines[2*i+1] = QPoint( (int)( xc + r * c ), + (int)( yc - r * s ) ); + } else { + d->lines[2*i] = QPoint( (int)( xc + ( r - 1 - smallLineSize ) * c ), + (int)( yc - ( r - 1 - smallLineSize ) * s ) ); + d->lines[2*i+1] = QPoint( (int)( xc + ( r - 1 ) * c ), + (int)( yc -( r - 1 ) * s ) ); + } + } + } +} + +/*! + \property QDial::minValue + \brief the current minimum value + + When setting this property, the \l QDial::maxValue is adjusted if + necessary to ensure that the range remains valid. + + \sa setRange() +*/ +int QDial::minValue() const +{ + return QRangeControl::minValue(); +} + +/*! + \property QDial::maxValue + \brief the current maximum value + + When setting this property, the \l QDial::minValue is adjusted if + necessary to ensure that the range remains valid. + + \sa setRange() +*/ +int QDial::maxValue() const +{ + return QRangeControl::maxValue(); +} + +void QDial::setMinValue( int minVal ) +{ + QRangeControl::setMinValue( minVal ); +} + +void QDial::setMaxValue( int maxVal ) +{ + QRangeControl::setMaxValue( maxVal ); +} + +/*! + \property QDial::lineStep + \brief the current line step + + setLineStep() calls the virtual stepChange() function if the new + line step is different from the previous setting. + + \sa QRangeControl::setSteps() pageStep setRange() +*/ + +int QDial::lineStep() const +{ + return QRangeControl::lineStep(); +} + +/*! + \property QDial::pageStep + \brief the current page step + + setPageStep() calls the virtual stepChange() function if the new + page step is different from the previous setting. + + \sa stepChange() +*/ +int QDial::pageStep() const +{ + return QRangeControl::pageStep(); +} + +void QDial::setLineStep( int i ) +{ + setSteps( i, pageStep() ); +} + +void QDial::setPageStep( int i ) +{ + setSteps( lineStep(), i ); +} + +/*! + \property QDial::value + \brief the current dial value + + This is guaranteed to be within the range + \l{QDial::minValue}..\l{QDial::maxValue}. + + \sa minValue maxValue +*/ + +int QDial::value() const +{ + return QRangeControl::value(); +} + +#endif // QT_FEATURE_DIAL diff --git a/src/widgets/qdial.h b/src/widgets/qdial.h new file mode 100644 index 0000000..3249b2c --- /dev/null +++ b/src/widgets/qdial.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Definition of the dial widget +** +** Created : 990104 +** +** Copyright (C) 1999-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. +** +**********************************************************************/ + + +#ifndef QDIAL_H +#define QDIAL_H + +#ifndef QT_H +#include "qwidget.h" +#include "qrangecontrol.h" +#endif // QT_H + +#ifndef QT_NO_DIAL + +class QDialPrivate; + +class Q_EXPORT QDial: public QWidget, public QRangeControl +{ + Q_OBJECT + Q_PROPERTY( bool tracking READ tracking WRITE setTracking ) + Q_PROPERTY( bool wrapping READ wrapping WRITE setWrapping ) + Q_PROPERTY( int notchSize READ notchSize ) + Q_PROPERTY( double notchTarget READ notchTarget WRITE setNotchTarget ) + Q_PROPERTY( bool notchesVisible READ notchesVisible WRITE setNotchesVisible ) + Q_PROPERTY( int minValue READ minValue WRITE setMinValue ) + Q_PROPERTY( int maxValue READ maxValue WRITE setMaxValue ) + Q_PROPERTY( int lineStep READ lineStep WRITE setLineStep ) + Q_PROPERTY( int pageStep READ pageStep WRITE setPageStep ) + Q_PROPERTY( int value READ value WRITE setValue ) + +public: + QDial( QWidget* parent=0, const char* name=0, WFlags f = 0 ); + QDial( int minValue, int maxValue, int pageStep, int value, + QWidget* parent=0, const char* name=0 ); + ~QDial(); + + bool tracking() const; + + bool wrapping() const; + + int notchSize() const; + + virtual void setNotchTarget( double ); + double notchTarget() const; + + bool notchesVisible() const; + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + int minValue() const; + int maxValue() const; + void setMinValue( int ); + void setMaxValue( int ); + int lineStep() const; + int pageStep() const; + void setLineStep( int ); + void setPageStep( int ); + int value() const; + +public slots: + virtual void setValue( int ); + void addLine(); + void subtractLine(); + void addPage(); + void subtractPage(); + virtual void setNotchesVisible( bool b ); + virtual void setWrapping( bool on ); + virtual void setTracking( bool enable ); + +signals: + void valueChanged( int value ); + void dialPressed(); + void dialMoved( int value ); + void dialReleased(); + +protected: + void resizeEvent( QResizeEvent * ); + void paintEvent( QPaintEvent * ); + + void keyPressEvent( QKeyEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); +#ifndef QT_NO_WHEELEVENT + void wheelEvent( QWheelEvent * ); +#endif + void focusInEvent( QFocusEvent * ); + void focusOutEvent( QFocusEvent * ); + + void valueChange(); + void rangeChange(); + + virtual void repaintScreen( const QRect *cr = 0 ); + +private: + QDialPrivate * d; + + int valueFromPoint( const QPoint & ) const; + double angle( const QPoint &, const QPoint & ) const; + QPointArray calcArrow( double &a ) const; + QRect calcDial() const; + int calcBigLineSize() const; + void calcLines(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QDial( const QDial & ); + QDial &operator=( const QDial & ); +#endif + +}; + +#endif // QT_NO_DIAL + +#endif diff --git a/src/widgets/qdialogbuttons.cpp b/src/widgets/qdialogbuttons.cpp new file mode 100644 index 0000000..787a406 --- /dev/null +++ b/src/widgets/qdialogbuttons.cpp @@ -0,0 +1,456 @@ +/**************************************************************************** +** +** Implementation of QDialogButtons class +** +** Copyright (C) 2005-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 "qdialogbuttons_p.h" +#ifndef QT_NO_DIALOGBUTTONS + +#include <qapplication.h> +#include <qpushbutton.h> +#include <qguardedptr.h> +#include <qmap.h> +#include <qvariant.h> +#ifndef QT_NO_DIALOG +#include <qdialog.h> +#endif // QT_NO_DIALOG +#include <qlayout.h> +#include <qstyle.h> +#include <qmap.h> + +struct QDialogButtonsPrivate +{ + QMap<int, QString> text; + QMap<QDialogButtons::Button, QWidget *> buttons; + QGuardedPtr<QWidget> custom; + Q_UINT32 enabled, visible; + QDialogButtons::Button def; + Qt::Orientation orient; + bool questionMode; +}; + +#ifndef QT_NO_DIALOG +QDialogButtons::QDialogButtons(QDialog *parent, bool autoConnect, Q_UINT32 buttons, + Orientation orient, const char *name ) : QWidget(parent, name) +{ + init(buttons, orient); + if(parent && autoConnect) { + QObject::connect(this, SIGNAL(acceptClicked()), parent, SLOT(accept())); + QObject::connect(this, SIGNAL(rejectClicked()), parent, SLOT(reject())); + } +} +#endif // QT_NO_DIALOG + +QDialogButtons::QDialogButtons(QWidget *parent, Q_UINT32 buttons, + Orientation orient, const char *name ) : QWidget(parent, name) +{ + init(buttons, orient); +} + +void +QDialogButtons::init(Q_UINT32 buttons, Orientation orient) +{ + if(buttons == All) { + qWarning("QDialogButtons: cannot specify All by itself!"); + buttons = None; + } + d = new QDialogButtonsPrivate; + d->questionMode = FALSE; + d->orient = orient; + d->def = (Button)style().styleHint(QStyle::SH_DialogButtons_DefaultButton, this); + d->enabled = d->visible = buttons; +} + +QDialogButtons::~QDialogButtons() +{ + delete (QWidget *)d->custom; + delete d; +} + +void +QDialogButtons::setQuestionMode(bool b) +{ + d->questionMode = b; +} + +bool +QDialogButtons::questionMode() const +{ + return d->questionMode; +} + +void +QDialogButtons::setButtonEnabled(Button button, bool enabled) +{ + if(enabled) + d->enabled |= button; + else + d->enabled ^= button; + if(d->buttons.contains(button)) + d->buttons[button]->setEnabled(enabled); +} + +bool +QDialogButtons::isButtonEnabled(Button button) const +{ + return ((int)(d->enabled & button)) == button; +} + +void +QDialogButtons::setButtonVisible(Button button, bool visible) +{ + if(visible) { + if(d->buttons.contains(button)) + d->buttons[button]->show(); + d->visible |= button; + } else { + if(d->buttons.contains(button)) + d->buttons[button]->hide(); + d->visible ^= button; + } + layoutButtons(); +} + +bool +QDialogButtons::isButtonVisible(Button button) const +{ + return ((int)(d->visible & button)) == button; +} + +void +QDialogButtons::addWidget(QWidget *w) +{ + QBoxLayout *lay = NULL; + if(!d->custom) { + d->custom = new QWidget(this, "dialog_custom_area"); + if(orientation() == Horizontal) + lay = new QHBoxLayout(d->custom); + else + lay = new QVBoxLayout(d->custom); + layoutButtons(); + } else { + lay = (QBoxLayout*)d->custom->layout(); + } + if(w->parent() != d->custom) + w->reparent(d->custom, 0, QPoint(0, 0), TRUE); + lay->addWidget(w); +} + +void +QDialogButtons::setDefaultButton(Button button) +{ + if(!((int)(d->visible & button) == button)) { + qWarning("QDialogButtons: Button '%d' is not visible (so cannot be default)", button); + return; + } + if(d->def != button) { +#ifndef QT_NO_PROPERTIES + if(d->buttons.contains(d->def)) + d->buttons[d->def]->setProperty("default", QVariant(FALSE,0)); +#endif + d->def = button; +#ifndef QT_NO_PROPERTIES + if(d->buttons.contains(d->def)) + d->buttons[d->def]->setProperty("default", QVariant(FALSE,0)); +#endif + } +} + +QDialogButtons::Button +QDialogButtons::defaultButton() const +{ + return d->def; +} + +void +QDialogButtons::setButtonText(Button button, const QString &str) +{ + d->text[button] = str; +#ifndef QT_NO_PROPERTIES + if(d->buttons.contains(button)) + d->buttons[button]->setProperty("text", QVariant(str)); +#endif + layoutButtons(); +} + +QString +QDialogButtons::buttonText(Button b) const +{ + if(d->text.contains(b)) + return d->text[b]; + return QString(); //null if it is default.. +} + +void +QDialogButtons::setOrientation(Orientation orient) +{ + if(d->orient != orient) { + d->orient = orient; + if(d->custom && d->custom->layout()) + ((QBoxLayout*)d->custom->layout())->setDirection(orient == Horizontal ? QBoxLayout::LeftToRight : + QBoxLayout::TopToBottom); + layoutButtons(); + } +} + +Qt::Orientation +QDialogButtons::orientation() const +{ + return d->orient; +} + +QWidget * +QDialogButtons::createButton(Button b) +{ + QPushButton *ret = new QPushButton(this, "qdialog_button"); + QObject::connect(ret, SIGNAL(clicked()), this, SLOT(handleClicked())); + if(d->text.contains(b)) { + ret->setText(d->text[b]); + } else { + switch(b) { + case All: { + QString txt = buttonText(defaultButton()); + if(txt.isNull()) { + if(defaultButton() == Accept) { + if(questionMode()) + txt = tr("Yes to All"); + else + txt = tr("OK to All"); + } else { + if(questionMode()) + txt = tr("No to All"); + else + txt = tr("Cancel All"); + } + } else { + txt += tr(" to All"); //ick, I can't really do this!! + } + ret->setText(txt); + break; } + case Accept: + if(questionMode()) + ret->setText(tr("Yes")); + else + ret->setText(tr("OK")); + break; + case Reject: + if(questionMode()) + ret->setText(tr("No")); + else + ret->setText(tr("Cancel")); + break; + case Apply: + ret->setText(tr("Apply")); + break; + case Ignore: + ret->setText(tr("Ignore")); + break; + case Retry: + ret->setText(tr("Retry")); + break; + case Abort: + ret->setText(tr("Abort")); + break; + case Help: + ret->setText(tr("Help")); + break; + default: + break; + } + } + return ret; +} + +void +QDialogButtons::handleClicked() +{ + const QObject *s = sender(); + if(!s) + return; + + for(QMapIterator<QDialogButtons::Button, QWidget *> it = d->buttons.begin(); it != d->buttons.end(); ++it) { + if(it.data() == s) { + emit clicked((QDialogButtons::Button)it.key()); + switch(it.key()) { + case Retry: + emit retryClicked(); + break; + case Ignore: + emit ignoreClicked(); + break; + case Abort: + emit abortClicked(); + break; + case All: + emit allClicked(); + break; + case Accept: + emit acceptClicked(); + break; + case Reject: + emit rejectClicked(); + break; + case Apply: + emit applyClicked(); + break; + case Help: + emit helpClicked(); + break; + default: + break; + } + return; + } + } +} + +void +QDialogButtons::resizeEvent(QResizeEvent *) +{ + layoutButtons(); +} + +void +QDialogButtons::showEvent(QShowEvent *) +{ + layoutButtons(); +} + +void +QDialogButtons::styleChanged(QStyle &old) +{ + layoutButtons(); + QWidget::styleChange(old); +} + +void +QDialogButtons::layoutButtons() +{ + if(!isVisible()) //nah.. + return; + + QStyle::SubRect rects[] = { + QStyle::SR_DialogButtonAccept, QStyle::SR_DialogButtonReject, + QStyle::SR_DialogButtonApply, QStyle::SR_DialogButtonHelp, + QStyle::SR_DialogButtonCustom, QStyle::SR_DialogButtonAll, + QStyle::SR_DialogButtonRetry, QStyle::SR_DialogButtonIgnore, + QStyle::SR_DialogButtonAbort }; + for(unsigned int i = 0; i < (sizeof(rects) / sizeof(rects[0])); i++) { + QWidget *w = NULL; + if(rects[i] == QStyle::SR_DialogButtonCustom) { + w = d->custom; + } else { + Button b = None; + if(rects[i] == QStyle::SR_DialogButtonApply) + b = Apply; + else if(rects[i] == QStyle::SR_DialogButtonAll) + b = All; + else if(rects[i] == QStyle::SR_DialogButtonAccept) + b = Accept; + else if(rects[i] == QStyle::SR_DialogButtonReject) + b = Reject; + else if(rects[i] == QStyle::SR_DialogButtonHelp) + b = Help; + else if(rects[i] == QStyle::SR_DialogButtonRetry) + b = Retry; + else if(rects[i] == QStyle::SR_DialogButtonAbort) + b = Abort; + else if(rects[i] == QStyle::SR_DialogButtonIgnore) + b = Ignore; + if(b != None) { + if(d->buttons.contains(b)) { + w = d->buttons[b]; + if(!(d->visible & b)) { + w->hide(); + continue; + } + } else if(d->visible & b) { + w = createButton(b); + d->buttons.insert(b, w); + } else { + continue; + } + if(w) { + if(b == d->def) { + w->setFocus(); +#ifndef QT_NO_PROPERTIES + w->setProperty("default", QVariant(TRUE,0)); +#endif + } + w->setEnabled(d->enabled & b); + } + } + } + if(w) { + w->show(); + w->setGeometry(style().subRect(rects[i], this)); + } + } +} + +QSize +QDialogButtons::sizeHint() const +{ + constPolish(); + QSize s; + if(d->custom) + s = d->custom->sizeHint(); + return (style().sizeFromContents(QStyle::CT_DialogButtons, this, s. + expandedTo(QApplication::globalStrut()))); +} + +QSize +QDialogButtons::sizeHint(QDialogButtons::Button button) const +{ + QWidget *w = NULL; + if(d->visible & button) { + if(!d->buttons.contains(button)) { + QDialogButtons *that = (QDialogButtons*)this; //ick, constness.. + w = that->createButton(button); + that->d->buttons.insert(button, w); + } else { + w = d->buttons[button]; + } + } + return w->sizeHint(); +} + +QSize +QDialogButtons::minimumSizeHint() const +{ + return sizeHint(); +} +#endif diff --git a/src/widgets/qdialogbuttons_p.h b/src/widgets/qdialogbuttons_p.h new file mode 100644 index 0000000..73bbfee --- /dev/null +++ b/src/widgets/qdialogbuttons_p.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Definition of QDialogButtons class. +** +** Copyright (C) 2005-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. +** +**********************************************************************/ + +#ifndef QDIALOGBUTTONS_P_H +#define QDIALOGBUTTONS_P_H + +#ifndef QT_H +#ifndef QT_H +#include "qwidget.h" +#endif // QT_H +#endif + +#ifndef QT_NO_DIALOGBUTTONS +struct QDialogButtonsPrivate; + +class +QDialogButtons : public QWidget +{ + Q_OBJECT +public: + enum Button { None=0, Accept=0x01, Reject=0x02, Help=0x04, Apply=0x08, All=0x10, Abort=0x20, Retry=0x40, Ignore=0x80 }; +#ifndef QT_NO_DIALOG + QDialogButtons(QDialog *parent, bool autoConnect = TRUE, Q_UINT32 buttons = Accept | Reject, + Orientation orient = Horizontal, const char *name = NULL); +#endif // QT_NO_DIALOG + QDialogButtons(QWidget *parent, Q_UINT32 buttons = Accept | Reject, + Orientation orient = Horizontal, const char *name = NULL); + ~QDialogButtons(); + + void setQuestionMode(bool); + bool questionMode() const; + + void setButtonEnabled(Button button, bool enabled); + bool isButtonEnabled(Button) const; + + inline void showButton(Button b) { setButtonVisible(b, TRUE) ; } + inline void hideButton(Button b) { setButtonVisible(b, FALSE); } + virtual void setButtonVisible(Button, bool visible); + bool isButtonVisible(Button) const; + + void addWidget(QWidget *); + + virtual void setDefaultButton(Button); + Button defaultButton() const; + + virtual void setButtonText(Button, const QString &); + QString buttonText(Button) const; + + void setOrientation(Orientation); + Orientation orientation() const; + + virtual QSize sizeHint(Button) const; + QSize minimumSizeHint() const; + QSize sizeHint() const; + +protected: + void layoutButtons(); + virtual QWidget *createButton(Button); + + void showEvent(QShowEvent *); + void resizeEvent(QResizeEvent *); + void styleChanged(QStyle &); + +private slots: + void handleClicked(); + +signals: + void clicked(Button); + void acceptClicked(); + void rejectClicked(); + void helpClicked(); + void applyClicked(); + void allClicked(); + void retryClicked(); + void ignoreClicked(); + void abortClicked(); + +private: + QDialogButtonsPrivate *d; + void init(Q_UINT32, Orientation); +}; +#endif //QT_NO_DIALOGBUTTONS +#endif //QDIALOGBUTTONS_P_H diff --git a/src/widgets/qdockarea.cpp b/src/widgets/qdockarea.cpp new file mode 100644 index 0000000..2d4d957 --- /dev/null +++ b/src/widgets/qdockarea.cpp @@ -0,0 +1,1320 @@ +/**************************************************************************** +** +** Implementation of the QDockArea class +** +** Created : 001010 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the workspace 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 "qdockarea.h" + +#ifndef QT_NO_MAINWINDOW +#include "qsplitter.h" +#include "qlayout.h" +#include "qptrvector.h" +#include "qapplication.h" +#include "qpainter.h" +#include "qwidgetlist.h" +#include "qmap.h" +#include "qmainwindow.h" + +//#define QDOCKAREA_DEBUG + +struct Q_EXPORT DockData +{ + DockData() : w( 0 ), rect() {} + DockData( QDockWindow *dw, const QRect &r ) : w( dw ), rect( r ) {} + QDockWindow *w; + QRect rect; + + Q_DUMMY_COMPARISON_OPERATOR( DockData ) +}; + +static int fix_x( QDockWindow* w, int width = -1 ) { + if ( QApplication::reverseLayout() ) { + if ( width < 0 ) + width = w->width(); + return w->parentWidget()->width() - w->x() - width; + } + return w->x(); +} +static int fix_x( QDockWindow* w, int x, int width = -1 ) { + if ( QApplication::reverseLayout() ) { + if ( width < 0 ) + width = w->width(); + return w->parentWidget()->width() - x - width; + } + return x; +} + +static QPoint fix_pos( QDockWindow* w ) { + if ( QApplication::reverseLayout() ) { + QPoint p = w->pos(); + p.rx() = w->parentWidget()->width() - p.x() - w->width(); + return p; + } + return w->pos(); +} + + +void QDockAreaLayout::setGeometry( const QRect &r ) +{ + QLayout::setGeometry( r ); + layoutItems( r ); +} + +QLayoutIterator QDockAreaLayout::iterator() +{ + return 0; +} + +QSize QDockAreaLayout::sizeHint() const +{ + if ( !dockWindows || !dockWindows->first() ) + return QSize( 0, 0 ); + + if ( dirty ) { + QDockAreaLayout *that = (QDockAreaLayout *) this; + that->layoutItems( geometry() ); + } + + int w = 0; + int h = 0; + QPtrListIterator<QDockWindow> it( *dockWindows ); + QDockWindow *dw = 0; + it.toFirst(); + int y = -1; + int x = -1; + int ph = 0; + int pw = 0; + while ( ( dw = it.current() ) != 0 ) { + int plush = 0, plusw = 0; + ++it; + if ( dw->isHidden() ) + continue; + if ( hasHeightForWidth() ) { + if ( y != dw->y() ) + plush = ph; + y = dw->y(); + ph = dw->height(); + } else { + if ( x != dw->x() ) + plusw = pw; + x = dw->x(); + pw = dw->width(); + } + h = QMAX( h, dw->height() + plush ); + w = QMAX( w, dw->width() + plusw ); + } + + if ( hasHeightForWidth() ) + return QSize( 0, h ); + return QSize( w, 0 ); +} + +bool QDockAreaLayout::hasHeightForWidth() const +{ + return orient == Horizontal; +} + +void QDockAreaLayout::init() +{ + dirty = TRUE; + cached_width = 0; + cached_height = 0; + cached_hfw = -1; + cached_wfh = -1; +} + +void QDockAreaLayout::invalidate() +{ + dirty = TRUE; + cached_width = 0; + cached_height = 0; +} + +static int start_pos( const QRect &r, Qt::Orientation o ) +{ + if ( o == Qt::Horizontal ) { + return QMAX( 0, r.x() ); + } else { + return QMAX( 0, r.y() ); + } +} + +static void add_size( int s, int &pos, Qt::Orientation o ) +{ + if ( o == Qt::Horizontal ) { + pos += s; + } else { + pos += s; + } +} + +static int space_left( const QRect &r, int pos, Qt::Orientation o ) +{ + if ( o == Qt::Horizontal ) { + return ( r.x() + r.width() ) - pos; + } else { + return ( r.y() + r.height() ) - pos; + } +} + +static int dock_extent( QDockWindow *w, Qt::Orientation o, int maxsize ) +{ + if ( o == Qt::Horizontal ) + return QMIN( maxsize, QMAX( w->sizeHint().width(), w->fixedExtent().width() ) ); + else + return QMIN( maxsize, QMAX( w->sizeHint().height(), w->fixedExtent().height() ) ); +} + +static int dock_strut( QDockWindow *w, Qt::Orientation o ) +{ + if ( o != Qt::Horizontal ) { + int wid; + if ( ( wid = w->fixedExtent().width() ) != -1 ) + return QMAX( wid, QMAX( w->minimumSize().width(), w->minimumSizeHint().width() ) ); + return QMAX( w->sizeHint().width(), QMAX( w->minimumSize().width(), w->minimumSizeHint().width() ) ); + } else { + int hei; + if ( ( hei = w->fixedExtent().height() ) != -1 ) + return QMAX( hei, QMAX( w->minimumSizeHint().height(), w->minimumSize().height() ) ); + return QMAX( w->sizeHint().height(), QMAX( w->minimumSizeHint().height(), w->minimumSize().height() ) ); + } +} + +static void set_geometry( QDockWindow *w, int pos, int sectionpos, int extent, int strut, Qt::Orientation o ) +{ + if ( o == Qt::Horizontal ) + w->setGeometry( fix_x( w, pos, extent), sectionpos, extent, strut ); + else + w->setGeometry( sectionpos, pos, strut, extent ); +} + +static int size_extent( const QSize &s, Qt::Orientation o, bool swap = FALSE ) +{ + return o == Qt::Horizontal ? ( swap ? s.height() : s.width() ) : ( swap ? s.width() : s.height() ); +} + +static int point_pos( const QPoint &p, Qt::Orientation o, bool swap = FALSE ) +{ + return o == Qt::Horizontal ? ( swap ? p.y() : p.x() ) : ( swap ? p.x() : p.y() ); +} + +static void shrink_extend( QDockWindow *dw, int &dockExtend, int /*spaceLeft*/, Qt::Orientation o ) +{ + QToolBar *tb = ::qt_cast<QToolBar*>(dw); + if ( o == Qt::Horizontal ) { + int mw = 0; + if ( !tb ) + mw = dw->minimumWidth(); + else + mw = dw->sizeHint().width(); + dockExtend = mw; + } else { + int mh = 0; + if ( !tb ) + mh = dw->minimumHeight(); + else + mh = dw->sizeHint().height(); + dockExtend = mh; + } +} + +static void place_line( QValueList<DockData> &lastLine, Qt::Orientation o, int linestrut, int fullextent, int tbstrut, int maxsize, QDockAreaLayout * ) +{ + QDockWindow *last = 0; + QRect lastRect; + for ( QValueList<DockData>::Iterator it = lastLine.begin(); it != lastLine.end(); ++it ) { + if ( tbstrut != -1 && ::qt_cast<QToolBar*>((*it).w) ) + (*it).rect.setHeight( tbstrut ); + if ( !last ) { + last = (*it).w; + lastRect = (*it).rect; + continue; + } + if ( !last->isStretchable() ) { + int w = QMIN( lastRect.width(), maxsize ); + set_geometry( last, lastRect.x(), lastRect.y(), w, lastRect.height(), o ); + } else { + int w = QMIN( (*it).rect.x() - lastRect.x(), maxsize ); + set_geometry( last, lastRect.x(), lastRect.y(), w, + last->isResizeEnabled() ? linestrut : lastRect.height(), o ); + } + last = (*it).w; + lastRect = (*it).rect; + } + if ( !last ) + return; + if ( !last->isStretchable() ) { + int w = QMIN( lastRect.width(), maxsize ); + set_geometry( last, lastRect.x(), lastRect.y(), w, lastRect.height(), o ); + } else { + int w = QMIN( fullextent - lastRect.x() - ( o == Qt::Vertical ? 1 : 0 ), maxsize ); + set_geometry( last, lastRect.x(), lastRect.y(), w, + last->isResizeEnabled() ? linestrut : lastRect.height(), o ); + } +} + + +QSize QDockAreaLayout::minimumSize() const +{ + if ( !dockWindows || !dockWindows->first() ) + return QSize( 0, 0 ); + + if ( dirty ) { + QDockAreaLayout *that = (QDockAreaLayout *) this; + that->layoutItems( geometry() ); + } + + int s = 0; + + QPtrListIterator<QDockWindow> it( *dockWindows ); + QDockWindow *dw = 0; + while ( ( dw = it.current() ) != 0 ) { + ++it; + if ( dw->isHidden() ) + continue; + s = QMAX( s, dock_strut( dw, orientation() ) ); + } + + return orientation() == Horizontal ? QSize( 0, s ? s+2 : 0 ) : QSize( s, 0 ); +} + + + +int QDockAreaLayout::layoutItems( const QRect &rect, bool testonly ) +{ + if ( !dockWindows || !dockWindows->first() ) + return 0; + + dirty = FALSE; + + // some corrections + QRect r = rect; + if ( orientation() == Vertical ) + r.setHeight( r.height() - 3 ); + + // init + lines.clear(); + ls.clear(); + QPtrListIterator<QDockWindow> it( *dockWindows ); + QDockWindow *dw = 0; + int start = start_pos( r, orientation() ); + int pos = start; + int sectionpos = 0; + int linestrut = 0; + QValueList<DockData> lastLine; + int tbstrut = -1; + int maxsize = size_extent( rect.size(), orientation() ); + int visibleWindows = 0; + + // go through all widgets in the dock + while ( ( dw = it.current() ) != 0 ) { + ++it; + if ( dw->isHidden() ) + continue; + ++visibleWindows; + // find position for the widget: This is the maximum of the + // end of the previous widget and the offset of the widget. If + // the position + the width of the widget dosn't fit into the + // dock, try moving it a bit back, if possible. + int op = pos; + int dockExtend = dock_extent( dw, orientation(), maxsize ); + if ( !dw->isStretchable() ) { + pos = QMAX( pos, dw->offset() ); + if ( pos + dockExtend > size_extent( r.size(), orientation() ) - 1 ) + pos = QMAX( op, size_extent( r.size(), orientation() ) - 1 - dockExtend ); + } + if ( !lastLine.isEmpty() && !dw->newLine() && space_left( rect, pos, orientation() ) < dockExtend ) + shrink_extend( dw, dockExtend, space_left( rect, pos, orientation() ), orientation() ); + // if the current widget doesn't fit into the line anymore and it is not the first widget of the line + if ( !lastLine.isEmpty() && + ( space_left( rect, pos, orientation() ) < dockExtend || dw->newLine() ) ) { + if ( !testonly ) // place the last line, if not in test mode + place_line( lastLine, orientation(), linestrut, size_extent( r.size(), orientation() ), tbstrut, maxsize, this ); + // remember the line coordinats of the last line + if ( orientation() == Horizontal ) + lines.append( QRect( 0, sectionpos, r.width(), linestrut ) ); + else + lines.append( QRect( sectionpos, 0, linestrut, r.height() ) ); + // do some clearing for the next line + lastLine.clear(); + sectionpos += linestrut; + linestrut = 0; + pos = start; + tbstrut = -1; + } + + // remeber first widget of a line + if ( lastLine.isEmpty() ) { + ls.append( dw ); + // try to make the best position + int op = pos; + if ( !dw->isStretchable() ) + pos = QMAX( pos, dw->offset() ); + if ( pos + dockExtend > size_extent( r.size(), orientation() ) - 1 ) + pos = QMAX( op, size_extent( r.size(), orientation() ) - 1 - dockExtend ); + } + // do some calculations and add the remember the rect which the docking widget requires for the placing + QRect dwRect(pos, sectionpos, dockExtend, dock_strut( dw, orientation() ) ); + lastLine.append( DockData( dw, dwRect ) ); + if ( ::qt_cast<QToolBar*>(dw) ) + tbstrut = QMAX( tbstrut, dock_strut( dw, orientation() ) ); + linestrut = QMAX( dock_strut( dw, orientation() ), linestrut ); + add_size( dockExtend, pos, orientation() ); + } + + // if some stuff was not placed/stored yet, do it now + if ( !testonly ) + place_line( lastLine, orientation(), linestrut, size_extent( r.size(), orientation() ), tbstrut, maxsize, this ); + if ( orientation() == Horizontal ) + lines.append( QRect( 0, sectionpos, r.width(), linestrut ) ); + else + lines.append( QRect( sectionpos, 0, linestrut, r.height() ) ); + if ( *(--lines.end()) == *(--(--lines.end())) ) + lines.remove( lines.at( lines.count() - 1 ) ); + + it.toFirst(); + bool hadResizable = FALSE; + while ( ( dw = it.current() ) != 0 ) { + ++it; + if ( !dw->isVisibleTo( parentWidget ) ) + continue; + hadResizable = hadResizable || dw->isResizeEnabled(); + dw->updateSplitterVisibility( visibleWindows > 1 ); //!dw->area()->isLastDockWindow( dw ) ); + } + return sectionpos + linestrut; +} + +int QDockAreaLayout::heightForWidth( int w ) const +{ + if ( dockWindows->isEmpty() && parentWidget ) + return parentWidget->minimumHeight(); + + if ( cached_width != w ) { + QDockAreaLayout * mthis = (QDockAreaLayout*)this; + mthis->cached_width = w; + int h = mthis->layoutItems( QRect( 0, 0, w, 0 ), TRUE ); + mthis->cached_hfw = h; + return h; + } + + return cached_hfw; +} + +int QDockAreaLayout::widthForHeight( int h ) const +{ + if ( cached_height != h ) { + QDockAreaLayout * mthis = (QDockAreaLayout*)this; + mthis->cached_height = h; + int w = mthis->layoutItems( QRect( 0, 0, 0, h ), TRUE ); + mthis->cached_wfh = w; + return w; + } + return cached_wfh; +} + + + + +/*! + \class QDockArea qdockarea.h + \brief The QDockArea class manages and lays out QDockWindows. + + \ingroup application + + A QDockArea is a container which manages a list of + \l{QDockWindow}s which it lays out within its area. In cooperation + with the \l{QDockWindow}s it is responsible for the docking and + undocking of \l{QDockWindow}s and moving them inside the dock + area. QDockAreas also handle the wrapping of \l{QDockWindow}s to + fill the available space as compactly as possible. QDockAreas can + contain QToolBars since QToolBar is a QDockWindow subclass. + + QMainWindow contains four QDockAreas which you can use for your + QToolBars and QDockWindows, so in most situations you do not need + to use the QDockArea class directly. Although QMainWindow contains + support for its own dock areas it isn't convenient for adding new + QDockAreas. If you need to create your own dock areas we suggest + that you create a subclass of QWidget and add your QDockAreas to + your subclass. + + \img qmainwindow-qdockareas.png QMainWindow's QDockAreas + + \target lines + \e Lines. QDockArea uses the concept of lines. A line is a + horizontal region which may contain dock windows side-by-side. A + dock area may have room for more than one line. When dock windows + are docked into a dock area they are usually added at the right + hand side of the top-most line that has room (unless manually + placed by the user). When users move dock windows they may leave + empty lines or gaps in non-empty lines. Dock windows can be lined + up to minimize wasted space using the lineUp() function. + + The QDockArea class maintains a position list of all its child + dock windows. Dock windows are added to a dock area from position + 0 onwards. Dock windows are laid out sequentially in position + order from left to right, and in the case of multiple lines of + dock windows, from top to bottom. If a dock window is floated it + still retains its position since this is where the window will + return if the user double clicks its caption. A dock window's + position can be determined with hasDockWindow(). The position can + be changed with moveDockWindow(). + + To dock or undock a dock window use QDockWindow::dock() and + QDockWindow::undock() respectively. If you want to control which + dock windows can dock in a dock area use setAcceptDockWindow(). To + see if a dock area contains a particular dock window use + \l{hasDockWindow()}; to see how many dock windows a dock area + contains use count(). + + The streaming operators can write the positions of the dock + windows in the dock area to a QTextStream. The positions can be + read back later to restore the saved positions. + + Save the positions to a QTextStream: + \code + ts << *myDockArea; + \endcode + + Restore the positions from a QTextStream: + \code + ts >> *myDockArea; + \endcode +*/ + +/*! + \property QDockArea::handlePosition + \brief where the dock window splitter handle is placed in the dock + area + + The default position is \c Normal. +*/ + +/*! + \property QDockArea::orientation + \brief the dock area's orientation + + There is no default value; the orientation is specified in the + constructor. +*/ + +/*! + \enum QDockArea::HandlePosition + + A dock window has two kinds of handles, the dock window handle + used for dragging the dock window, and the splitter handle used to + resize the dock window in relation to other dock windows using a + splitter. (The splitter handle is only visible for docked + windows.) + + This enum specifies where the dock window splitter handle is + placed in the dock area. + + \value Normal The splitter handles of dock windows are placed at + the right or bottom. + + \value Reverse The splitter handles of dock windows are placed at + the left or top. +*/ + +/*! + Constructs a QDockArea with orientation \a o, HandlePosition \a h, + parent \a parent and called \a name. +*/ + +QDockArea::QDockArea( Orientation o, HandlePosition h, QWidget *parent, const char *name ) + : QWidget( parent, name ), orient( o ), layout( 0 ), hPos( h ) +{ + dockWindows = new QPtrList<QDockWindow>; + layout = new QDockAreaLayout( this, o, dockWindows, 0, 0, "toollayout" ); + installEventFilter( this ); +} + +/*! + Destroys the dock area and all the dock windows docked in the dock + area. + + Does not affect any floating dock windows or dock windows in other + dock areas, even if they first appeared in this dock area. + Floating dock windows are effectively top level windows and are + not child windows of the dock area. When a floating dock window is + docked (dragged into a dock area) its parent becomes the dock + area. +*/ + +QDockArea::~QDockArea() +{ + dockWindows->setAutoDelete( TRUE ); + delete dockWindows; + dockWindows = 0; +} + +/*! + Moves the QDockWindow \a w within the dock area. If \a w is not + already docked in this area, \a w is docked first. If \a index is + -1 or larger than the number of docked widgets, \a w is appended + at the end, otherwise it is inserted at the position \a index. +*/ + +void QDockArea::moveDockWindow( QDockWindow *w, int index ) +{ + invalidateFixedSizes(); + QDockWindow *dockWindow = 0; + int dockWindowIndex = findDockWindow( w ); + if ( dockWindowIndex == -1 ) { + dockWindow = w; + dockWindow->reparent( this, QPoint( 0, 0 ), TRUE ); + w->installEventFilter( this ); + updateLayout(); + setSizePolicy( QSizePolicy( orientation() == Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum, + orientation() == Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum ) ); + dockWindows->append( w ); + } else { + if ( w->parent() != this ) + w->reparent( this, QPoint( 0, 0 ), TRUE ); + if ( index == - 1 ) { + dockWindows->removeRef( w ); + dockWindows->append( w ); + } + } + + w->dockArea = this; + w->curPlace = QDockWindow::InDock; + w->updateGui(); + + if ( index != -1 && index < (int)dockWindows->count() ) { + dockWindows->removeRef( w ); + dockWindows->insert( index, w ); + } +} + +/*! + Returns TRUE if the dock area contains the dock window \a w; + otherwise returns FALSE. If \a index is not 0 it will be set as + follows: if the dock area contains the dock window \a *index is + set to \a w's index position; otherwise \a *index is set to -1. +*/ + +bool QDockArea::hasDockWindow( QDockWindow *w, int *index ) +{ + int i = dockWindows->findRef( w ); + if ( index ) + *index = i; + return i != -1; +} + +int QDockArea::lineOf( int index ) +{ + QPtrList<QDockWindow> lineStarts = layout->lineStarts(); + int i = 0; + for ( QDockWindow *w = lineStarts.first(); w; w = lineStarts.next(), ++i ) { + if ( dockWindows->find( w ) >= index ) + return i; + } + return i; +} + +/*! + \overload + + Moves the dock window \a w inside the dock area where \a p is the + new position (in global screen coordinates), \a r is the suggested + rectangle of the dock window and \a swap specifies whether or not + the orientation of the docked widget needs to be changed. + + This function is used internally by QDockWindow. You shouldn't + need to call it yourself. +*/ + +void QDockArea::moveDockWindow( QDockWindow *w, const QPoint &p, const QRect &r, bool swap ) +{ + invalidateFixedSizes(); + int mse = -10; + bool hasResizable = FALSE; + for ( QDockWindow *dw = dockWindows->first(); dw; dw = dockWindows->next() ) { + if ( dw->isHidden() ) + continue; + if ( dw->isResizeEnabled() ) + hasResizable = TRUE; + if ( orientation() != Qt::Horizontal ) + mse = QMAX( QMAX( dw->fixedExtent().width(), dw->width() ), mse ); + else + mse = QMAX( QMAX( dw->fixedExtent().height(), dw->height() ), mse ); + } + if ( !hasResizable && w->isResizeEnabled() ) { + if ( orientation() != Qt::Horizontal ) + mse = QMAX( w->fixedExtent().width(), mse ); + else + mse = QMAX( w->fixedExtent().height(), mse ); + } + + QDockWindow *dockWindow = 0; + int dockWindowIndex = findDockWindow( w ); + QPtrList<QDockWindow> lineStarts = layout->lineStarts(); + QValueList<QRect> lines = layout->lineList(); + bool wasAloneInLine = FALSE; + QPoint pos = mapFromGlobal( p ); + QRect lr = *lines.at( lineOf( dockWindowIndex ) ); + if ( dockWindowIndex != -1 ) { + if ( lineStarts.find( w ) != -1 && + ( dockWindowIndex < (int)dockWindows->count() - 1 && lineStarts.find( dockWindows->at( dockWindowIndex + 1 ) ) != -1 || + dockWindowIndex == (int)dockWindows->count() - 1 ) ) + wasAloneInLine = TRUE; + dockWindow = dockWindows->take( dockWindowIndex ); + if ( !wasAloneInLine ) { // only do the pre-layout if the widget isn't the only one in its line + if ( lineStarts.findRef( dockWindow ) != -1 && dockWindowIndex < (int)dockWindows->count() ) + dockWindows->at( dockWindowIndex )->setNewLine( TRUE ); + layout->layoutItems( QRect( 0, 0, width(), height() ), TRUE ); + } + } else { + dockWindow = w; + dockWindow->reparent( this, QPoint( 0, 0 ), TRUE ); + if ( swap ) + dockWindow->resize( dockWindow->height(), dockWindow->width() ); + w->installEventFilter( this ); + } + + lineStarts = layout->lineStarts(); + lines = layout->lineList(); + + QRect rect = QRect( mapFromGlobal( r.topLeft() ), r.size() ); + if ( orientation() == Horizontal && QApplication::reverseLayout() ) { + rect = QRect( width() - rect.x() - rect.width(), rect.y(), rect.width(), rect.height() ); + pos.rx() = width() - pos.x(); + } + dockWindow->setOffset( point_pos( rect.topLeft(), orientation() ) ); + if ( orientation() == Horizontal ) { + int offs = dockWindow->offset(); + if ( width() - offs < dockWindow->minimumWidth() ) + dockWindow->setOffset( width() - dockWindow->minimumWidth() ); + } else { + int offs = dockWindow->offset(); + if ( height() - offs < dockWindow->minimumHeight() ) + dockWindow->setOffset( height() - dockWindow->minimumHeight() ); + } + + if ( dockWindows->isEmpty() ) { + dockWindows->append( dockWindow ); + } else { + int dockLine = -1; + bool insertLine = FALSE; + int i = 0; + QRect lineRect; + // find the line which we touched with the mouse + for ( QValueList<QRect>::Iterator it = lines.begin(); it != lines.end(); ++it, ++i ) { + if ( point_pos( pos, orientation(), TRUE ) >= point_pos( (*it).topLeft(), orientation(), TRUE ) && + point_pos( pos, orientation(), TRUE ) <= point_pos( (*it).topLeft(), orientation(), TRUE ) + + size_extent( (*it).size(), orientation(), TRUE ) ) { + dockLine = i; + lineRect = *it; + break; + } + } + if ( dockLine == -1 ) { // outside the dock... + insertLine = TRUE; + if ( point_pos( pos, orientation(), TRUE ) < 0 ) // insert as first line + dockLine = 0; + else + dockLine = (int)lines.count(); // insert after the last line ### size_t/int cast + } else { // inside the dock (we have found a dockLine) + if ( point_pos( pos, orientation(), TRUE ) < + point_pos( lineRect.topLeft(), orientation(), TRUE ) + 4 ) { // mouse was at the very beginning of the line + insertLine = TRUE; // insert a new line before that with the docking widget + } else if ( point_pos( pos, orientation(), TRUE ) > + point_pos( lineRect.topLeft(), orientation(), TRUE ) + + size_extent( lineRect.size(), orientation(), TRUE ) - 4 ) { // mouse was at the very and of the line + insertLine = TRUE; // insert a line after that with the docking widget + dockLine++; + } + } + + if ( !insertLine && wasAloneInLine && lr.contains( pos ) ) // if we are alone in a line and just moved in there, re-insert it + insertLine = TRUE; + +#if defined(QDOCKAREA_DEBUG) + qDebug( "insert in line %d, and insert that line: %d", dockLine, insertLine ); + qDebug( " (btw, we have %d lines)", lines.count() ); +#endif + QDockWindow *dw = 0; + if ( dockLine >= (int)lines.count() ) { // insert after last line + dockWindows->append( dockWindow ); + dockWindow->setNewLine( TRUE ); +#if defined(QDOCKAREA_DEBUG) + qDebug( "insert at the end" ); +#endif + } else if ( dockLine == 0 && insertLine ) { // insert before first line + dockWindows->insert( 0, dockWindow ); + dockWindows->at( 1 )->setNewLine( TRUE ); +#if defined(QDOCKAREA_DEBUG) + qDebug( "insert at the begin" ); +#endif + } else { // insert somewhere in between + // make sure each line start has a new line + for ( dw = lineStarts.first(); dw; dw = lineStarts.next() ) + dw->setNewLine( TRUE ); + + // find the index of the first widget in the search line + int searchLine = dockLine; +#if defined(QDOCKAREA_DEBUG) + qDebug( "search line start of %d", searchLine ); +#endif + QDockWindow *lsw = lineStarts.at( searchLine ); + int index = dockWindows->find( lsw ); + if ( index == -1 ) { // the linestart widget hasn't been found, try to find it harder + if ( lsw == w && dockWindowIndex <= (int)dockWindows->count()) + index = dockWindowIndex; + else + index = 0; + if ( index < (int)dockWindows->count() ) + (void)dockWindows->at( index ); // move current to index + } +#if defined(QDOCKAREA_DEBUG) + qDebug( " which starts at %d", index ); +#endif + if ( !insertLine ) { // if we insert the docking widget in the existing line + // find the index for the widget + bool inc = TRUE; + bool firstTime = TRUE; + for ( dw = dockWindows->current(); dw; dw = dockWindows->next() ) { + if ( orientation() == Horizontal ) + dw->setFixedExtentWidth( -1 ); + else + dw->setFixedExtentHeight( -1 ); + if ( !firstTime && lineStarts.find( dw ) != -1 ) // we are in the next line, so break + break; + if ( point_pos( pos, orientation() ) < + point_pos( fix_pos( dw ), orientation() ) + size_extent( dw->size(), orientation() ) / 2 ) { + inc = FALSE; + } + if ( inc ) + index++; + firstTime = FALSE; + } +#if defined(QDOCKAREA_DEBUG) + qDebug( "insert at index: %d", index ); +#endif + // if we insert it just before a widget which has a new line, transfer the newline to the docking widget + // but not if we didn't only mave a widget in its line which was alone in the line before + if ( !( wasAloneInLine && lr.contains( pos ) ) + && index >= 0 && index < (int)dockWindows->count() && + dockWindows->at( index )->newLine() && lineOf( index ) == dockLine ) { +#if defined(QDOCKAREA_DEBUG) + qDebug( "get rid of the old newline and get me one" ); +#endif + dockWindows->at( index )->setNewLine( FALSE ); + dockWindow->setNewLine( TRUE ); + } else if ( wasAloneInLine && lr.contains( pos ) ) { + dockWindow->setNewLine( TRUE ); + } else { // if we are somewhere in a line, get rid of the newline + dockWindow->setNewLine( FALSE ); + } + } else { // insert in a new line, so make sure the dock widget and the widget which will be after it have a newline +#if defined(QDOCKAREA_DEBUG) + qDebug( "insert a new line" ); +#endif + if ( index < (int)dockWindows->count() ) { +#if defined(QDOCKAREA_DEBUG) + qDebug( "give the widget at %d a newline", index ); +#endif + QDockWindow* nldw = dockWindows->at( index ); + if ( nldw ) + nldw->setNewLine( TRUE ); + } +#if defined(QDOCKAREA_DEBUG) + qDebug( "give me a newline" ); +#endif + dockWindow->setNewLine( TRUE ); + } + // finally insert the widget + dockWindows->insert( index, dockWindow ); + } + } + + if ( mse != -10 && w->isResizeEnabled() ) { + if ( orientation() != Qt::Horizontal ) + w->setFixedExtentWidth( QMIN( QMAX( w->minimumWidth(), mse ), w->sizeHint().width() ) ); + else + w->setFixedExtentHeight( QMIN( QMAX( w->minimumHeight(), mse ), w->sizeHint().height() ) ); + } + + updateLayout(); + setSizePolicy( QSizePolicy( orientation() == Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum, + orientation() == Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum ) ); +} + +/*! + Removes the dock window \a w from the dock area. If \a + makeFloating is TRUE, \a w gets floated, and if \a swap is TRUE, + the orientation of \a w gets swapped. If \a fixNewLines is TRUE + (the default) newlines in the area will be fixed. + + You should never need to call this function yourself. Use + QDockWindow::dock() and QDockWindow::undock() instead. +*/ + +void QDockArea::removeDockWindow( QDockWindow *w, bool makeFloating, bool swap, bool fixNewLines ) +{ + w->removeEventFilter( this ); + QDockWindow *dockWindow = 0; + int i = findDockWindow( w ); + if ( i == -1 ) + return; + dockWindow = dockWindows->at( i ); + dockWindows->remove( i ); + QPtrList<QDockWindow> lineStarts = layout->lineStarts(); + if ( fixNewLines && lineStarts.findRef( dockWindow ) != -1 && i < (int)dockWindows->count() ) + dockWindows->at( i )->setNewLine( TRUE ); + if ( makeFloating ) { + QWidget *p = parentWidget() ? parentWidget() : topLevelWidget(); + dockWindow->reparent( p, WType_Dialog | WStyle_Customize | WStyle_NoBorder | WStyle_Tool, QPoint( 0, 0 ), FALSE ); + } + if ( swap ) + dockWindow->resize( dockWindow->height(), dockWindow->width() ); + updateLayout(); + if ( dockWindows->isEmpty() ) + setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) ); +} + +int QDockArea::findDockWindow( QDockWindow *w ) +{ + return dockWindows ? dockWindows->findRef( w ) : -1; +} + +void QDockArea::updateLayout() +{ + layout->invalidate(); + layout->activate(); +} + +/*! \reimp + */ + +bool QDockArea::eventFilter( QObject *o, QEvent *e ) +{ + if ( e->type() == QEvent::Close ) { + if ( ::qt_cast<QDockWindow*>(o) ) { + o->removeEventFilter( this ); + QApplication::sendEvent( o, e ); + if ( ( (QCloseEvent*)e )->isAccepted() ) + removeDockWindow( (QDockWindow*)o, FALSE, FALSE ); + return TRUE; + } + } + return FALSE; +} + +/*! \internal + + Invalidates the offset of the next dock window in the dock area. + */ + +void QDockArea::invalidNextOffset( QDockWindow *dw ) +{ + int i = dockWindows->find( dw ); + if ( i == -1 || i >= (int)dockWindows->count() - 1 ) + return; + if ( ( dw = dockWindows->at( ++i ) ) ) + dw->setOffset( 0 ); +} + +/*! + \property QDockArea::count + \brief the number of dock windows in the dock area +*/ +int QDockArea::count() const +{ + return dockWindows->count(); +} + +/*! + \property QDockArea::empty + \brief whether the dock area is empty +*/ + +bool QDockArea::isEmpty() const +{ + return dockWindows->isEmpty(); +} + + +/*! + Returns a list of the dock windows in the dock area. +*/ + +QPtrList<QDockWindow> QDockArea::dockWindowList() const +{ + return *dockWindows; +} + +/*! + Lines up the dock windows in this dock area to minimize wasted + space. If \a keepNewLines is TRUE, only space within lines is + cleaned up. If \a keepNewLines is FALSE the number of lines might + be changed. +*/ + +void QDockArea::lineUp( bool keepNewLines ) +{ + for ( QDockWindow *dw = dockWindows->first(); dw; dw = dockWindows->next() ) { + dw->setOffset( 0 ); + if ( !keepNewLines ) + dw->setNewLine( FALSE ); + } + layout->activate(); +} + +QDockArea::DockWindowData *QDockArea::dockWindowData( QDockWindow *w ) +{ + DockWindowData *data = new DockWindowData; + data->index = findDockWindow( w ); + if ( data->index == -1 ) { + delete data; + return 0; + } + QPtrList<QDockWindow> lineStarts = layout->lineStarts(); + int i = -1; + for ( QDockWindow *dw = dockWindows->first(); dw; dw = dockWindows->next() ) { + if ( lineStarts.findRef( dw ) != -1 ) + ++i; + if ( dw == w ) + break; + } + data->line = i; + data->offset = point_pos( QPoint( fix_x(w), w->y() ), orientation() ); + data->area = this; + data->fixedExtent = w->fixedExtent(); + return data; +} + +void QDockArea::dockWindow( QDockWindow *dockWindow, DockWindowData *data ) +{ + if ( !data ) + return; + + dockWindow->reparent( this, QPoint( 0, 0 ), FALSE ); + dockWindow->installEventFilter( this ); + dockWindow->dockArea = this; + dockWindow->updateGui(); + + if ( dockWindows->isEmpty() ) { + dockWindows->append( dockWindow ); + } else { + QPtrList<QDockWindow> lineStarts = layout->lineStarts(); + int index = 0; + if ( (int)lineStarts.count() > data->line ) + index = dockWindows->find( lineStarts.at( data->line ) ); + if ( index == -1 ) { + index = 0; + (void)dockWindows->at( index ); + } + bool firstTime = TRUE; + int offset = data->offset; + for ( QDockWindow *dw = dockWindows->current(); dw; dw = dockWindows->next() ) { + if ( !firstTime && lineStarts.find( dw ) != -1 ) + break; + if ( offset < + point_pos( fix_pos( dw ), orientation() ) + size_extent( dw->size(), orientation() ) / 2 ) + break; + index++; + firstTime = FALSE; + } + if ( index >= 0 && index < (int)dockWindows->count() && + dockWindows->at( index )->newLine() && lineOf( index ) == data->line ) { + dockWindows->at( index )->setNewLine( FALSE ); + dockWindow->setNewLine( TRUE ); + } else { + dockWindow->setNewLine( FALSE ); + } + + dockWindows->insert( index, dockWindow ); + } + dockWindow->show(); + + dockWindow->setFixedExtentWidth( data->fixedExtent.width() ); + dockWindow->setFixedExtentHeight( data->fixedExtent.height() ); + + updateLayout(); + setSizePolicy( QSizePolicy( orientation() == Horizontal ? QSizePolicy::Expanding : QSizePolicy::Minimum, + orientation() == Vertical ? QSizePolicy::Expanding : QSizePolicy::Minimum ) ); + +} + +/*! + Returns TRUE if dock window \a dw could be docked into the dock + area; otherwise returns FALSE. + + \sa setAcceptDockWindow() +*/ + +bool QDockArea::isDockWindowAccepted( QDockWindow *dw ) +{ + if ( !dw ) + return FALSE; + if ( forbiddenWidgets.findRef( dw ) != -1 ) + return FALSE; + + QMainWindow *mw = ::qt_cast<QMainWindow*>(parentWidget()); + if ( !mw ) + return TRUE; + if ( !mw->hasDockWindow( dw ) ) + return FALSE; + if ( !mw->isDockEnabled( this ) ) + return FALSE; + if ( !mw->isDockEnabled( dw, this ) ) + return FALSE; + return TRUE; +} + +/*! + If \a accept is TRUE, dock window \a dw can be docked in the dock + area. If \a accept is FALSE, dock window \a dw cannot be docked in + the dock area. + + \sa isDockWindowAccepted() +*/ + +void QDockArea::setAcceptDockWindow( QDockWindow *dw, bool accept ) +{ + if ( accept ) + forbiddenWidgets.removeRef( dw ); + else if ( forbiddenWidgets.findRef( dw ) == -1 ) + forbiddenWidgets.append( dw ); +} + +void QDockArea::invalidateFixedSizes() +{ + for ( QDockWindow *dw = dockWindows->first(); dw; dw = dockWindows->next() ) { + if ( orientation() == Qt::Horizontal ) + dw->setFixedExtentWidth( -1 ); + else + dw->setFixedExtentHeight( -1 ); + } +} + +int QDockArea::maxSpace( int hint, QDockWindow *dw ) +{ + int index = findDockWindow( dw ); + if ( index == -1 || index + 1 >= (int)dockWindows->count() ) { + if ( orientation() == Horizontal ) + return dw->width(); + return dw->height(); + } + + QDockWindow *w = 0; + int i = 0; + do { + w = dockWindows->at( index + (++i) ); + } while ( i + 1 < (int)dockWindows->count() && ( !w || w->isHidden() ) ); + if ( !w || !w->isResizeEnabled() || i >= (int)dockWindows->count() ) { + if ( orientation() == Horizontal ) + return dw->width(); + return dw->height(); + } + int min = 0; + QToolBar *tb = ::qt_cast<QToolBar*>(w); + if ( orientation() == Horizontal ) { + w->setFixedExtentWidth( -1 ); + if ( !tb ) + min = QMAX( w->minimumSize().width(), w->minimumSizeHint().width() ); + else + min = w->sizeHint().width(); + } else { + w->setFixedExtentHeight( -1 ); + if ( !tb ) + min = QMAX( w->minimumSize().height(), w->minimumSizeHint().height() ); + else + min = w->sizeHint().height(); + } + + int diff = hint - ( orientation() == Horizontal ? dw->width() : dw->height() ); + + if ( ( orientation() == Horizontal ? w->width() : w->height() ) - diff < min ) + hint = ( orientation() == Horizontal ? dw->width() : dw->height() ) + ( orientation() == Horizontal ? w->width() : w->height() ) - min; + + diff = hint - ( orientation() == Horizontal ? dw->width() : dw->height() ); + if ( orientation() == Horizontal ) + w->setFixedExtentWidth( w->width() - diff ); + else + w->setFixedExtentHeight( w->height() - diff ); + return hint; +} + +void QDockArea::setFixedExtent( int d, QDockWindow *dw ) +{ + QPtrList<QDockWindow> lst; + QDockWindow *w; + for ( w = dockWindows->first(); w; w = dockWindows->next() ) { + if ( w->isHidden() ) + continue; + if ( orientation() == Horizontal ) { + if ( dw->y() != w->y() ) + continue; + } else { + if ( dw->x() != w->x() ) + continue; + } + if ( orientation() == Horizontal ) + d = QMAX( d, w->minimumHeight() ); + else + d = QMAX( d, w->minimumWidth() ); + if ( w->isResizeEnabled() ) + lst.append( w ); + } + for ( w = lst.first(); w; w = lst.next() ) { + if ( orientation() == Horizontal ) + w->setFixedExtentHeight( d ); + else + w->setFixedExtentWidth( d ); + } +} + +bool QDockArea::isLastDockWindow( QDockWindow *dw ) +{ + int i = dockWindows->find( dw ); + if ( i == -1 || i >= (int)dockWindows->count() - 1 ) + return TRUE; + QDockWindow *w = 0; + if ( ( w = dockWindows->at( ++i ) ) ) { + if ( orientation() == Horizontal && dw->y() < w->y() ) + return TRUE; + if ( orientation() == Vertical && dw->x() < w->x() ) + return TRUE; + } else { + return TRUE; + } + return FALSE; +} + +#ifndef QT_NO_TEXTSTREAM + +/*! + \relates QDockArea + + Writes the layout of the dock windows in dock area \a dockArea to + the text stream \a ts. + + \sa operator>>() +*/ + +QTextStream &operator<<( QTextStream &ts, const QDockArea &dockArea ) +{ + QString str; + QPtrList<QDockWindow> l = dockArea.dockWindowList(); + + for ( QDockWindow *dw = l.first(); dw; dw = l.next() ) + str += "[" + QString( dw->caption() ) + "," + QString::number( (int)dw->offset() ) + + "," + QString::number( (int)dw->newLine() ) + "," + QString::number( dw->fixedExtent().width() ) + + "," + QString::number( dw->fixedExtent().height() ) + "," + QString::number( (int)!dw->isHidden() ) + "]"; + ts << str << endl; + + return ts; +} + +/*! + \relates QDockArea + + Reads the layout description of the dock windows in dock area \a + dockArea from the text stream \a ts and restores it. The layout + description must have been previously written by the operator<<() + function. + + \sa operator<<() +*/ + +QTextStream &operator>>( QTextStream &ts, QDockArea &dockArea ) +{ + QString s = ts.readLine(); + + QString name, offset, newLine, width, height, visible; + + enum State { Pre, Name, Offset, NewLine, Width, Height, Visible, Post }; + int state = Pre; + QChar c; + QPtrList<QDockWindow> l = dockArea.dockWindowList(); + + for ( int i = 0; i < (int)s.length(); ++i ) { + c = s[ i ]; + if ( state == Pre && c == '[' ) { + state++; + continue; + } + if ( c == ',' && + ( state == Name || state == Offset || state == NewLine || state == Width || state == Height ) ) { + state++; + continue; + } + if ( state == Visible && c == ']' ) { + for ( QDockWindow *dw = l.first(); dw; dw = l.next() ) { + if ( QString( dw->caption() ) == name ) { + dw->setNewLine( (bool)newLine.toInt() ); + dw->setOffset( offset.toInt() ); + dw->setFixedExtentWidth( width.toInt() ); + dw->setFixedExtentHeight( height.toInt() ); + if ( !(bool)visible.toInt() ) + dw->hide(); + else + dw->show(); + break; + } + } + + name = offset = newLine = width = height = visible = ""; + + state = Pre; + continue; + } + if ( state == Name ) + name += c; + else if ( state == Offset ) + offset += c; + else if ( state == NewLine ) + newLine += c; + else if ( state == Width ) + width += c; + else if ( state == Height ) + height += c; + else if ( state == Visible ) + visible += c; + } + + dockArea.QWidget::layout()->invalidate(); + dockArea.QWidget::layout()->activate(); + return ts; +} +#endif + +#endif //QT_NO_MAINWINDOW diff --git a/src/widgets/qdockarea.h b/src/widgets/qdockarea.h new file mode 100644 index 0000000..c780d3c --- /dev/null +++ b/src/widgets/qdockarea.h @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Definition of the QDockArea class +** +** Created : 001010 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the workspace 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. +** +**********************************************************************/ + +#ifndef QDOCKAREA_H +#define QDOCKAREA_H + +#ifndef QT_H +#include "qwidget.h" +#include "qptrlist.h" +#include "qdockwindow.h" +#include "qlayout.h" +#include "qvaluelist.h" +#include "qguardedptr.h" +#include "qtextstream.h" +#endif // QT_H + +#ifndef QT_NO_MAINWINDOW + +class QSplitter; +class QBoxLayout; +class QDockAreaLayout; +class QMouseEvent; +class QDockWindowResizeHandle; +class QDockAreaPrivate; + +class Q_EXPORT QDockAreaLayout : public QLayout +{ + Q_OBJECT + friend class QDockArea; + +public: + QDockAreaLayout( QWidget* parent, Qt::Orientation o, QPtrList<QDockWindow> *wl, int space = -1, int margin = -1, const char *name = 0 ) + : QLayout( parent, space, margin, name ), orient( o ), dockWindows( wl ), parentWidget( parent ) { init(); } + ~QDockAreaLayout() {} + + void addItem( QLayoutItem * ) {} + bool hasHeightForWidth() const; + int heightForWidth( int ) const; + int widthForHeight( int ) const; + QSize sizeHint() const; + QSize minimumSize() const; + QLayoutIterator iterator(); + QSizePolicy::ExpandData expanding() const { return QSizePolicy::NoDirection; } + void invalidate(); + Qt::Orientation orientation() const { return orient; } + QValueList<QRect> lineList() const { return lines; } + QPtrList<QDockWindow> lineStarts() const { return ls; } + +protected: + void setGeometry( const QRect& ); + +private: + void init(); + int layoutItems( const QRect&, bool testonly = FALSE ); + Qt::Orientation orient; + bool dirty; + int cached_width, cached_height; + int cached_hfw, cached_wfh; + QPtrList<QDockWindow> *dockWindows; + QWidget *parentWidget; + QValueList<QRect> lines; + QPtrList<QDockWindow> ls; +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + QDockAreaLayout( const QDockAreaLayout & ); + QDockAreaLayout &operator=( const QDockAreaLayout & ); +#endif +}; + +class Q_EXPORT QDockArea : public QWidget +{ + Q_OBJECT + Q_ENUMS( HandlePosition ) + Q_PROPERTY( Orientation orientation READ orientation ) + Q_PROPERTY( int count READ count ) + Q_PROPERTY( bool empty READ isEmpty ) + Q_PROPERTY( HandlePosition handlePosition READ handlePosition ) + + friend class QDockWindow; + friend class QDockWindowResizeHandle; + friend class QDockAreaLayout; + +public: + enum HandlePosition { Normal, Reverse }; + + QDockArea( Orientation o, HandlePosition h = Normal, QWidget* parent=0, const char* name=0 ); + ~QDockArea(); + + void moveDockWindow( QDockWindow *w, const QPoint &globalPos, const QRect &rect, bool swap ); + void removeDockWindow( QDockWindow *w, bool makeFloating, bool swap, bool fixNewLines = TRUE ); + void moveDockWindow( QDockWindow *w, int index = -1 ); + bool hasDockWindow( QDockWindow *w, int *index = 0 ); + + void invalidNextOffset( QDockWindow *dw ); + + Orientation orientation() const { return orient; } + HandlePosition handlePosition() const { return hPos; } + + bool eventFilter( QObject *, QEvent * ); + bool isEmpty() const; + int count() const; + QPtrList<QDockWindow> dockWindowList() const; + + bool isDockWindowAccepted( QDockWindow *dw ); + void setAcceptDockWindow( QDockWindow *dw, bool accept ); + +public slots: + void lineUp( bool keepNewLines ); + +private: + struct DockWindowData + { + int index; + int offset; + int line; + QSize fixedExtent; + QGuardedPtr<QDockArea> area; + }; + + int findDockWindow( QDockWindow *w ); + int lineOf( int index ); + DockWindowData *dockWindowData( QDockWindow *w ); + void dockWindow( QDockWindow *dockWindow, DockWindowData *data ); + void updateLayout(); + void invalidateFixedSizes(); + int maxSpace( int hint, QDockWindow *dw ); + void setFixedExtent( int d, QDockWindow *dw ); + bool isLastDockWindow( QDockWindow *dw ); + +private: + Orientation orient; + QPtrList<QDockWindow> *dockWindows; + QDockAreaLayout *layout; + HandlePosition hPos; + QPtrList<QDockWindow> forbiddenWidgets; + QDockAreaPrivate *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QDockArea( const QDockArea & ); + QDockArea& operator=( const QDockArea & ); +#endif + +}; + +#ifndef QT_NO_TEXTSTREAM +Q_EXPORT QTextStream &operator<<( QTextStream &, const QDockArea & ); +Q_EXPORT QTextStream &operator>>( QTextStream &, QDockArea & ); +#endif + +#define Q_DEFINED_QDOCKAREA +#include "qwinexport.h" +#endif + +#endif //QT_NO_MAINWINDOW diff --git a/src/widgets/qdockwindow.cpp b/src/widgets/qdockwindow.cpp new file mode 100644 index 0000000..8646ec9 --- /dev/null +++ b/src/widgets/qdockwindow.cpp @@ -0,0 +1,2123 @@ +/**************************************************************************** +** +** Implementation of the QDockWindow class +** +** Created : 001010 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the workspace 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 "qdockwindow.h" + +#ifndef QT_NO_MAINWINDOW +#include "qdesktopwidget.h" +#include "qdockarea.h" +#include "qwidgetresizehandler_p.h" +#include "qtitlebar_p.h" +#include "qpainter.h" +#include "qapplication.h" +#include "qtoolbutton.h" +#include "qtoolbar.h" +#include "qlayout.h" +#include "qmainwindow.h" +#include "qtimer.h" +#include "qtooltip.h" +#include "qguardedptr.h" +#include "qcursor.h" +#include "qstyle.h" + +#if defined(Q_WS_MAC9) +#define MAC_DRAG_HACK +#endif +#ifdef Q_WS_MACX +static bool default_opaque = TRUE; +#else +static bool default_opaque = FALSE; +#endif + +class QDockWindowPrivate +{ +}; + +class QDockWindowResizeHandle : public QWidget +{ + Q_OBJECT + +public: + QDockWindowResizeHandle( Qt::Orientation o, QWidget *parent, QDockWindow *w, const char* /*name*/=0 ); + void setOrientation( Qt::Orientation o ); + Qt::Orientation orientation() const { return orient; } + + QSize sizeHint() const; + +protected: + void paintEvent( QPaintEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + +private: + void startLineDraw(); + void endLineDraw(); + void drawLine( const QPoint &globalPos ); + +private: + Qt::Orientation orient; + bool mousePressed; + QPainter *unclippedPainter; + QPoint lastPos, firstPos; + QDockWindow *dockWindow; + +}; + +QDockWindowResizeHandle::QDockWindowResizeHandle( Qt::Orientation o, QWidget *parent, + QDockWindow *w, const char * ) + : QWidget( parent, "qt_dockwidget_internal" ), mousePressed( FALSE ), unclippedPainter( 0 ), dockWindow( w ) +{ + setOrientation( o ); +} + +QSize QDockWindowResizeHandle::sizeHint() const +{ + int sw = 2 * style().pixelMetric(QStyle::PM_SplitterWidth, this) / 3; + return (style().sizeFromContents(QStyle::CT_DockWindow, this, QSize(sw, sw)). + expandedTo(QApplication::globalStrut())); +} + +void QDockWindowResizeHandle::setOrientation( Qt::Orientation o ) +{ + orient = o; + if ( o == QDockArea::Horizontal ) { +#ifndef QT_NO_CURSOR + setCursor( splitVCursor ); +#endif + setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + } else { +#ifndef QT_NO_CURSOR + setCursor( splitHCursor ); +#endif + setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Expanding ) ); + } +} + +void QDockWindowResizeHandle::mousePressEvent( QMouseEvent *e ) +{ + e->ignore(); + if ( e->button() != LeftButton ) + return; + e->accept(); + mousePressed = TRUE; + if ( !dockWindow->opaqueMoving() ) + startLineDraw(); + lastPos = firstPos = e->globalPos(); + if ( !dockWindow->opaqueMoving() ) + drawLine( e->globalPos() ); +} + +void QDockWindowResizeHandle::mouseMoveEvent( QMouseEvent *e ) +{ + if ( !mousePressed ) + return; + if ( !dockWindow->opaqueMoving() ) { + if ( orientation() != dockWindow->area()->orientation() ) { + if ( orientation() == Horizontal ) { + int minpos = dockWindow->area()->mapToGlobal( QPoint( 0, 0 ) ).y(); + int maxpos = dockWindow->area()->mapToGlobal( QPoint( 0, 0 ) ).y() + dockWindow->area()->height(); + if ( e->globalPos().y() < minpos || e->globalPos().y() > maxpos ) + return; + } else { + int minpos = dockWindow->area()->mapToGlobal( QPoint( 0, 0 ) ).x(); + int maxpos = dockWindow->area()->mapToGlobal( QPoint( 0, 0 ) ).x() + dockWindow->area()->width(); + if ( e->globalPos().x() < minpos || e->globalPos().x() > maxpos ) + return; + } + } else { + QWidget *w = dockWindow->area()->topLevelWidget(); + if ( w ) { + if ( orientation() == Horizontal ) { + int minpos = w->mapToGlobal( QPoint( 0, 0 ) ).y(); + int maxpos = w->mapToGlobal( QPoint( 0, 0 ) ).y() + w->height(); + if ( e->globalPos().y() < minpos || e->globalPos().y() > maxpos ) + return; + } else { + int minpos = w->mapToGlobal( QPoint( 0, 0 ) ).x(); + int maxpos = w->mapToGlobal( QPoint( 0, 0 ) ).x() + w->width(); + if ( e->globalPos().x() < minpos || e->globalPos().x() > maxpos ) + return; + } + } + } + } + + if ( !dockWindow->opaqueMoving() ) + drawLine( lastPos ); + lastPos = e->globalPos(); + if ( dockWindow->opaqueMoving() ) { + mouseReleaseEvent( e ); + mousePressed = TRUE; + firstPos = e->globalPos(); + } + if ( !dockWindow->opaqueMoving() ) + drawLine( e->globalPos() ); +} + +void QDockWindowResizeHandle::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( mousePressed ) { + if ( !dockWindow->opaqueMoving() ) { + drawLine( lastPos ); + endLineDraw(); + } + if ( orientation() != dockWindow->area()->orientation() ) + dockWindow->area()->invalidNextOffset( dockWindow ); + if ( orientation() == Horizontal ) { + int dy; + if ( dockWindow->area()->handlePosition() == QDockArea::Normal || orientation() != dockWindow->area()->orientation() ) + dy = e->globalPos().y() - firstPos.y(); + else + dy = firstPos.y() - e->globalPos().y(); + int d = dockWindow->height() + dy; + if ( orientation() != dockWindow->area()->orientation() ) { + dockWindow->setFixedExtentHeight( -1 ); + d = QMAX( d, dockWindow->minimumHeight() ); + int ms = dockWindow->area()->maxSpace( d, dockWindow ); + d = QMIN( d, ms ); + dockWindow->setFixedExtentHeight( d ); + } else { + dockWindow->area()->setFixedExtent( d, dockWindow ); + } + } else { + int dx; + if ( dockWindow->area()->handlePosition() == QDockArea::Normal || orientation() != dockWindow->area()->orientation() ) + dx = e->globalPos().x() - firstPos.x(); + else + dx = firstPos.x() - e->globalPos().x(); + int d = dockWindow->width() + dx; + if ( orientation() != dockWindow->area()->orientation() ) { + dockWindow->setFixedExtentWidth( -1 ); + d = QMAX( d, dockWindow->minimumWidth() ); + int ms = dockWindow->area()->maxSpace( d, dockWindow ); + d = QMIN( d, ms ); + dockWindow->setFixedExtentWidth( d ); + } else { + dockWindow->area()->setFixedExtent( d, dockWindow ); + } + } + } + + QApplication::postEvent( dockWindow->area(), new QEvent( QEvent::LayoutHint ) ); + mousePressed = FALSE; +} + +void QDockWindowResizeHandle::paintEvent( QPaintEvent * ) +{ + QPainter p( this ); + style().drawPrimitive(QStyle::PE_DockWindowResizeHandle, &p, rect(), colorGroup(), + (isEnabled() ? + QStyle::Style_Enabled : QStyle::Style_Default) | + (orientation() == Qt::Horizontal ? + QStyle::Style_Horizontal : QStyle::Style_Default )); +} + +void QDockWindowResizeHandle::startLineDraw() +{ + if ( unclippedPainter ) + endLineDraw(); +#ifdef MAC_DRAG_HACK + QWidget *paint_on = topLevelWidget(); +#else + int scr = QApplication::desktop()->screenNumber( this ); + QWidget *paint_on = QApplication::desktop()->screen( scr ); +#endif + unclippedPainter = new QPainter( paint_on, TRUE ); + unclippedPainter->setPen( QPen( gray, orientation() == Horizontal ? height() : width() ) ); + unclippedPainter->setRasterOp( XorROP ); +} + +void QDockWindowResizeHandle::endLineDraw() +{ + if ( !unclippedPainter ) + return; + delete unclippedPainter; + unclippedPainter = 0; +} + +void QDockWindowResizeHandle::drawLine( const QPoint &globalPos ) +{ +#ifdef MAC_DRAG_HACK + QPoint start = mapTo(topLevelWidget(), QPoint(0, 0)); + QPoint starta = dockWindow->area()->mapTo(topLevelWidget(), QPoint(0, 0)); + QPoint end = globalPos - topLevelWidget()->pos(); +#else + QPoint start = mapToGlobal( QPoint( 0, 0 ) ); + QPoint starta = dockWindow->area()->mapToGlobal( QPoint( 0, 0 ) ); + QPoint end = globalPos; +#endif + + if ( orientation() == Horizontal ) { + if ( orientation() == dockWindow->orientation() ) + unclippedPainter->drawLine( starta.x() , end.y(), starta.x() + dockWindow->area()->width(), end.y() ); + else + unclippedPainter->drawLine( start.x(), end.y(), start.x() + width(), end.y() ); + } else { + if ( orientation() == dockWindow->orientation() ) + unclippedPainter->drawLine( end.x(), starta.y(), end.x(), starta.y() + dockWindow->area()->height() ); + else + unclippedPainter->drawLine( end.x(), start.y(), end.x(), start.y() + height() ); + } +} + +static QPoint realWidgetPos( QDockWindow *w ) +{ + if ( !w->parentWidget() || w->place() == QDockWindow::OutsideDock ) + return w->pos(); + return w->parentWidget()->mapToGlobal( w->geometry().topLeft() ); +} + +class QDockWindowHandle : public QWidget +{ + Q_OBJECT + Q_PROPERTY( QString caption READ caption ) + friend class QDockWindow; + friend class QDockWindowTitleBar; + +public: + QDockWindowHandle( QDockWindow *dw ); + void updateGui(); + + QSize minimumSizeHint() const; + QSize minimumSize() const { return minimumSizeHint(); } + QSize sizeHint() const { return minimumSize(); } + QSizePolicy sizePolicy() const; + void setOpaqueMoving( bool b ) { opaque = b; } + + QString caption() const { return dockWindow->caption(); } + +signals: + void doubleClicked(); + +protected: + void paintEvent( QPaintEvent *e ); + void resizeEvent( QResizeEvent *e ); + void mousePressEvent( QMouseEvent *e ); + void mouseMoveEvent( QMouseEvent *e ); + void mouseReleaseEvent( QMouseEvent *e ); + void mouseDoubleClickEvent( QMouseEvent *e ); + void keyPressEvent( QKeyEvent *e ); + void keyReleaseEvent( QKeyEvent *e ); +#ifndef QT_NO_STYLE + void styleChange( QStyle& ); +#endif + +private slots: + void minimize(); + +private: + QDockWindow *dockWindow; + QPoint offset; + QToolButton *closeButton; + QTimer *timer; + uint opaque : 1; + uint mousePressed : 1; + uint hadDblClick : 1; + uint ctrlDown : 1; + QGuardedPtr<QWidget> oldFocus; +}; + +class QDockWindowTitleBar : public QTitleBar +{ + Q_OBJECT + friend class QDockWindow; + friend class QDockWindowHandle; + +public: + QDockWindowTitleBar( QDockWindow *dw ); + void updateGui(); + void setOpaqueMoving( bool b ) { opaque = b; } + +protected: + void resizeEvent( QResizeEvent *e ); + void mousePressEvent( QMouseEvent *e ); + void mouseMoveEvent( QMouseEvent *e ); + void mouseReleaseEvent( QMouseEvent *e ); + void mouseDoubleClickEvent( QMouseEvent *e ); + void keyPressEvent( QKeyEvent *e ); + void keyReleaseEvent( QKeyEvent *e ); + +private: + QDockWindow *dockWindow; + QPoint offset; + uint mousePressed : 1; + uint hadDblClick : 1; + uint opaque : 1; + uint ctrlDown : 1; + QGuardedPtr<QWidget> oldFocus; + +}; + +QDockWindowHandle::QDockWindowHandle( QDockWindow *dw ) + : QWidget( dw, "qt_dockwidget_internal", WNoAutoErase ), dockWindow( dw ), + closeButton( 0 ), opaque( default_opaque ), mousePressed( FALSE ) +{ + ctrlDown = FALSE; + timer = new QTimer( this ); + connect( timer, SIGNAL( timeout() ), this, SLOT( minimize() ) ); +#ifdef Q_WS_WIN + setCursor( SizeAllCursor ); +#endif +} + +void QDockWindowHandle::paintEvent( QPaintEvent *e ) +{ + if ( (!dockWindow->dockArea || mousePressed) && !opaque ) + return; + erase(); + QPainter p( this ); + QStyle::SFlags flags = QStyle::Style_Default; + if ( isEnabled() ) + flags |= QStyle::Style_Enabled; + if ( !dockWindow->area() || dockWindow->area()->orientation() == Horizontal ) + flags |= QStyle::Style_Horizontal; + + style().drawPrimitive( QStyle::PE_DockWindowHandle, &p, + QStyle::visualRect( style().subRect( QStyle::SR_DockWindowHandleRect, + this ), this ), + colorGroup(), flags ); + QWidget::paintEvent( e ); +} + +void QDockWindowHandle::keyPressEvent( QKeyEvent *e ) +{ + if ( !mousePressed ) + return; + if ( e->key() == Key_Control ) { + ctrlDown = TRUE; + dockWindow->handleMove( mapFromGlobal(QCursor::pos()) - offset, QCursor::pos(), !opaque ); + } +} + +void QDockWindowHandle::keyReleaseEvent( QKeyEvent *e ) +{ + if ( !mousePressed ) + return; + if ( e->key() == Key_Control ) { + ctrlDown = FALSE; + dockWindow->handleMove( mapFromGlobal(QCursor::pos()) - offset, QCursor::pos(), !opaque ); + } +} + +void QDockWindowHandle::mousePressEvent( QMouseEvent *e ) +{ + if ( !dockWindow->dockArea ) + return; + ctrlDown = ( e->state() & ControlButton ) == ControlButton; + oldFocus = qApp->focusWidget(); + setFocus(); + e->ignore(); + if ( e->button() != LeftButton ) + return; + e->accept(); + hadDblClick = FALSE; + mousePressed = TRUE; + offset = e->pos(); + dockWindow->startRectDraw( mapToGlobal( e->pos() ), !opaque ); + if ( !opaque ) + qApp->installEventFilter( dockWindow ); +} + +void QDockWindowHandle::mouseMoveEvent( QMouseEvent *e ) +{ + if ( !mousePressed || e->pos() == offset ) + return; + ctrlDown = ( e->state() & ControlButton ) == ControlButton; + dockWindow->handleMove( e->pos() - offset, e->globalPos(), !opaque ); + if ( opaque ) + dockWindow->updatePosition( e->globalPos() ); +} + +void QDockWindowHandle::mouseReleaseEvent( QMouseEvent *e ) +{ + ctrlDown = FALSE; + qApp->removeEventFilter( dockWindow ); + if ( oldFocus ) + oldFocus->setFocus(); + if ( !mousePressed ) + return; + dockWindow->endRectDraw( !opaque ); + mousePressed = FALSE; +#ifdef Q_WS_MAC + releaseMouse(); +#endif + if ( !hadDblClick && offset == e->pos() ) { + timer->start( QApplication::doubleClickInterval(), TRUE ); + } else if ( !hadDblClick ) { + dockWindow->updatePosition( e->globalPos() ); + } + if ( opaque ) + dockWindow->titleBar->mousePressed = FALSE; +} + +void QDockWindowHandle::minimize() +{ + if ( !dockWindow->area() ) + return; + + QMainWindow *mw = ::qt_cast<QMainWindow*>(dockWindow->area()->parentWidget()); + if ( mw && mw->isDockEnabled( dockWindow, Qt::DockMinimized ) ) + mw->moveDockWindow( dockWindow, Qt::DockMinimized ); +} + +void QDockWindowHandle::resizeEvent( QResizeEvent * ) +{ + updateGui(); +} + +void QDockWindowHandle::updateGui() +{ + if ( !closeButton ) { + closeButton = new QToolButton( this, "qt_close_button1" ); +#ifndef QT_NO_CURSOR + closeButton->setCursor( arrowCursor ); +#endif + closeButton->setPixmap( style().stylePixmap( QStyle::SP_DockWindowCloseButton, closeButton ) ); + closeButton->setFixedSize( 12, 12 ); + connect( closeButton, SIGNAL( clicked() ), + dockWindow, SLOT( hide() ) ); + } + + if ( dockWindow->isCloseEnabled() && dockWindow->area() ) + closeButton->show(); + else + closeButton->hide(); + + if ( !dockWindow->area() ) + return; + + if ( dockWindow->area()->orientation() == Horizontal ) { + int off = ( width() - closeButton->width() - 1 ) / 2; + closeButton->move( off, 2 ); + } else { + int off = ( height() - closeButton->height() - 1 ) / 2; + int x = QApplication::reverseLayout() ? 2 : width() - closeButton->width() - 2; + closeButton->move( x, off ); + } +} + +#ifndef QT_NO_STYLE +void QDockWindowHandle::styleChange( QStyle& ) +{ + if ( closeButton ) + closeButton->setPixmap( style().stylePixmap( QStyle::SP_DockWindowCloseButton, closeButton ) ); +} +#endif + +QSize QDockWindowHandle::minimumSizeHint() const +{ + if ( !dockWindow->dockArea ) + return QSize( 0, 0 ); + int wh = dockWindow->isCloseEnabled() ? 17 : style().pixelMetric( QStyle::PM_DockWindowHandleExtent, this ); + if ( dockWindow->orientation() == Horizontal ) + return QSize( wh, 0 ); + return QSize( 0, wh ); +} + +QSizePolicy QDockWindowHandle::sizePolicy() const +{ + if ( dockWindow->orientation() != Horizontal ) + return QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ); + return QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred ); +} + +void QDockWindowHandle::mouseDoubleClickEvent( QMouseEvent *e ) +{ + e->ignore(); + if ( e->button() != LeftButton ) + return; + e->accept(); + timer->stop(); + emit doubleClicked(); + hadDblClick = TRUE; +} + +QDockWindowTitleBar::QDockWindowTitleBar( QDockWindow *dw ) + : QTitleBar( 0, dw, "qt_dockwidget_internal" ), dockWindow( dw ), + mousePressed( FALSE ), hadDblClick( FALSE ), opaque( default_opaque ) +{ + setWFlags( getWFlags() | WStyle_Tool ); + ctrlDown = FALSE; + setMouseTracking( TRUE ); + setFixedHeight( style().pixelMetric( QStyle::PM_TitleBarHeight, this ) ); + connect( this, SIGNAL(doClose()), dockWindow, SLOT(hide()) ); +} + +void QDockWindowTitleBar::keyPressEvent( QKeyEvent *e ) +{ + if ( !mousePressed ) + return; + if ( e->key() == Key_Control ) { + ctrlDown = TRUE; + dockWindow->handleMove( mapFromGlobal( QCursor::pos() ) - offset, QCursor::pos(), !opaque ); + } +} + +void QDockWindowTitleBar::keyReleaseEvent( QKeyEvent *e ) +{ + if ( !mousePressed ) + return; + if ( e->key() == Key_Control ) { + ctrlDown = FALSE; + dockWindow->handleMove( mapFromGlobal( QCursor::pos() ) - offset, QCursor::pos(), !opaque ); + } +} + +void QDockWindowTitleBar::mousePressEvent( QMouseEvent *e ) +{ + QStyle::SubControl tbctrl = style().querySubControl( QStyle::CC_TitleBar, this, e->pos() ); + if ( tbctrl > QStyle::SC_TitleBarLabel ) { + QTitleBar::mousePressEvent( e ); + return; + } + + ctrlDown = ( e->state() & ControlButton ) == ControlButton; + oldFocus = qApp->focusWidget(); +// setFocus activates the window, which deactivates the main window +// not what we want, and not required anyway on Windows +#ifndef Q_WS_WIN + setFocus(); +#endif + + e->ignore(); + if ( e->button() != LeftButton ) + return; + if ( e->y() < 3 && dockWindow->isResizeEnabled() ) + return; + + e->accept(); + bool oldPressed = mousePressed; + mousePressed = TRUE; + hadDblClick = FALSE; + offset = e->pos(); + dockWindow->startRectDraw( mapToGlobal( e->pos() ), !opaque ); +// grabMouse resets the Windows mouse press count, so we never receive a double click on Windows +// not required on Windows, and did work on X11, too, but no problem there in the first place +#ifndef Q_WS_WIN + if(!oldPressed && dockWindow->opaqueMoving()) + grabMouse(); +#else + Q_UNUSED( oldPressed ); +#endif +} + +void QDockWindowTitleBar::mouseMoveEvent( QMouseEvent *e ) +{ + if ( !mousePressed ) { + QTitleBar::mouseMoveEvent( e ); + return; + } + + ctrlDown = ( e->state() & ControlButton ) == ControlButton; + e->accept(); + dockWindow->handleMove( e->pos() - offset, e->globalPos(), !opaque ); +} + +void QDockWindowTitleBar::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( !mousePressed ) { + QTitleBar::mouseReleaseEvent( e ); + return; + } + + ctrlDown = FALSE; + qApp->removeEventFilter( dockWindow ); + if ( oldFocus ) + oldFocus->setFocus(); + + if ( dockWindow->place() == QDockWindow::OutsideDock ) + dockWindow->raise(); + + if(dockWindow->opaqueMoving()) + releaseMouse(); + if ( !mousePressed ) + return; + dockWindow->endRectDraw( !opaque ); + mousePressed = FALSE; + if ( !hadDblClick ) + dockWindow->updatePosition( e->globalPos() ); + if ( opaque ) { + dockWindow->horHandle->mousePressed = FALSE; + dockWindow->verHandle->mousePressed = FALSE; + } +} + +void QDockWindowTitleBar::resizeEvent( QResizeEvent *e ) +{ + updateGui(); + QTitleBar::resizeEvent( e ); +} + +void QDockWindowTitleBar::updateGui() +{ + if ( dockWindow->isCloseEnabled() ) { + setWFlags( getWFlags() | WStyle_SysMenu ); + } else { + setWFlags( getWFlags() & ~WStyle_SysMenu ); + } +} + +void QDockWindowTitleBar::mouseDoubleClickEvent( QMouseEvent * ) +{ + emit doubleClicked(); + hadDblClick = TRUE; +} + +/*! + \class QDockWindow qdockwindow.h + \brief The QDockWindow class provides a widget which can be docked + inside a QDockArea or floated as a top level window on the + desktop. + + \ingroup application + \mainclass + + This class handles moving, resizing, docking and undocking dock + windows. QToolBar is a subclass of QDockWindow so the + functionality provided for dock windows is available with the same + API for toolbars. + + \img qmainwindow-qdockareas.png QDockWindows in a QDockArea + \caption Two QDockWindows (\l{QToolBar}s) in a \l QDockArea + + \img qdockwindow.png A QDockWindow + \caption A Floating QDockWindow + + If the user drags the dock window into the dock area the dock + window will be docked. If the user drags the dock area outside any + dock areas the dock window will be undocked (floated) and will + become a top level window. Double clicking a floating dock + window's titlebar will dock the dock window to the last dock area + it was docked in. Double clicking a docked dock window's handle + will undock (float) the dock window. + \omit + Single clicking a docked dock window's handle will minimize the + dock window (only its handle will appear, below the menu bar). + Single clicking the minimized handle will restore the dock window + to the last dock area that it was docked in. + \endomit + If the user clicks the close button (which does not appear on + dock windows by default - see \l closeMode) the dock window will + disappear. You can control whether or not a dock window has a + close button with setCloseMode(). + + QMainWindow provides four dock areas (top, left, right and bottom) + which can be used by dock windows. For many applications using the + dock areas provided by QMainWindow is sufficient. (See the \l + QDockArea documentation if you want to create your own dock + areas.) In QMainWindow a right-click popup menu (the dock window + menu) is available which lists dock windows and can be used to + show or hide them. (The popup menu only lists dock windows that + have a \link setCaption() caption\endlink.) + + When you construct a dock window you \e must pass it a QDockArea + or a QMainWindow as its parent if you want it docked. Pass 0 for + the parent if you want it floated. + + \code + QToolBar *fileTools = new QToolBar( this, "File Actions" ); + moveDockWindow( fileTools, Left ); + \endcode + + In the example above we create a new QToolBar in the constructor + of a QMainWindow subclass (so that the \e this pointer points to + the QMainWindow). By default the toolbar will be added to the \c + Top dock area, but we've moved it to the \c Left dock area. + + A dock window is often used to contain a single widget. In these + cases the widget can be set by calling setWidget(). If you're + constructing a dock window that contains multiple widgets, e.g. a + toolbar, arrange the widgets within a box layout inside the dock + window. To do this use the boxLayout() function to get a pointer + to the dock window's box layout, then add widgets to the layout + using the box layout's QBoxLayout::addWidget() function. The dock + window will dynamically set the orientation of the layout to be + vertical or horizontal as necessary, although you can control this + yourself with setOrientation(). + + Although a common use of dock windows is for toolbars, they can be + used with any widgets. (See the \link designer-manual.book Qt + Designer\endlink and \link linguist-manual.book Qt + Linguist\endlink applications, for example.) When using larger + widgets it may make sense for the dock window to be resizable by + calling setResizeEnabled(). Resizable dock windows are given + splitter-like handles to allow the user to resize them within + their dock area. When resizable dock windows are undocked they + become top level windows and can be resized like any other top + level windows, e.g. by dragging a corner or edge. + + Dock windows can be docked and undocked using dock() and undock(). + A dock window's orientation can be set with setOrientation(). You + can also use QDockArea::moveDockWindow(). If you're using a + QMainWindow, QMainWindow::moveDockWindow() and + QMainWindow::removeDockWindow() are available. + + A dock window can have some preferred settings, for example, you + can set a preferred offset from the left edge (or top edge for + vertical dock areas) of the dock area using setOffset(). If you'd + prefer a dock window to start on a new \link qdockarea.html#lines + line\endlink when it is docked use setNewLine(). The + setFixedExtentWidth() and setFixedExtentHeight() functions can be + used to define the dock window's preferred size, and the + setHorizontallyStretchable() and setVerticallyStretchable() + functions set whether the dock window can be stretched or not. + Dock windows can be moved by default, but this can be changed with + setMovingEnabled(). When a dock window is moved it is shown as a + rectangular outline, but it can be shown normally using + setOpaqueMoving(). + + When a dock window's visibility changes, i.e. it is shown or + hidden, the visibilityChanged() signal is emitted. When a dock + window is docked, undocked or moved inside the dock area the + placeChanged() signal is emitted. +*/ + +/*! + \enum QDockWindow::Place + + This enum specifies the possible locations for a QDockWindow: + + \value InDock Inside a QDockArea. + \value OutsideDock Floating as a top level window on the desktop. +*/ + +/*! + \enum QDockWindow::CloseMode + + This enum type specifies when (if ever) a dock window has a close + button. + + \value Never The dock window never has a close button and cannot + be closed by the user. + \value Docked The dock window has a close button only when + docked. + \value Undocked The dock window has a close button only when + floating. + \value Always The dock window always has a close button. + \omit + Note that dock windows can always be minimized if the user clicks + their dock window handle when they are docked. + \endomit +*/ + +/*! + \fn void QDockWindow::setHorizontalStretchable( bool b ) + \obsolete +*/ +/*! + \fn void QDockWindow::setVerticalStretchable( bool b ) + \obsolete +*/ +/*! + \fn bool QDockWindow::isHorizontalStretchable() const + \obsolete +*/ +/*! + \fn bool QDockWindow::isVerticalStretchable() const + \obsolete +*/ +/*! + \fn void QDockWindow::orientationChanged( Orientation o ) + + This signal is emitted when the orientation of the dock window is + changed. The new orientation is \a o. +*/ + +/*! + \fn void QDockWindow::placeChanged( QDockWindow::Place p ) + + This signal is emitted when the dock window is docked (\a p is \c + InDock), undocked (\a p is \c OutsideDock) or moved inside the + the dock area. + + \sa QDockArea::moveDockWindow(), QDockArea::removeDockWindow(), + QMainWindow::moveDockWindow(), QMainWindow::removeDockWindow() +*/ + +/*! + \fn void QDockWindow::visibilityChanged( bool visible ) + + This signal is emitted when the visibility of the dock window + relatively to its dock area is changed. If \a visible is TRUE, the + QDockWindow is now visible to the dock area, otherwise it has been + hidden. + + A dock window can be hidden if it has a close button which the + user has clicked. In the case of a QMainWindow a dock window can + have its visibility changed (hidden or shown) by clicking its name + in the dock window menu that lists the QMainWindow's dock windows. +*/ + +/*! + \fn QDockArea *QDockWindow::area() const + + Returns the dock area in which this dock window is docked, or 0 if + the dock window is floating. +*/ + +// DOC: Can't use \property 'cos it thinks the thing returns a bool. +/*! + \fn Place QDockWindow::place() const + + This function returns where the dock window is placed. This is + either \c InDock or \c OutsideDock. + + \sa QDockArea::moveDockWindow(), QDockArea::removeDockWindow(), + QMainWindow::moveDockWindow(), QMainWindow::removeDockWindow() +*/ + + +/*! + Constructs a QDockWindow with parent \a parent, called \a name and + with widget flags \a f. +*/ + +QDockWindow::QDockWindow( QWidget* parent, const char* name, WFlags f ) + : QFrame( parent, name, f | WType_Dialog | WStyle_Customize | WStyle_NoBorder ) +{ + curPlace = InDock; + isToolbar = FALSE; + init(); +} + +/*! + Constructs a QDockWindow with parent \a parent, called \a name and + with widget flags \a f. + + If \a p is \c InDock, the dock window is docked into a dock area + and \a parent \e must be a QDockArea or a QMainWindow. If the \a + parent is a QMainWindow the dock window will be docked in the main + window's \c Top dock area. + + If \a p is \c OutsideDock, the dock window is created as a floating + window. + + We recommend creating the dock area \c InDock with a QMainWindow + as parent then calling QMainWindow::moveDockWindow() to move the + dock window where you want it. +*/ + +QDockWindow::QDockWindow( Place p, QWidget *parent, const char *name, WFlags f ) + : QFrame( parent, name, f | WType_Dialog | WStyle_Customize | WStyle_NoBorder ) +{ + curPlace = p; + isToolbar = FALSE; + init(); +} + +/*! \internal +*/ + +QDockWindow::QDockWindow( Place p, QWidget *parent, const char *name, WFlags f, bool toolbar ) + : QFrame( parent, name, f | WType_Dialog | WStyle_Customize | WStyle_NoBorder ) +{ + curPlace = p; + isToolbar = toolbar; + init(); +} + +class QDockWindowGridLayout : public QGridLayout +{ +public: + QDockWindowGridLayout( QWidget *parent, int nRows, int nCols ) + : QGridLayout( parent, nRows, nCols ) {}; + + QSizePolicy::ExpandData expanding() const + { + return QSizePolicy::NoDirection; + } +}; + +void QDockWindow::init() +{ + wid = 0; + unclippedPainter = 0; + dockArea = 0; + tmpDockArea = 0; + resizeEnabled = FALSE; + moveEnabled = TRUE; + nl = FALSE; + opaque = default_opaque; + cMode = Never; + offs = 0; + fExtent = QSize( -1, -1 ); + dockWindowData = 0; + lastPos = QPoint( -1, -1 ); + lastSize = QSize( -1, -1 ); + + widgetResizeHandler = new QWidgetResizeHandler( this ); + widgetResizeHandler->setMovingEnabled( FALSE ); + + titleBar = new QDockWindowTitleBar( this ); + verHandle = new QDockWindowHandle( this ); + horHandle = new QDockWindowHandle( this ); + + vHandleLeft = new QDockWindowResizeHandle( Qt::Vertical, this, this, "vert. handle" ); + vHandleRight = new QDockWindowResizeHandle( Qt::Vertical, this, this, "vert. handle" ); + hHandleTop = new QDockWindowResizeHandle( Qt::Horizontal, this, this, "horz. handle" ); + hHandleBottom = new QDockWindowResizeHandle( Qt::Horizontal, this, this, "horz. handle" ); + + // Creating inner layout + hbox = new QVBoxLayout(); + vbox = new QHBoxLayout(); + childBox = new QBoxLayout(QBoxLayout::LeftToRight); + vbox->addWidget( verHandle ); + vbox->addLayout( childBox ); + + hbox->setResizeMode( QLayout::FreeResize ); + hbox->setMargin( isResizeEnabled() || curPlace == OutsideDock ? 2 : 0 ); + hbox->setSpacing( 1 ); + hbox->addWidget( titleBar ); + hbox->addWidget( horHandle ); + hbox->addLayout( vbox ); + + // Set up the initial handle layout for Vertical + // Handle layout will change on calls to setOrienation() + QGridLayout *glayout = new QDockWindowGridLayout( this, 3, 3 ); + glayout->setResizeMode( QLayout::Minimum ); + glayout->addMultiCellWidget( hHandleTop, 0, 0, 1, 1 ); + glayout->addMultiCellWidget( hHandleBottom, 2, 2, 1, 1 ); + glayout->addMultiCellWidget( vHandleLeft, 0, 2, 0, 0 ); + glayout->addMultiCellWidget( vHandleRight, 0, 2, 2, 2 ); + glayout->addLayout( hbox, 1, 1 ); + glayout->setRowStretch( 1, 1 ); + glayout->setColStretch( 1, 1 ); + + hHandleBottom->hide(); + vHandleRight->hide(); + hHandleTop->hide(); + vHandleLeft->hide(); + setFrameStyle( QFrame::StyledPanel | QFrame::Raised ); + setLineWidth( 2 ); + + if ( parentWidget() ) + parentWidget()->installEventFilter( this ); + QWidget *mw = parentWidget(); + QDockArea *da = ::qt_cast<QDockArea*>(parentWidget()); + if ( da ) { + if ( curPlace == InDock ) + da->moveDockWindow( this ); + mw = da->parentWidget(); + } + if ( ::qt_cast<QMainWindow*>(mw) ) { + if ( place() == InDock ) { + Dock myDock = Qt::DockTop; + // make sure we put the window in the correct dock. + if ( dockArea ) { + QMainWindow *mainw = (QMainWindow*)mw; + // I'm not checking if it matches the top because I've + // done the assignment to it above. + if ( dockArea == mainw->leftDock() ) + myDock = Qt::DockLeft; + else if ( dockArea == mainw->rightDock() ) + myDock = Qt::DockRight; + else if ( dockArea == mainw->bottomDock() ) + myDock = Qt::DockBottom; + } + ( (QMainWindow*)mw )->addDockWindow( this, myDock ); + } + moveEnabled = ((QMainWindow*)mw)->dockWindowsMovable(); + opaque = ((QMainWindow*)mw)->opaqueMoving(); + } + + updateGui(); + stretchable[ Horizontal ] = FALSE; + stretchable[ Vertical ] = FALSE; + + connect( titleBar, SIGNAL( doubleClicked() ), this, SLOT( dock() ) ); + connect( verHandle, SIGNAL( doubleClicked() ), this, SLOT( undock() ) ); + connect( horHandle, SIGNAL( doubleClicked() ), this, SLOT( undock() ) ); + connect( this, SIGNAL( orientationChanged(Orientation) ), + this, SLOT( setOrientation(Orientation) ) ); +} + +/*! + Sets the orientation of the dock window to \a o. The orientation + is propagated to the layout boxLayout(). + + \warning All undocked QToolBars will always have a horizontal orientation. +*/ + +void QDockWindow::setOrientation( Orientation o ) +{ + QGridLayout *glayout = (QGridLayout*)layout(); + glayout->remove( hHandleTop ); + glayout->remove( hHandleBottom ); + glayout->remove( vHandleLeft ); + glayout->remove( vHandleRight ); + + if ( o == Horizontal ) { + // Set up the new layout as + // 3 3 3 1 = vHandleLeft 4 = hHandleBottom + // 1 X 2 2 = vHandleRight X = Inner Layout + // 4 4 4 3 = hHandleTop + glayout->addMultiCellWidget( hHandleTop, 0, 0, 0, 2 ); + glayout->addMultiCellWidget( hHandleBottom, 2, 2, 0, 2 ); + glayout->addMultiCellWidget( vHandleLeft, 1, 1, 0, 0 ); + glayout->addMultiCellWidget( vHandleRight, 1, 1, 2, 2 ); + } else { + // Set up the new layout as + // 1 3 2 1 = vHandleLeft 4 = hHandleBottom + // 1 X 2 2 = vHandleRight X = Inner Layout + // 1 4 2 3 = hHandleTop + glayout->addMultiCellWidget( hHandleTop, 0, 0, 1, 1 ); + glayout->addMultiCellWidget( hHandleBottom, 2, 2, 1, 1 ); + glayout->addMultiCellWidget( vHandleLeft, 0, 2, 0, 0 ); + glayout->addMultiCellWidget( vHandleRight, 0, 2, 2, 2 ); + } + boxLayout()->setDirection( o == Horizontal ? QBoxLayout::LeftToRight : QBoxLayout::TopToBottom ); + QApplication::sendPostedEvents( this, QEvent::LayoutHint ); + QEvent *e = new QEvent( QEvent::LayoutHint ); + QApplication::postEvent( this, e ); +} + +/*! + \reimp + + Destroys the dock window and its child widgets. +*/ + +QDockWindow::~QDockWindow() +{ + qApp->removeEventFilter( this ); + if ( area() ) + area()->removeDockWindow( this, FALSE, FALSE ); + QDockArea *a = area(); + if ( !a && dockWindowData ) + a = ( (QDockArea::DockWindowData*)dockWindowData )->area; + QMainWindow *mw = a ? ::qt_cast<QMainWindow*>(a->parentWidget()) : 0; + if ( mw ) + mw->removeDockWindow( this ); + + delete (QDockArea::DockWindowData*)dockWindowData; +} + +/*! \reimp +*/ + +void QDockWindow::resizeEvent( QResizeEvent *e ) +{ + QFrame::resizeEvent( e ); + updateGui(); +} + + +void QDockWindow::swapRect( QRect &r, Qt::Orientation o, const QPoint &offset, QDockArea * ) +{ + QBoxLayout *bl = boxLayout()->createTmpCopy(); + bl->setDirection( o == Horizontal ? QBoxLayout::LeftToRight : QBoxLayout::TopToBottom ); + bl->activate(); + r.setSize( bl->sizeHint() ); + bl->data = 0; + delete bl; + bool reverse = QApplication::reverseLayout(); + if ( o == Qt::Horizontal ) + r.moveBy( -r.width()/2, 0 ); + else + r.moveBy( reverse ? - r.width() : 0, -r.height() / 2 ); + r.moveBy( offset.x(), offset.y() ); +} + +QWidget *QDockWindow::areaAt( const QPoint &gp ) +{ + QWidget *w = qApp->widgetAt( gp, TRUE ); + + if ( w && ( w == this || w == titleBar ) && parentWidget() ) + w = parentWidget()->childAt( parentWidget()->mapFromGlobal( gp ) ); + + while ( w ) { + if ( ::qt_cast<QDockArea*>(w) ) { + QDockArea *a = (QDockArea*)w; + if ( a->isDockWindowAccepted( this ) ) + return w; + } + if ( ::qt_cast<QMainWindow*>(w) ) { + QMainWindow *mw = (QMainWindow*)w; + QDockArea *a = mw->dockingArea( mw->mapFromGlobal( gp ) ); + if ( a && a->isDockWindowAccepted( this ) ) + return a; + } + w = w->parentWidget( TRUE ); + } + return 0; +} + +void QDockWindow::handleMove( const QPoint &pos, const QPoint &gp, bool drawRect ) +{ + if ( !unclippedPainter ) + return; + + if ( drawRect ) { + QRect dr(currRect); +#ifdef MAC_DRAG_HACK + dr.moveBy(-topLevelWidget()->geometry().x(), -topLevelWidget()->geometry().y()); +#endif + unclippedPainter->drawRect( dr ); + } + currRect = QRect( realWidgetPos( this ), size() ); + QWidget *w = areaAt( gp ); + if ( titleBar->ctrlDown || horHandle->ctrlDown || verHandle->ctrlDown ) + w = 0; + currRect.moveBy( pos.x(), pos.y() ); + if ( !::qt_cast<QDockArea*>(w) ) { + if ( startOrientation != Horizontal && ::qt_cast<QToolBar*>(this) ) + swapRect( currRect, Horizontal, startOffset, (QDockArea*)w ); + if ( drawRect ) { + unclippedPainter->setPen( QPen( gray, 3 ) ); + QRect dr(currRect); +#ifdef MAC_DRAG_HACK + dr.moveBy(-topLevelWidget()->geometry().x(), -topLevelWidget()->geometry().y()); +#endif + unclippedPainter->drawRect( dr ); + } else { + QPoint mp( mapToGlobal( pos )); + if(place() == InDock) { + undock(); + if(titleBar) { + mp = QPoint(titleBar->width() / 2, titleBar->height() / 2); + QMouseEvent me(QEvent::MouseButtonPress, mp, LeftButton, 0); + QApplication::sendEvent(titleBar, &me); + mp = titleBar->mapToGlobal( mp ); + } + } + move( mp ); + } + state = OutsideDock; + return; + } + + QDockArea *area = (QDockArea*)w; + if( area->isVisible() ) { + state = InDock; + Orientation o = ( area ? area->orientation() : + ( boxLayout()->direction() == QBoxLayout::LeftToRight || + boxLayout()->direction() == QBoxLayout::RightToLeft ? + Horizontal : Vertical ) ); + if ( startOrientation != o ) + swapRect( currRect, o, startOffset, area ); + if ( drawRect ) { + unclippedPainter->setPen( QPen( gray, 1 ) ); + QRect dr(currRect); +#ifdef MAC_DRAG_HACK + dr.moveBy(-topLevelWidget()->geometry().x(), -topLevelWidget()->geometry().y()); +#endif + unclippedPainter->drawRect( dr ); + } + tmpDockArea = area; + } +} + +void QDockWindow::updateGui() +{ + if ( curPlace == OutsideDock ) { + hbox->setMargin( 2 ); + horHandle->hide(); + verHandle->hide(); + if ( moveEnabled ) + titleBar->show(); + else + titleBar->hide(); + titleBar->updateGui(); + hHandleTop->hide(); + vHandleLeft->hide(); + hHandleBottom->hide(); + vHandleRight->hide(); + setLineWidth( 2 ); + widgetResizeHandler->setActive( isResizeEnabled() ); + } else { + hbox->setMargin( isResizeEnabled() ? 0 : 2 ); + titleBar->hide(); + if ( orientation() == Horizontal ) { + horHandle->hide(); + if ( moveEnabled ) + verHandle->show(); + else + verHandle->hide(); +#ifdef Q_WS_MAC + if(horHandle->mousePressed) { + horHandle->mousePressed = FALSE; + verHandle->mousePressed = TRUE; + verHandle->grabMouse(); + } +#endif + verHandle->updateGui(); + } else { + if ( moveEnabled ) + horHandle->show(); + else + horHandle->hide(); + horHandle->updateGui(); +#ifdef Q_WS_MAC + if(verHandle->mousePressed) { + verHandle->mousePressed = FALSE; + horHandle->mousePressed = TRUE; + horHandle->grabMouse(); + } +#endif + verHandle->hide(); + } + if ( isResizeEnabled() ) { + if ( orientation() == Horizontal ) { + hHandleBottom->raise(); + hHandleTop->raise(); + } else { + vHandleRight->raise(); + vHandleLeft->raise(); + } + + if ( area() ) { + if ( orientation() == Horizontal ) { + if ( area()->handlePosition() == QDockArea::Normal ) { + hHandleBottom->show(); + hHandleTop->hide(); + } else { + hHandleTop->show(); + hHandleBottom->hide(); + } + if ( !area()->isLastDockWindow( this ) ) + vHandleRight->show(); + else + vHandleRight->hide(); + vHandleLeft->hide(); + } else { + if ( (area()->handlePosition() == QDockArea::Normal) != QApplication::reverseLayout() ) { + vHandleRight->show(); + vHandleLeft->hide(); + } else { + vHandleLeft->show(); + vHandleRight->hide(); + } + if ( !area()->isLastDockWindow( this ) ) + hHandleBottom->show(); + else + hHandleBottom->hide(); + hHandleTop->hide(); + } + } + } else if ( area() ) { // hide resize handles if resizing is disabled + if ( orientation() == Horizontal ) { + hHandleTop->hide(); + hHandleBottom->hide(); + } else { + vHandleLeft->hide(); + vHandleRight->hide(); + } + } +#ifndef Q_OS_TEMP + if ( moveEnabled ) + setLineWidth( 1 ); + else + setLineWidth( 0 ); + hbox->setMargin( lineWidth() ); +#else + hbox->setMargin( 2 ); +#endif + widgetResizeHandler->setActive( FALSE ); + } +} + +void QDockWindow::updatePosition( const QPoint &globalPos ) +{ + if ( curPlace == OutsideDock && state == InDock ) + lastSize = size(); + + bool doAdjustSize = curPlace != state && state == OutsideDock; + bool doUpdate = TRUE; + bool doOrientationChange = TRUE; + if ( state != curPlace && state == InDock ) { + doUpdate = FALSE; + curPlace = state; + updateGui(); + QApplication::sendPostedEvents(); + } + Orientation oo = orientation(); + + if ( state == InDock ) { + if ( tmpDockArea ) { + bool differentDocks = FALSE; + if ( dockArea && dockArea != tmpDockArea ) { + differentDocks = TRUE; + delete (QDockArea::DockWindowData*)dockWindowData; + dockWindowData = dockArea->dockWindowData( this ); + dockArea->removeDockWindow( this, FALSE, FALSE ); + } + dockArea = tmpDockArea; + if ( differentDocks ) { + if ( doUpdate ) { + doUpdate = FALSE; + curPlace = state; + updateGui(); + } + emit orientationChanged( tmpDockArea->orientation() ); + doOrientationChange = FALSE; + } else { + updateGui(); + } + dockArea->moveDockWindow( this, globalPos, currRect, startOrientation != oo ); + } + } else { + if ( dockArea ) { + QMainWindow *mw = (QMainWindow*)dockArea->parentWidget(); + if ( ::qt_cast<QMainWindow*>(mw) && + ( !mw->isDockEnabled( QMainWindow::DockTornOff ) || + !mw->isDockEnabled( this, QMainWindow::DockTornOff ) ) ) + return; + delete (QDockArea::DockWindowData*)dockWindowData; + dockWindowData = dockArea->dockWindowData( this ); + dockArea->removeDockWindow( this, TRUE, + startOrientation != Horizontal && ::qt_cast<QToolBar*>(this) ); + } + dockArea = 0; + QPoint topLeft = currRect.topLeft(); + QRect screen = qApp->desktop()->availableGeometry( topLeft ); + if ( !screen.contains( topLeft ) ) { + topLeft.setY(QMAX(topLeft.y(), screen.top())); + topLeft.setY(QMIN(topLeft.y(), screen.bottom()-height())); + topLeft.setX(QMAX(topLeft.x(), screen.left())); + topLeft.setX(QMIN(topLeft.x(), screen.right()-width())); + } + move( topLeft ); + } + + if ( curPlace == InDock && state == OutsideDock && !::qt_cast<QToolBar*>(this) ) { + if ( lastSize != QSize( -1, -1 ) ) + resize( lastSize ); + } + + if ( doUpdate ) { + curPlace = state; + updateGui(); + } + if ( doOrientationChange ) + emit orientationChanged( orientation() ); + tmpDockArea = 0; + if ( doAdjustSize ) { + QApplication::sendPostedEvents( this, QEvent::LayoutHint ); + if ( ::qt_cast<QToolBar*>(this) ) + adjustSize(); + if (lastSize == QSize(-1, -1)) + clearWState(WState_Resized); // Ensures size is recalculated (non-opaque). + show(); + if ( parentWidget() && isTopLevel() ) + parentWidget()->setActiveWindow(); + + } + + emit placeChanged( curPlace ); +} + +/*! + Sets the dock window's main widget to \a w. + + \sa boxLayout() +*/ + +void QDockWindow::setWidget( QWidget *w ) +{ + wid = w; + boxLayout()->addWidget( w ); + updateGui(); +} + +/*! + Returns the dock window's main widget. + + \sa setWidget() +*/ + +QWidget *QDockWindow::widget() const +{ + return wid; +} + +void QDockWindow::startRectDraw( const QPoint &so, bool drawRect ) +{ + state = place(); + if ( unclippedPainter ) + endRectDraw( !opaque ); +#ifdef MAC_DRAG_HACK + QWidget *paint_on = topLevelWidget(); +#else + int scr = QApplication::desktop()->screenNumber( this ); + QWidget *paint_on = QApplication::desktop()->screen( scr ); +#endif + unclippedPainter = new QPainter( paint_on, TRUE ); + unclippedPainter->setPen( QPen( gray, curPlace == OutsideDock ? 3 : 1 ) ); + unclippedPainter->setRasterOp( XorROP ); + currRect = QRect( realWidgetPos( this ), size() ); + if ( drawRect ) { + QRect dr(currRect); +#ifdef MAC_DRAG_HACK + dr.moveBy(-topLevelWidget()->geometry().x(), -topLevelWidget()->geometry().y()); +#endif + unclippedPainter->drawRect( dr ); + } + startOrientation = orientation(); + startOffset = mapFromGlobal( so ); +} + +void QDockWindow::endRectDraw( bool drawRect ) +{ + if ( !unclippedPainter ) + return; + if ( drawRect ) { + QRect dr(currRect); +#ifdef MAC_DRAG_HACK + dr.moveBy(-topLevelWidget()->geometry().x(), -topLevelWidget()->geometry().y()); +#endif + unclippedPainter->drawRect( dr ); + } + delete unclippedPainter; + unclippedPainter = 0; +} + +/*! + \reimp +*/ +void QDockWindow::drawFrame( QPainter *p ) +{ + if ( place() == InDock ) { + QFrame::drawFrame( p ); + return; + } + + QStyle::SFlags flags = QStyle::Style_Default; + QStyleOption opt(lineWidth(),midLineWidth()); + + if ( titleBar->isActive() ) + flags |= QStyle::Style_Active; + + style().drawPrimitive( QStyle::PE_WindowFrame, p, rect(), colorGroup(), flags, opt ); +} + +/*! + \reimp +*/ +void QDockWindow::drawContents( QPainter *p ) +{ + QStyle::SFlags flags = QStyle::Style_Default; + if ( titleBar->isActive() ) + flags |= QStyle::Style_Active; + style().drawControl( QStyle::CE_DockWindowEmptyArea, p, this, + rect(), colorGroup(), flags ); +} + +/*! + \property QDockWindow::resizeEnabled + \brief whether the dock window is resizeable + + A resizeable dock window can be resized using splitter-like + handles inside a dock area and like every other top level window + when floating. + + A dock window is both horizontally and vertically stretchable if + you call setResizeEnabled(TRUE). + + This property is FALSE by default. + + \sa setVerticallyStretchable() setHorizontallyStretchable() +*/ + +void QDockWindow::setResizeEnabled( bool b ) +{ + resizeEnabled = b; + hbox->setMargin( b ? 0 : 2 ); + updateGui(); +} + +/*! + \property QDockWindow::movingEnabled + \brief whether the user can move the dock window within the dock + area, move the dock window to another dock area, or float the dock + window. + + This property is TRUE by default. +*/ + +void QDockWindow::setMovingEnabled( bool b ) +{ + moveEnabled = b; + updateGui(); +} + +bool QDockWindow::isResizeEnabled() const +{ + return resizeEnabled; +} + +bool QDockWindow::isMovingEnabled() const +{ + return moveEnabled; +} + +/*! + \property QDockWindow::closeMode + \brief the close mode of a dock window + + Defines when (if ever) the dock window has a close button. The + choices are \c Never, \c Docked (i.e. only when docked), \c + Undocked (only when undocked, i.e. floated) or \c Always. + + The default is \c Never. +*/ + +void QDockWindow::setCloseMode( int m ) +{ + cMode = m; + if ( place() == InDock ) { + horHandle->updateGui(); + verHandle->updateGui(); + } else { + titleBar->updateGui(); + } +} + +/*! + Returns TRUE if the dock window has a close button; otherwise + returns FALSE. The result depends on the dock window's \l Place + and its \l CloseMode. + + \sa setCloseMode() +*/ + +bool QDockWindow::isCloseEnabled() const +{ + return ( ( cMode & Docked ) == Docked && place() == InDock || + ( cMode & Undocked ) == Undocked && place() == OutsideDock ); +} + +int QDockWindow::closeMode() const +{ + return cMode; +} + +/*! + \property QDockWindow::horizontallyStretchable + \brief whether the dock window is horizontally stretchable. + + A dock window is horizontally stretchable if you call + setHorizontallyStretchable(TRUE) or setResizeEnabled(TRUE). + + \sa setResizeEnabled() + + \bug Strecthability is broken. You must call setResizeEnabled(TRUE) to get + proper behavior and even then QDockWindow does not limit stretchablilty. +*/ + +void QDockWindow::setHorizontallyStretchable( bool b ) +{ + stretchable[ Horizontal ] = b; +} + +/*! + \property QDockWindow::verticallyStretchable + \brief whether the dock window is vertically stretchable. + + A dock window is vertically stretchable if you call + setVerticallyStretchable(TRUE) or setResizeEnabled(TRUE). + + \sa setResizeEnabled() + + \bug Strecthability is broken. You must call setResizeEnabled(TRUE) to get + proper behavior and even then QDockWindow does not limit stretchablilty. +*/ + +void QDockWindow::setVerticallyStretchable( bool b ) +{ + stretchable[ Vertical ] = b; +} + +bool QDockWindow::isHorizontallyStretchable() const +{ + return isResizeEnabled() || stretchable[ Horizontal ]; +} + +bool QDockWindow::isVerticallyStretchable() const +{ + return isResizeEnabled() || stretchable[ Vertical ]; +} + +/*! + \property QDockWindow::stretchable + \brief whether the dock window is stretchable in the current + orientation() + + This property can be set using setHorizontallyStretchable() and + setVerticallyStretchable(), or with setResizeEnabled(). + + \sa setResizeEnabled() + + \bug Strecthability is broken. You must call setResizeEnabled(TRUE) to get + proper behavior and even then QDockWindow does not limit stretchablilty. +*/ + +bool QDockWindow::isStretchable() const +{ + if ( orientation() == Horizontal ) + return isHorizontallyStretchable(); + return isVerticallyStretchable(); +} + +/*! + Returns the orientation of the dock window. + + \sa orientationChanged() +*/ + +Qt::Orientation QDockWindow::orientation() const +{ + if ( dockArea ) + return dockArea->orientation(); + if ( ::qt_cast<QToolBar*>(this) ) + return Horizontal; + return ( ((QDockWindow*)this)->boxLayout()->direction() == QBoxLayout::LeftToRight || + ((QDockWindow*)this)->boxLayout()->direction() == QBoxLayout::RightToLeft ? + Horizontal : Vertical ); +} + +int QDockWindow::offset() const +{ + return offs; +} + +/*! + \property QDockWindow::offset + \brief the dock window's preferred offset from the dock area's + left edge (top edge for vertical dock areas) + + The default is 0. +*/ + +void QDockWindow::setOffset( int o ) +{ + offs = o; +} + +/*! + Returns the dock window's preferred size (fixed extent). + + \sa setFixedExtentWidth() setFixedExtentHeight() +*/ + +QSize QDockWindow::fixedExtent() const +{ + return fExtent; +} + +/*! + Sets the dock window's preferred width for its fixed extent (size) + to \a w. + + \sa setFixedExtentHeight() +*/ + +void QDockWindow::setFixedExtentWidth( int w ) +{ + fExtent.setWidth( w ); +} + +/*! + Sets the dock window's preferred height for its fixed extent + (size) to \a h. + + \sa setFixedExtentWidth() +*/ + +void QDockWindow::setFixedExtentHeight( int h ) +{ + fExtent.setHeight( h ); +} + +/*! + \property QDockWindow::newLine + \brief whether the dock window prefers to start a new line in the + dock area. + + The default is FALSE, i.e. the dock window doesn't require a new + line in the dock area. +*/ + +void QDockWindow::setNewLine( bool b ) +{ + nl = b; +} + +bool QDockWindow::newLine() const +{ + return nl; +} + +/*! + Returns the layout which is used for adding widgets to the dock + window. The layout's orientation is set automatically to match the + orientation of the dock window. You can add widgets to the layout + using the box layout's QBoxLayout::addWidget() function. + + If the dock window only needs to contain a single widget use + setWidget() instead. + + \sa setWidget() setOrientation() +*/ + +QBoxLayout *QDockWindow::boxLayout() +{ + return childBox; +} + +/*! \reimp + */ + +QSize QDockWindow::sizeHint() const +{ + QSize sh( QFrame::sizeHint() ); + if ( place() == InDock ) + sh = sh.expandedTo( fixedExtent() ); + sh = sh.expandedTo( QSize( 16, 16 ) ); + if ( area() ) { + if ( area()->orientation() == Horizontal && !vHandleRight->isVisible() ) + sh.setWidth( sh.width() + 2 * style().pixelMetric(QStyle::PM_SplitterWidth, this) / 3 ); + else if ( area()->orientation() == Vertical && !hHandleBottom->isVisible() ) + sh.setHeight( sh.height() + 2 * style().pixelMetric(QStyle::PM_SplitterWidth, this) / 3 ); + } + return sh; +} + +/*! \reimp + */ + +QSize QDockWindow::minimumSize() const +{ + QSize ms( QFrame::minimumSize() ); + if ( place() == InDock ) + ms = ms.expandedTo( fixedExtent() ); + ms = ms.expandedTo( QSize( 16, 16 ) ); + if ( area() ) { + if ( area()->orientation() == Horizontal && !vHandleRight->isVisible() ) + ms.setWidth( ms.width() + 2 * style().pixelMetric(QStyle::PM_SplitterWidth, this) / 3 ); + else if ( area()->orientation() == Vertical && !hHandleBottom->isVisible() ) + ms.setHeight( ms.height() + 2 * style().pixelMetric(QStyle::PM_SplitterWidth, this) / 3 ); + } + return ms; +} + +/*! \reimp + */ + +QSize QDockWindow::minimumSizeHint() const +{ + QSize msh( QFrame::minimumSize() ); + if ( place() == InDock ) + msh = msh.expandedTo( fixedExtent() ); + msh = msh.expandedTo( QSize( 16, 16 ) ); + if ( area() ) { + if ( area()->orientation() == Horizontal && !vHandleRight->isVisible() ) + msh.setWidth( msh.width() + 2 * style().pixelMetric(QStyle::PM_SplitterWidth, this) / 3 ); + else if ( area()->orientation() == Vertical && !hHandleBottom->isVisible() ) + msh.setHeight( msh.height() + 2 * style().pixelMetric(QStyle::PM_SplitterWidth, this) / 3 ); + } + return msh; +} + +/*! \internal */ +void QDockWindow::undock( QWidget *w ) +{ + QMainWindow *mw = 0; + if ( area() ) + mw = ::qt_cast<QMainWindow*>(area()->parentWidget()); + if ( mw && !mw->isDockEnabled( this, DockTornOff ) ) + return; + if ( (place() == OutsideDock && !w) ) + return; + + QPoint p( 50, 50 ); + if ( topLevelWidget() ) + p = topLevelWidget()->pos() + QPoint( 20, 20 ); + if ( dockArea ) { + delete (QDockArea::DockWindowData*)dockWindowData; + dockWindowData = dockArea->dockWindowData( this ); + dockArea->removeDockWindow( this, TRUE, orientation() != Horizontal && ::qt_cast<QToolBar*>(this) ); + } + dockArea = 0; + if ( lastPos != QPoint( -1, -1 ) && lastPos.x() > 0 && lastPos.y() > 0 ) + move( lastPos ); + else + move( p ); + if ( lastSize != QSize( -1, -1 ) ) + resize( lastSize ); + curPlace = OutsideDock; + updateGui(); + emit orientationChanged( orientation() ); + QApplication::sendPostedEvents( this, QEvent::LayoutHint ); + if ( ::qt_cast<QToolBar*>(this) ) + adjustSize(); + if ( !w ) { + if ( !parentWidget() || parentWidget()->isVisible() ) { + if (lastSize == QSize(-1, -1)) + clearWState(WState_Resized); // Ensures size is recalculated (opaque). + show(); + } + } else { + reparent( w, 0, QPoint( 0, 0 ), FALSE ); + move( -width() - 5, -height() - 5 ); + resize( 1, 1 ); + show(); + } + if ( parentWidget() && isTopLevel() ) + parentWidget()->setActiveWindow(); + emit placeChanged( place() ); +} + +/*! + \fn void QDockWindow::undock() + + Undocks the QDockWindow from its current dock area if it is + docked; otherwise does nothing. + + \sa dock() QDockArea::moveDockWindow(), + QDockArea::removeDockWindow(), QMainWindow::moveDockWindow(), + QMainWindow::removeDockWindow() +*/ + +void QDockWindow::removeFromDock( bool fixNewLines ) +{ + if ( dockArea ) + dockArea->removeDockWindow( this, FALSE, FALSE, fixNewLines ); +} + +/*! + Docks the dock window into the last dock area in which it was + docked. + + If the dock window has no last dock area (e.g. it was created as a + floating window and has never been docked), or if the last dock + area it was docked in does not exist (e.g. the dock area has been + deleted), nothing happens. + + The dock window will dock with the dock area regardless of the return value + of QDockArea::isDockWindowAccepted(). + + \sa undock() QDockArea::moveDockWindow(), + QDockArea::removeDockWindow(), QMainWindow::moveDockWindow(), + QMainWindow::removeDockWindow(), QDockArea::isDockWindowAccepted() + +*/ + +void QDockWindow::dock() +{ + if ( !(QDockArea::DockWindowData*)dockWindowData || + !( (QDockArea::DockWindowData*)dockWindowData )->area ) + return; + curPlace = InDock; + lastPos = pos(); + lastSize = size(); + ( (QDockArea::DockWindowData*)dockWindowData )-> + area->dockWindow( this, (QDockArea::DockWindowData*)dockWindowData ); + emit orientationChanged( orientation() ); + emit placeChanged( place() ); +} + +/*! \reimp + */ + +void QDockWindow::hideEvent( QHideEvent *e ) +{ + QFrame::hideEvent( e ); +} + +/*! \reimp + */ + +void QDockWindow::showEvent( QShowEvent *e ) +{ + if (curPlace == OutsideDock && (parent() && strcmp(parent()->name(), "qt_hide_dock") != 0)) { + QRect sr = qApp->desktop()->availableGeometry( this ); + if ( !sr.contains( pos() ) ) { + int nx = QMIN( QMAX( x(), sr.x() ), sr.right()-width() ); + int ny = QMIN( QMAX( y(), sr.y() ), sr.bottom()-height() ); + move( nx, ny ); + } + } + + QFrame::showEvent( e ); +} + +/*! + \property QDockWindow::opaqueMoving + \brief whether the dock window will be shown normally whilst it is + being moved. + + If this property is FALSE, (the default), the dock window will be + represented by an outline rectangle whilst it is being moved. + + \warning Currently opaque moving has some problems and we do not + recommend using it at this time. We expect to fix these problems + in a future release. +*/ + +void QDockWindow::setOpaqueMoving( bool b ) +{ + opaque = b; + horHandle->setOpaqueMoving( b ); + verHandle->setOpaqueMoving( b ); + titleBar->setOpaqueMoving( b ); +} + +bool QDockWindow::opaqueMoving() const +{ + return opaque; +} + +/*! \reimp */ + +void QDockWindow::setCaption( const QString &s ) +{ + titleBar->setCaption( s ); + verHandle->update(); + horHandle->update(); +#ifndef QT_NO_WIDGET_TOPEXTRA + QFrame::setCaption( s ); +#endif +#ifndef QT_NO_TOOLTIP + QToolTip::remove( horHandle ); + QToolTip::remove( verHandle ); + if ( !s.isEmpty() ) { + QToolTip::add( horHandle, s ); + QToolTip::add( verHandle, s ); + } +#endif +} + +void QDockWindow::updateSplitterVisibility( bool visible ) +{ + if ( area() && isResizeEnabled() ) { + if ( orientation() == Horizontal ) { + if ( visible ) + vHandleRight->show(); + else + vHandleRight->hide(); + vHandleLeft->hide(); + } else { + if ( visible ) + hHandleBottom->show(); + else + hHandleBottom->hide(); + hHandleTop->hide(); + } + } +} + +/*! \reimp */ +bool QDockWindow::eventFilter( QObject * o, QEvent *e ) +{ + if ( !o->isWidgetType() ) + return FALSE; + + if ( e->type() == QEvent::KeyPress && + ( horHandle->mousePressed || + verHandle->mousePressed || + titleBar->mousePressed ) ) { + QKeyEvent *ke = (QKeyEvent*)e; + if ( ke->key() == Key_Escape ) { + horHandle->mousePressed = + verHandle->mousePressed = + titleBar->mousePressed = FALSE; + endRectDraw( !opaque ); + qApp->removeEventFilter( this ); + return TRUE; + } + } else if ( ((QWidget*)o)->topLevelWidget() != this && place() == OutsideDock && isTopLevel() ) { + if ( (e->type() == QEvent::WindowDeactivate || + e->type() == QEvent::WindowActivate ) ) + event( e ); + } + return FALSE; +} + +/*! \reimp */ +bool QDockWindow::event( QEvent *e ) +{ + switch ( e->type() ) { + case QEvent::WindowDeactivate: + if ( place() == OutsideDock && isTopLevel() && parentWidget() + && parentWidget()->isActiveWindow() ) + return TRUE; + case QEvent::Hide: + if ( !isHidden() ) + break; + // fall through + case QEvent::HideToParent: + emit visibilityChanged( FALSE ); + break; + case QEvent::Show: + if ( e->spontaneous() ) + break; + case QEvent::ShowToParent: + emit visibilityChanged( TRUE ); + break; + default: + break; + } + return QFrame::event( e ); +} + +#ifdef QT_NO_WIDGET_TOPEXTRA +QString QDockWindow::caption() const +{ + return titleBar->caption(); +} +#endif + +/*! \reimp */ +void QDockWindow::contextMenuEvent( QContextMenuEvent *e ) +{ + QObject *o = this; + while ( o ) { + if ( ::qt_cast<QMainWindow*>(o) ) + break; + o = o->parent(); + } + if ( !o || ! ( (QMainWindow*)o )->showDockMenu( e->globalPos() ) ) + e->ignore(); +} + +#include "qdockwindow.moc" + +#endif //QT_NO_MAINWINDOW diff --git a/src/widgets/qdockwindow.h b/src/widgets/qdockwindow.h new file mode 100644 index 0000000..0e37f0b --- /dev/null +++ b/src/widgets/qdockwindow.h @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Definition of the QDockWindow class +** +** Created : 001010 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the workspace 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. +** +**********************************************************************/ + +#ifndef QDOCKWINDOW_H +#define QDOCKWINDOW_H + +#ifndef QT_H +#include "qframe.h" +#endif // QT_H + +#ifndef QT_NO_MAINWINDOW + +class QDockWindowHandle; +class QDockWindowTitleBar; +class QPainter; +class QDockWindowResizeHandle; +class QBoxLayout; +class QHBoxLayout; +class QVBoxLayout; +class QDockArea; +class QWidgetResizeHandler; +class QMainWindow; +class QDockAreaLayout; +class QDockWindowPrivate; +class QToolBar; +class QWindowsXPStyle; + +class Q_EXPORT QDockWindow : public QFrame +{ + Q_OBJECT + Q_ENUMS( CloseMode Place ) + Q_PROPERTY( int closeMode READ closeMode WRITE setCloseMode ) //### this shouldn't be of type int?! + Q_PROPERTY( bool resizeEnabled READ isResizeEnabled WRITE setResizeEnabled ) + Q_PROPERTY( bool movingEnabled READ isMovingEnabled WRITE setMovingEnabled ) + Q_PROPERTY( bool horizontallyStretchable READ isHorizontallyStretchable WRITE setHorizontallyStretchable ) + Q_PROPERTY( bool verticallyStretchable READ isVerticallyStretchable WRITE setVerticallyStretchable ) + Q_PROPERTY( bool stretchable READ isStretchable ) + Q_PROPERTY( bool newLine READ newLine WRITE setNewLine ) + Q_PROPERTY( bool opaqueMoving READ opaqueMoving WRITE setOpaqueMoving ) + Q_PROPERTY( int offset READ offset WRITE setOffset ) + Q_PROPERTY( Place place READ place ) + + friend class QDockWindowHandle; + friend class QDockWindowTitleBar; + friend class QDockArea; + friend class QDockAreaLayout; + friend class QMainWindow; + friend class QCEMainWindow; + friend class QToolBar; + friend class QWindowsXPStyle; + +public: + enum Place { InDock, OutsideDock }; + enum CloseMode { Never = 0, Docked = 1, Undocked = 2, Always = Docked | Undocked }; + + QDockWindow( Place p = InDock, QWidget* parent=0, const char* name=0, WFlags f = 0 ); + QDockWindow( QWidget* parent, const char* name=0, WFlags f = 0 ); + ~QDockWindow(); + + virtual void setWidget( QWidget *w ); + QWidget *widget() const; + + Place place() const { return curPlace; } + + QDockArea *area() const; + + virtual void setCloseMode( int m ); + bool isCloseEnabled() const; + int closeMode() const; + + virtual void setResizeEnabled( bool b ); + virtual void setMovingEnabled( bool b ); + bool isResizeEnabled() const; + bool isMovingEnabled() const; + + virtual void setHorizontallyStretchable( bool b ); + virtual void setVerticallyStretchable( bool b ); + bool isHorizontallyStretchable() const; + bool isVerticallyStretchable() const; + void setHorizontalStretchable( bool b ) { setHorizontallyStretchable( b ); } + void setVerticalStretchable( bool b ) { setVerticallyStretchable( b ); } + bool isHorizontalStretchable() const { return isHorizontallyStretchable(); } + bool isVerticalStretchable() const { return isVerticallyStretchable(); } + bool isStretchable() const; + + virtual void setOffset( int o ); + int offset() const; + + virtual void setFixedExtentWidth( int w ); + virtual void setFixedExtentHeight( int h ); + QSize fixedExtent() const; + + virtual void setNewLine( bool b ); + bool newLine() const; + + Qt::Orientation orientation() const; + + QSize sizeHint() const; + QSize minimumSize() const; + QSize minimumSizeHint() const; + + QBoxLayout *boxLayout(); + + virtual void setOpaqueMoving( bool b ); + bool opaqueMoving() const; + + bool eventFilter( QObject *o, QEvent *e ); + +#ifdef QT_NO_WIDGET_TOPEXTRA + QString caption() const; +#endif + +signals: + void orientationChanged( Orientation o ); + void placeChanged( QDockWindow::Place p ); + void visibilityChanged( bool ); + +public slots: + virtual void undock( QWidget *w ); + virtual void undock() { undock( 0 ); } + virtual void dock(); + virtual void setOrientation( Orientation o ); + void setCaption( const QString &s ); + +protected: + void resizeEvent( QResizeEvent *e ); + void showEvent( QShowEvent *e ); + void hideEvent( QHideEvent *e ); + void contextMenuEvent( QContextMenuEvent *e ); + + void drawFrame( QPainter * ); + void drawContents( QPainter * ); + + bool event( QEvent *e ); + +private slots: + void toggleVisible() { if ( !isVisible() ) show(); else hide(); } + +private: + QDockWindow( Place p, QWidget* parent, const char* name, WFlags f, bool toolbar ); + + void handleMove( const QPoint &pos, const QPoint &gp, bool drawRect ); + void updateGui(); + void updateSplitterVisibility( bool visible ); + + void startRectDraw( const QPoint &so, bool drawRect ); + void endRectDraw( bool drawRect ); + void updatePosition( const QPoint &globalPos ); + QWidget *areaAt( const QPoint &gp ); + void removeFromDock( bool fixNewLines = TRUE ); + void swapRect( QRect &r, Qt::Orientation o, const QPoint &offset, QDockArea *area ); + void init(); + +private: + QDockWindowHandle *horHandle, *verHandle; + QDockWindowTitleBar *titleBar; + QWidget *wid; + QPainter *unclippedPainter; + QDockArea *dockArea, *tmpDockArea; + QRect currRect; + Place curPlace; + Place state; + bool resizeEnabled : 1; + bool moveEnabled : 1; + bool nl : 1; + bool opaque : 1; + bool isToolbar : 1; + bool stretchable[ 3 ]; + Orientation startOrientation; + int cMode; + QPoint startOffset; + int offs; + QSize fExtent; + QDockWindowResizeHandle *hHandleTop, *hHandleBottom, *vHandleLeft, *vHandleRight; + QVBoxLayout *hbox; + QHBoxLayout *vbox; + QBoxLayout *childBox; + void *dockWindowData; + QPoint lastPos; + QSize lastSize; + QWidgetResizeHandler *widgetResizeHandler; + QDockWindowPrivate *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QDockWindow( const QDockWindow & ); + QDockWindow& operator=( const QDockWindow & ); +#endif +}; + +inline QDockArea *QDockWindow::area() const +{ + return dockArea; +} + +#define Q_DEFINED_QDOCKWINDOW +#include "qwinexport.h" +#endif + +#endif // QDOCKWINDOW_H diff --git a/src/widgets/qeffects.cpp b/src/widgets/qeffects.cpp new file mode 100644 index 0000000..50e104f --- /dev/null +++ b/src/widgets/qeffects.cpp @@ -0,0 +1,675 @@ +/**************************************************************************** +** +** Implementation of QEffects functions +** +** Created : 000621 +** +** Copyright (C) 2005-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 "qapplication.h" +#ifndef QT_NO_EFFECTS +#include "qwidget.h" +#include "qeffects_p.h" +#include "qpixmap.h" +#include "qimage.h" +#include "qtimer.h" +#include "qdatetime.h" +#include "qguardedptr.h" +#include "qscrollview.h" + +/* + Internal class to get access to protected QWidget-members +*/ + +class QAccessWidget : public QWidget +{ + friend class QAlphaWidget; + friend class QRollEffect; +public: + QAccessWidget( QWidget* parent=0, const char* name=0, WFlags f = 0 ) + : QWidget( parent, name, f ) {} +}; + +/* + Internal class QAlphaWidget. + + The QAlphaWidget is shown while the animation lasts + and displays the pixmap resulting from the alpha blending. +*/ + +class QAlphaWidget: public QWidget, private QEffects +{ + Q_OBJECT +public: + QAlphaWidget( QWidget* w, WFlags f = 0 ); + + void run( int time ); + +protected: + void paintEvent( QPaintEvent* e ); + void closeEvent( QCloseEvent* ); + bool eventFilter( QObject* o, QEvent* e ); + void alphaBlend(); + +protected slots: + void render(); + +private: + QPixmap pm; + double alpha; + QImage back; + QImage front; + QImage mixed; + QGuardedPtr<QAccessWidget> widget; + int duration; + int elapsed; + bool showWidget; + QTimer anim; + QTime checkTime; +}; + +static QAlphaWidget* q_blend = 0; + +/* + Constructs a QAlphaWidget. +*/ +QAlphaWidget::QAlphaWidget( QWidget* w, WFlags f ) + : QWidget( QApplication::desktop()->screen(QApplication::desktop()->screenNumber(w)), + "qt internal alpha effect widget", f ) +{ +#if 1 //ndef Q_WS_WIN + setEnabled( FALSE ); +#endif + + pm.setOptimization( QPixmap::BestOptim ); + setBackgroundMode( NoBackground ); + widget = (QAccessWidget*)w; + alpha = 0; +} + +/* + \reimp +*/ +void QAlphaWidget::paintEvent( QPaintEvent* ) +{ + bitBlt( this, QPoint(0,0), &pm ); +} + +/* + Starts the alphablending animation. + The animation will take about \a time ms +*/ +void QAlphaWidget::run( int time ) +{ + duration = time; + + if ( duration < 0 ) + duration = 150; + + if ( !widget ) + return; + + elapsed = 0; + checkTime.start(); + + showWidget = TRUE; + qApp->installEventFilter( this ); + + widget->setWState( WState_Visible ); + + move( widget->geometry().x(),widget->geometry().y() ); + resize( widget->size().width(), widget->size().height() ); + + front = QImage( widget->size(), 32 ); + front = QPixmap::grabWidget( widget ); + + back = QImage( widget->size(), 32 ); + back = QPixmap::grabWindow( QApplication::desktop()->winId(), + widget->geometry().x(), widget->geometry().y(), + widget->geometry().width(), widget->geometry().height() ); + + if ( !back.isNull() && checkTime.elapsed() < duration / 2 ) { + mixed = back.copy(); + pm = mixed; + show(); + setEnabled(FALSE); + + connect( &anim, SIGNAL(timeout()), this, SLOT(render())); + anim.start( 1 ); + } else { + duration = 0; + render(); + } +} + +/* + \reimp +*/ +bool QAlphaWidget::eventFilter( QObject* o, QEvent* e ) +{ + switch ( e->type() ) { + case QEvent::Move: + if ( o != widget ) + break; + move( widget->geometry().x(),widget->geometry().y() ); + update(); + break; + case QEvent::Hide: + case QEvent::Close: + if ( o != widget ) + break; + case QEvent::MouseButtonPress: +#ifndef QT_NO_SCROLLVIEW + if ( ::qt_cast<QScrollView*>(o) ) + break; +#endif + case QEvent::MouseButtonDblClick: + setEnabled(TRUE); + showWidget = FALSE; + render(); + break; + case QEvent::KeyPress: + { + QKeyEvent *ke = (QKeyEvent*)e; + if ( ke->key() == Key_Escape ) + showWidget = FALSE; + else + duration = 0; + render(); + break; + } + default: + break; + } + return QWidget::eventFilter( o, e ); +} + +/* + \reimp +*/ +void QAlphaWidget::closeEvent( QCloseEvent *e ) +{ + e->accept(); + if ( !q_blend ) + return; + + showWidget = FALSE; + render(); + + QWidget::closeEvent( e ); +} + +/* + Render alphablending for the time elapsed. + + Show the blended widget and free all allocated source + if the blending is finished. +*/ +void QAlphaWidget::render() +{ + int tempel = checkTime.elapsed(); + if ( elapsed >= tempel ) + elapsed++; + else + elapsed = tempel; + + if ( duration != 0 ) + alpha = tempel / double(duration); + else + alpha = 1; + if ( alpha >= 1 || !showWidget) { + anim.stop(); + qApp->removeEventFilter( this ); + + if ( widget ) { + if ( !showWidget ) { +#ifdef Q_WS_WIN + setEnabled(TRUE); + setFocus(); +#endif + widget->hide(); + widget->setWState( WState_ForceHide ); + widget->clearWState( WState_Visible ); + } else if ( duration ) { + BackgroundMode bgm = widget->backgroundMode(); + QColor erc = widget->eraseColor(); + const QPixmap *erp = widget->erasePixmap(); + + widget->clearWState( WState_Visible ); + widget->setBackgroundMode( NoBackground ); + widget->show(); + if ( bgm != FixedColor && bgm != FixedPixmap ) { + widget->clearWState( WState_Visible ); // prevent update in setBackgroundMode + widget->setBackgroundMode( bgm ); + widget->setWState( WState_Visible ); + } + if ( erc.isValid() ) { + widget->setEraseColor( erc ); + } else if ( erp ) { + widget->setErasePixmap( *erp ); + } + } else { + widget->clearWState( WState_Visible ); + widget->show(); + } + } + q_blend = 0; + deleteLater(); + } else { + if (widget) + widget->clearWState( WState_ForceHide ); + alphaBlend(); + pm = mixed; + repaint( FALSE ); + } +} + +/* + Calculate an alphablended image. +*/ +void QAlphaWidget::alphaBlend() +{ + const double ia = 1-alpha; + const int sw = front.width(); + const int sh = front.height(); + switch( front.depth() ) { + case 32: + { + Q_UINT32** md = (Q_UINT32**)mixed.jumpTable(); + Q_UINT32** bd = (Q_UINT32**)back.jumpTable(); + Q_UINT32** fd = (Q_UINT32**)front.jumpTable(); + + for (int sy = 0; sy < sh; sy++ ) { + Q_UINT32* bl = ((Q_UINT32*)bd[sy]); + Q_UINT32* fl = ((Q_UINT32*)fd[sy]); + for (int sx = 0; sx < sw; sx++ ) { + Q_UINT32 bp = bl[sx]; + Q_UINT32 fp = fl[sx]; + + ((Q_UINT32*)(md[sy]))[sx] = qRgb(int (qRed(bp)*ia + qRed(fp)*alpha), + int (qGreen(bp)*ia + qGreen(fp)*alpha), + int (qBlue(bp)*ia + qBlue(fp)*alpha) ); + } + } + } + default: + break; + } +} + +/* + Internal class QRollEffect + + The QRollEffect widget is shown while the animation lasts + and displays a scrolling pixmap. +*/ + +class QRollEffect : public QWidget, private QEffects +{ + Q_OBJECT +public: + QRollEffect( QWidget* w, WFlags f, DirFlags orient ); + + void run( int time ); + +protected: + void paintEvent( QPaintEvent* ); + bool eventFilter( QObject*, QEvent* ); + void closeEvent( QCloseEvent* ); + +private slots: + void scroll(); + +private: + QGuardedPtr<QAccessWidget> widget; + + int currentHeight; + int currentWidth; + int totalHeight; + int totalWidth; + + int duration; + int elapsed; + bool done; + bool showWidget; + int orientation; + + QTimer anim; + QTime checkTime; + + QPixmap pm; +}; + +static QRollEffect* q_roll = 0; + +/* + Construct a QRollEffect widget. +*/ +QRollEffect::QRollEffect( QWidget* w, WFlags f, DirFlags orient ) + : QWidget( QApplication::desktop()->screen(QApplication::desktop()->screenNumber(w)), + "qt internal roll effect widget", f ), orientation(orient) +{ +#if 1 //ndef Q_WS_WIN + setEnabled( FALSE ); +#endif + widget = (QAccessWidget*) w; + Q_ASSERT( widget ); + + setBackgroundMode( NoBackground ); + + if ( widget->testWState( WState_Resized ) ) { + totalWidth = widget->width(); + totalHeight = widget->height(); + } else { + totalWidth = widget->sizeHint().width(); + totalHeight = widget->sizeHint().height(); + } + + currentHeight = totalHeight; + currentWidth = totalWidth; + + if ( orientation & (RightScroll|LeftScroll) ) + currentWidth = 0; + if ( orientation & (DownScroll|UpScroll) ) + currentHeight = 0; + + pm.setOptimization( QPixmap::BestOptim ); + pm = QPixmap::grabWidget( widget ); +} + +/* + \reimp +*/ +void QRollEffect::paintEvent( QPaintEvent* ) +{ + int x = orientation & RightScroll ? QMIN(0, currentWidth - totalWidth) : 0; + int y = orientation & DownScroll ? QMIN(0, currentHeight - totalHeight) : 0; + + bitBlt( this, x, y, &pm, + 0, 0, pm.width(), pm.height(), CopyROP, TRUE ); +} + +/* + \reimp +*/ +bool QRollEffect::eventFilter( QObject* o, QEvent* e ) +{ + switch ( e->type() ) { + case QEvent::Move: + if ( o != widget ) + break; + move( widget->geometry().x(),widget->geometry().y() ); + update(); + break; + case QEvent::Hide: + case QEvent::Close: + if ( o != widget || done ) + break; + setEnabled(TRUE); + showWidget = FALSE; + done = TRUE; + scroll(); + break; + case QEvent::MouseButtonPress: +#ifndef QT_NO_SCROLLVIEW + if ( ::qt_cast<QScrollView*>(o) ) + break; +#endif + case QEvent::MouseButtonDblClick: + if ( done ) + break; + setEnabled(TRUE); + showWidget = FALSE; + done = TRUE; + scroll(); + break; + case QEvent::KeyPress: + { + QKeyEvent *ke = (QKeyEvent*)e; + if ( ke->key() == Key_Escape ) + showWidget = FALSE; + done = TRUE; + scroll(); + break; + } + default: + break; + } + return QWidget::eventFilter( o, e ); +} + +/* + \reimp +*/ +void QRollEffect::closeEvent( QCloseEvent *e ) +{ + e->accept(); + if ( done ) + return; + + showWidget = FALSE; + done = TRUE; + scroll(); + + QWidget::closeEvent( e ); +} + +/* + Start the animation. + + The animation will take about \a time ms, or is + calculated if \a time is negative +*/ +void QRollEffect::run( int time ) +{ + if ( !widget ) + return; + + duration = time; + elapsed = 0; + + if ( duration < 0 ) { + int dist = 0; + if ( orientation & (RightScroll|LeftScroll) ) + dist += totalWidth - currentWidth; + if ( orientation & (DownScroll|UpScroll) ) + dist += totalHeight - currentHeight; + duration = QMIN( QMAX( dist/3, 50 ), 120 ); + } + + connect( &anim, SIGNAL(timeout()), this, SLOT(scroll())); + + widget->setWState( WState_Visible ); + + move( widget->geometry().x(),widget->geometry().y() ); + resize( QMIN( currentWidth, totalWidth ), QMIN( currentHeight, totalHeight ) ); + + show(); + setEnabled(FALSE); + + qApp->installEventFilter( this ); + + showWidget = TRUE; + done = FALSE; + anim.start( 1 ); + checkTime.start(); +} + +/* + Roll according to the time elapsed. +*/ +void QRollEffect::scroll() +{ + if ( !done && widget) { + widget->clearWState( WState_ForceHide ); + int tempel = checkTime.elapsed(); + if ( elapsed >= tempel ) + elapsed++; + else + elapsed = tempel; + + if ( currentWidth != totalWidth ) { + currentWidth = totalWidth * (elapsed/duration) + + ( 2 * totalWidth * (elapsed%duration) + duration ) + / ( 2 * duration ); + // equiv. to int( (totalWidth*elapsed) / duration + 0.5 ) + done = (currentWidth >= totalWidth); + } + if ( currentHeight != totalHeight ) { + currentHeight = totalHeight * (elapsed/duration) + + ( 2 * totalHeight * (elapsed%duration) + duration ) + / ( 2 * duration ); + // equiv. to int( (totalHeight*elapsed) / duration + 0.5 ) + done = (currentHeight >= totalHeight); + } + done = ( currentHeight >= totalHeight ) && + ( currentWidth >= totalWidth ); + + int w = totalWidth; + int h = totalHeight; + int x = widget->geometry().x(); + int y = widget->geometry().y(); + + if ( orientation & RightScroll || orientation & LeftScroll ) + w = QMIN( currentWidth, totalWidth ); + if ( orientation & DownScroll || orientation & UpScroll ) + h = QMIN( currentHeight, totalHeight ); + + setUpdatesEnabled( FALSE ); + if ( orientation & UpScroll ) + y = widget->geometry().y() + QMAX( 0, totalHeight - currentHeight ); + if ( orientation & LeftScroll ) + x = widget->geometry().x() + QMAX( 0, totalWidth - currentWidth ); + if ( orientation & UpScroll || orientation & LeftScroll ) + move( x, y ); + + resize( w, h ); + setUpdatesEnabled( TRUE ); + repaint( FALSE ); + } + if ( done ) { + anim.stop(); + qApp->removeEventFilter( this ); + if ( widget ) { + if ( !showWidget ) { +#ifdef Q_WS_WIN + setEnabled(TRUE); + setFocus(); +#endif + widget->hide(); + widget->setWState( WState_ForceHide ); + widget->clearWState( WState_Visible ); + } else { + BackgroundMode bgm = widget->backgroundMode(); + QColor erc = widget->eraseColor(); + const QPixmap *erp = widget->erasePixmap(); + + widget->clearWState( WState_Visible ); + widget->setBackgroundMode( NoBackground ); + widget->show(); + if ( bgm != FixedColor && bgm != FixedPixmap ) { + widget->clearWState( WState_Visible ); // prevent update in setBackgroundMode + widget->setBackgroundMode( bgm ); + widget->setWState( WState_Visible ); + } + if ( erc.isValid() ) { + widget->setEraseColor( erc ); + } else if ( erp ) { + widget->setErasePixmap( *erp ); + } + } + } + q_roll = 0; + deleteLater(); + } +} + +/* + Delete this after timeout +*/ + +#include "qeffects.moc" + +/*! + Scroll widget \a w in \a time ms. \a orient may be 1 (vertical), 2 + (horizontal) or 3 (diagonal). +*/ +void qScrollEffect( QWidget* w, QEffects::DirFlags orient, int time ) +{ + if ( q_roll ) { + delete q_roll; + q_roll = 0; + } + + qApp->sendPostedEvents( w, QEvent::Move ); + qApp->sendPostedEvents( w, QEvent::Resize ); +#ifdef Q_WS_X11 + uint flags = Qt::WStyle_Customize | Qt::WNoAutoErase | Qt::WStyle_StaysOnTop + | (w->isPopup() ? Qt::WType_Popup : (Qt::WX11BypassWM | Qt::WStyle_Tool)); +#else + uint flags = Qt::WStyle_Customize | Qt::WType_Popup | Qt::WX11BypassWM | Qt::WNoAutoErase | Qt::WStyle_StaysOnTop; +#endif + + // those can popups - they would steal the focus, but are disabled + q_roll = new QRollEffect( w, flags, orient ); + q_roll->run( time ); +} + +/*! + Fade in widget \a w in \a time ms. +*/ +void qFadeEffect( QWidget* w, int time ) +{ + if ( q_blend ) { + delete q_blend; + q_blend = 0; + } + + qApp->sendPostedEvents( w, QEvent::Move ); + qApp->sendPostedEvents( w, QEvent::Resize ); + +#ifdef Q_WS_X11 + uint flags = Qt::WStyle_Customize | Qt::WNoAutoErase | Qt::WStyle_StaysOnTop + | (w->isPopup() ? Qt::WType_Popup : (Qt::WX11BypassWM | Qt::WStyle_Tool)); +#else + uint flags = Qt::WStyle_Customize | Qt::WType_Popup | Qt::WX11BypassWM | Qt::WNoAutoErase | Qt::WStyle_StaysOnTop; +#endif + + // those can popups - they would steal the focus, but are disabled + q_blend = new QAlphaWidget( w, flags ); + + q_blend->run( time ); +} +#endif //QT_NO_EFFECTS diff --git a/src/widgets/qeffects_p.h b/src/widgets/qeffects_p.h new file mode 100644 index 0000000..b9a6228 --- /dev/null +++ b/src/widgets/qeffects_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Definition of QEffects functions +** +** Created : 000621 +** +** Copyright (C) 2005-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. +** +**********************************************************************/ + +#ifndef QEFFECTS_P_H +#define QEFFECTS_P_H + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qeffects.cpp, qcombobox.cpp, qpopupmenu.cpp and qtooltip.cpp. +// This header file may change from version to version without notice, +// or even be removed. +// +// We mean it. +// +// + +#ifndef QT_H +#include "qnamespace.h" +#endif // QT_H + +#ifndef QT_NO_EFFECTS +class QWidget; + +struct QEffects +{ + enum Direction { + LeftScroll = 0x0001, + RightScroll = 0x0002, + UpScroll = 0x0004, + DownScroll = 0x0008 + }; + + typedef uint DirFlags; +}; + +extern void Q_EXPORT qScrollEffect( QWidget*, QEffects::DirFlags dir = QEffects::DownScroll, int time = -1 ); +extern void Q_EXPORT qFadeEffect( QWidget*, int time = -1 ); +#endif // QT_NO_EFFECTS + +#endif // QEFFECTS_P_H diff --git a/src/widgets/qframe.cpp b/src/widgets/qframe.cpp new file mode 100644 index 0000000..414274e --- /dev/null +++ b/src/widgets/qframe.cpp @@ -0,0 +1,754 @@ +/**************************************************************************** +** +** Implementation of QFrame widget class +** +** Created : 950201 +** +** 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 "qframe.h" +#ifndef QT_NO_FRAME +#include "qpainter.h" +#include "qdrawutil.h" +#include "qframe.h" +#include "qbitmap.h" +#include "qstyle.h" + +/*! + \class QFrame + \brief The QFrame class is the base class of widgets that can have a frame. + + \ingroup abstractwidgets + + It draws a frame and calls a virtual function, drawContents(), to + fill in the frame. This function is reimplemented by subclasses. + There are also two other less useful functions: drawFrame() and + frameChanged(). + + QPopupMenu uses this to "raise" the menu above the surrounding + screen. QProgressBar has a "sunken" look. QLabel has a flat look. + The frames of widgets like these can be changed. + + \code + QLabel label(...); + label.setFrameStyle( QFrame::Panel | QFrame::Raised ); + label.setLineWidth( 2 ); + + QProgressBar pbar(...); + label.setFrameStyle( QFrame::NoFrame ); + \endcode + + The QFrame class can also be used directly for creating simple + frames without any contents, although usually you would use a + QHBox or QVBox because they automatically lay out the widgets you + put inside the frame. + + A frame widget has four attributes: frameStyle(), lineWidth(), + midLineWidth(), and margin(). + + The frame style is specified by a \link QFrame::Shape frame + shape\endlink and a \link QFrame::Shadow shadow style\endlink. The + frame shapes are \c NoFrame, \c Box, \c Panel, \c StyledPanel, \c + PopupPanel, \c WinPanel, \c ToolBarPanel, \c MenuBarPanel, \c + HLine and \c VLine; the shadow styles are \c Plain, \c Raised and + \c Sunken. + + The line width is the width of the frame border. + + The mid-line width specifies the width of an extra line in the + middle of the frame, which uses a third color to obtain a special + 3D effect. Notice that a mid-line is only drawn for \c Box, \c + HLine and \c VLine frames that are raised or sunken. + + The margin is the gap between the frame and the contents of the + frame. + + \target picture + This table shows the most useful combinations of styles and widths + (and some rather useless ones): + + \img frames.png Table of frame styles +*/ + + +/*! + \enum QFrame::Shape + + This enum type defines the shapes of a QFrame's frame. + + \value NoFrame QFrame draws nothing + \value Box QFrame draws a box around its contents + \value Panel QFrame draws a panel to make the contents appear + raised or sunken + \value StyledPanel draws a rectangular panel with a look that + depends on the current GUI style. It can be raised or sunken. + \value HLine QFrame draws a horizontal line that frames nothing + (useful as separator) + \value VLine QFrame draws a vertical line that frames nothing + (useful as separator) + \value GroupBoxPanel draws a rectangular panel + \value WinPanel draws a rectangular panel that can be raised or + sunken like those in Windows 95. Specifying this shape sets + the line width to 2 pixels. WinPanel is provided for compatibility. + For GUI style independence we recommend using StyledPanel instead. + \value ToolBarPanel + \value MenuBarPanel + \value PopupPanel + \value LineEditPanel is used to draw a frame suitable for line edits. The + look depends upon the current GUI style. + \value TabWidgetPanel is used to draw a frame suitable for tab widgets. The + look depends upon the current GUI style. + \value MShape internal mask + + When it does not call QStyle, Shape interacts with QFrame::Shadow, + the lineWidth() and the midLineWidth() to create the total result. + See the \link #picture picture of the frames\endlink in the class + description. + + \sa QFrame::Shadow QFrame::style() QStyle::drawPrimitive() +*/ + + +/*! + \enum QFrame::Shadow + + This enum type defines the 3D effect used for QFrame's frame. + + \value Plain the frame and contents appear level with the + surroundings; draws using the palette foreground color (without + any 3D effect) + \value Raised the frame and contents appear raised; draws a 3D + raised line using the light and dark colors of the current color + group + \value Sunken the frame and contents appear sunken; draws a 3D + sunken line using the light and dark colors of the current color + group + \value MShadow internal; mask for the shadow + + Shadow interacts with QFrame::Shape, the lineWidth() and the + midLineWidth(). See the \link #picture picture of the frames\endlink + in the class description. + + \sa QFrame::Shape lineWidth() midLineWidth() +*/ + + +/*! + Constructs a frame widget with frame style \c NoFrame and a + 1-pixel frame width. + + The \a parent, \a name and \a f arguments are passed to the + QWidget constructor. +*/ + +QFrame::QFrame( QWidget *parent, const char *name, WFlags f ) + : QWidget( parent, name, f ) +{ + frect = QRect( 0, 0, 0, 0 ); + fstyle = NoFrame | Plain; + lwidth = 1; + mwidth = 0; + mlwidth = 0; + updateFrameWidth(); +} + +static const int wpwidth = 2; // WinPanel lwidth + +/*! + \fn int QFrame::frameStyle() const + + Returns the frame style. + + The default value is QFrame::NoFrame. + + \sa setFrameStyle(), frameShape(), frameShadow() +*/ + +/*! + \property QFrame::frameShape + \brief the frame shape value from the frame style + + \sa frameStyle(), frameShadow() +*/ + +/*! + \property QFrame::frameShadow + \brief the frame shadow value from the frame style + + \sa frameStyle(), frameShape() +*/ + +/*! + Sets the frame style to \a style. + + The \a style is the bitwise OR between a frame shape and a frame + shadow style. See the \link #picture illustration\endlink in the + class documentation. + + The frame shapes are given in \l{QFrame::Shape} and the shadow + styles in \l{QFrame::Shadow}. + + If a mid-line width greater than 0 is specified, an additional + line is drawn for \c Raised or \c Sunken \c Box, \c HLine, and \c + VLine frames. The mid-color of the current color group is used for + drawing middle lines. + + \sa \link #picture Illustration\endlink, frameStyle(), + colorGroup(), QColorGroup +*/ + +void QFrame::setFrameStyle( int style ) +{ + if ( !testWState( WState_OwnSizePolicy ) ) { + switch ( style & MShape ) { + case HLine: + setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ); + break; + case VLine: + setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Minimum ); + break; + default: + if ( (fstyle & MShape) == HLine || (fstyle & MShape) == VLine) + setSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ); + } + clearWState( WState_OwnSizePolicy ); + } + fstyle = (short)style; + updateFrameWidth( TRUE ); +} + +/*! + \property QFrame::lineWidth + \brief the line width + + Note that the \e total line width for \c HLine and \c VLine is + given by frameWidth(), not lineWidth(). + + The default value is 1. + + \sa midLineWidth(), frameWidth() +*/ + +void QFrame::setLineWidth( int w ) +{ + lwidth = (short)w; + updateFrameWidth(); +} + +/*! + \property QFrame::midLineWidth + \brief the width of the mid-line + + The default value is 0. + + \sa lineWidth(), frameWidth() +*/ + +void QFrame::setMidLineWidth( int w ) +{ + mlwidth = (short)w; + updateFrameWidth(); +} + + + +/*! + \property QFrame::margin + \brief the width of the margin + + The margin is the distance between the innermost pixel of the + frame and the outermost pixel of contentsRect(). It is included in + frameWidth(). + + The margin is filled according to backgroundMode(). + + The default value is 0. + + \sa setMargin(), lineWidth(), frameWidth() +*/ + +void QFrame::setMargin( int w ) +{ + mwidth = (short)w; + updateFrameWidth(); +} + + +/*! + \internal + Updated the fwidth parameter. +*/ + +void QFrame::updateFrameWidth( bool resetLineMetrics ) +{ + int frameType = fstyle & MShape; + int frameStyle = fstyle & MShadow; + + if ( resetLineMetrics ) { + switch ( frameType ) { + case MenuBarPanel: + mwidth = 0; + lwidth = style().pixelMetric( QStyle::PM_MenuBarFrameWidth, this ); + break; + case ToolBarPanel: + mwidth = 0; + lwidth = style().pixelMetric( QStyle::PM_DockWindowFrameWidth, this ); + break; + case LineEditPanel: + case TabWidgetPanel: + case PopupPanel: + mwidth = 0; + lwidth = style().pixelMetric( QStyle::PM_DefaultFrameWidth, this ); + break; + } + } + + fwidth = -1; + + switch ( frameType ) { + + case NoFrame: + fwidth = 0; + break; + + case Box: + switch ( frameStyle ) { + case Plain: + fwidth = lwidth; + break; + case Raised: + case Sunken: + fwidth = (short)(lwidth*2 + midLineWidth() ); + break; + } + break; + + + case LineEditPanel: + case TabWidgetPanel: + case PopupPanel: + case GroupBoxPanel: + case Panel: + case StyledPanel: + switch ( frameStyle ) { + case Plain: + case Raised: + case Sunken: + fwidth = lwidth; + break; + } + break; + + case WinPanel: + switch ( frameStyle ) { + case Plain: + case Raised: + case Sunken: + fwidth = wpwidth; //WinPanel does not use lwidth! + break; + } + break; + case MenuBarPanel: + fwidth = lwidth; + break; + case ToolBarPanel: + fwidth = lwidth; + break; + case HLine: + case VLine: + switch ( frameStyle ) { + case Plain: + fwidth = lwidth; + break; + case Raised: + case Sunken: + fwidth = (short)(lwidth*2 + midLineWidth()); + break; + } + break; + } + + if ( fwidth == -1 ) // invalid style + fwidth = 0; + + fwidth += margin(); + + frameChanged(); +} + + +/*! + \property QFrame::frameWidth + \brief the width of the frame that is drawn. + + Note that the frame width depends on the \link + QFrame::setFrameStyle() frame style \endlink, not only the line + width and the mid-line width. For example, the style \c NoFrame + always has a frame width of 0, whereas the style \c Panel has a + frame width equivalent to the line width. The frame width also + includes the margin. + + \sa lineWidth(), midLineWidth(), frameStyle(), margin() +*/ + +/*! + \property QFrame::frameRect + \brief the frame rectangle + + The frame rectangle is the rectangle the frame is drawn in. By + default, this is the entire widget. Setting this property does \e + not cause a widget update. + + If this property is set to a null rectangle (for example + \c{QRect(0, 0, 0, 0)}), then the frame rectangle is equivalent to + the \link QWidget::rect() widget rectangle\endlink. + + \sa contentsRect() +*/ + +QRect QFrame::frameRect() const +{ + if ( frect.isNull() ) + return rect(); + else + return frect; +} + +void QFrame::setFrameRect( const QRect &r ) +{ + frect = r.isValid() ? r : rect(); +} + + +/*! + \property QFrame::contentsRect + \brief the rectangle inside the frame + + \sa frameRect(), drawContents() +*/ + +QRect QFrame::contentsRect() const +{ + QRect r = frameRect(); + int w = frameWidth(); // total width + int frameType = fstyle & MShape; + if (frameType == PopupPanel) { + int vExtra = style().pixelMetric(QStyle::PM_PopupMenuFrameVerticalExtra, this); + int hExtra = style().pixelMetric(QStyle::PM_PopupMenuFrameHorizontalExtra, this); + r.setRect( r.x()+w+hExtra, r.y()+w+vExtra, r.width()-w*2-hExtra*2, r.height()-w*2-vExtra*2 ); + } else { + r.setRect( r.x()+w, r.y()+w, r.width()-w*2, r.height()-w*2 ); + } + return r; +} + +/*!\reimp +*/ +QSize QFrame::sizeHint() const +{ + // Returns a size hint for the frame - for HLine and VLine + // shapes, this is stretchable one way and 3 pixels wide the + // other. For other shapes, QWidget::sizeHint() is used. + switch (fstyle & MShape) { + case HLine: + return QSize(-1,3); + case VLine: + return QSize(3,-1); + default: + return QWidget::sizeHint(); + } +} + +/*! + Processes the paint event \a event. + + Paints the frame and the contents. + + Opens the painter on the frame and calls drawFrame(), then + drawContents(). +*/ + +void QFrame::paintEvent( QPaintEvent *event ) +{ + const int m = margin(); + if ( m && testWFlags( WNoAutoErase ) ) { + QRect r = contentsRect(); + r.addCoords( -m, -m, m, m ); + erase( event->region().intersect( QRegion( r ) - contentsRect() ) ); + } + + QPainter paint( this ); + + if ( !contentsRect().contains( event->rect() ) ) { + paint.save(); + paint.setClipRegion( event->region().intersect(frameRect()) ); + drawFrame( &paint ); + paint.restore(); + } + if ( event->rect().intersects( contentsRect() ) && + (fstyle & MShape) != HLine && (fstyle & MShape) != VLine ) { + paint.setClipRegion( event->region().intersect( contentsRect() ) ); + drawContents( &paint ); + } +} + + +/*! + Processes the resize event \a e. + + Adjusts the frame rectangle for the resized widget. The frame + rectangle is elastic, and the surrounding area is static. + + The resulting frame rectangle may be null or invalid. You can use + setMinimumSize() to avoid those possibilities. + + Nothing is done if the frame rectangle is a \link QRect::isNull() + null rectangle\endlink already. +*/ + +void QFrame::resizeEvent( QResizeEvent *e ) +{ + if ( !frect.isNull() ) { + QRect r( frect.x(), frect.y(), + width() - (e->oldSize().width() - frect.width()), + height() - (e->oldSize().height() - frect.height()) ); + setFrameRect( r ); + } + QWidget::resizeEvent( e ); +} + + +/*! + Draws the frame using the painter \a p and the current frame + attributes and color group. The rectangle inside the frame is not + affected. + + This function is virtual, but in general you do not need to + reimplement it. If you do, note that the QPainter is already open + and must remain open. + + \sa frameRect(), contentsRect(), drawContents(), frameStyle(), setPalette() +*/ + +void QFrame::drawFrame( QPainter *p ) +{ + QPoint p1, p2; + QRect r = frameRect(); + int type = fstyle & MShape; + int cstyle = fstyle & MShadow; +#ifdef QT_NO_DRAWUTIL + p->setPen( black ); // #### + p->drawRect( r ); //### a bit too simple +#else + const QColorGroup & g = colorGroup(); + +#ifndef QT_NO_STYLE + QStyleOption opt(lineWidth(),midLineWidth()); + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (cstyle == Sunken) + flags |= QStyle::Style_Sunken; + else if (cstyle == Raised) + flags |= QStyle::Style_Raised; + if (hasFocus()) + flags |= QStyle::Style_HasFocus; + if (hasMouse()) + flags |= QStyle::Style_MouseOver; +#endif // QT_NO_STYLE + + switch ( type ) { + + case Box: + if ( cstyle == Plain ) + qDrawPlainRect( p, r, g.foreground(), lwidth ); + else + qDrawShadeRect( p, r, g, cstyle == Sunken, lwidth, + midLineWidth() ); + break; + + case LineEditPanel: + style().drawPrimitive( QStyle::PE_PanelLineEdit, p, r, g, flags, opt ); + break; + + case GroupBoxPanel: + style().drawPrimitive( QStyle::PE_PanelGroupBox, p, r, g, flags, opt ); + break; + + case TabWidgetPanel: + style().drawPrimitive( QStyle::PE_PanelTabWidget, p, r, g, flags, opt ); + break; + + case MenuBarPanel: +#ifndef QT_NO_STYLE + style().drawPrimitive(QStyle::PE_PanelMenuBar, p, r, g, flags, opt); + break; +#endif // fall through to Panel if QT_NO_STYLE + + case ToolBarPanel: +#ifndef QT_NO_STYLE + style().drawPrimitive( QStyle::PE_PanelDockWindow, p, rect(), g, flags, opt); + break; +#endif // fall through to Panel if QT_NO_STYLE + + case StyledPanel: +#ifndef QT_NO_STYLE + if ( cstyle == Plain ) + qDrawPlainRect( p, r, g.foreground(), lwidth ); + else + style().drawPrimitive(QStyle::PE_Panel, p, r, g, flags, opt); + break; +#endif // fall through to Panel if QT_NO_STYLE + + case PopupPanel: +#ifndef QT_NO_STYLE + { + int vextra = style().pixelMetric(QStyle::PM_PopupMenuFrameVerticalExtra, this), + hextra = style().pixelMetric(QStyle::PM_PopupMenuFrameHorizontalExtra, this); + if(vextra > 0 || hextra > 0) { + QRect fr = frameRect(); + int fw = frameWidth(); + if(vextra > 0) { + style().drawControl(QStyle::CE_PopupMenuVerticalExtra, p, this, + QRect(fr.x() + fw, fr.y() + fw, fr.width() - (fw*2), vextra), + g, flags, opt); + style().drawControl(QStyle::CE_PopupMenuVerticalExtra, p, this, + QRect(fr.x() + fw, fr.bottom() - fw - vextra, fr.width() - (fw*2), vextra), + g, flags, opt); + } + if(hextra > 0) { + style().drawControl(QStyle::CE_PopupMenuHorizontalExtra, p, this, + QRect(fr.x() + fw, fr.y() + fw + vextra, hextra, fr.height() - (fw*2) - vextra), + g, flags, opt); + style().drawControl(QStyle::CE_PopupMenuHorizontalExtra, p, this, + QRect(fr.right() - fw - hextra, fr.y() + fw + vextra, hextra, fr.height() - (fw*2) - vextra), + g, flags, opt); + } + } + + if ( cstyle == Plain ) + qDrawPlainRect( p, r, g.foreground(), lwidth ); + else + style().drawPrimitive(QStyle::PE_PanelPopup, p, r, g, flags, opt); + break; + } +#endif // fall through to Panel if QT_NO_STYLE + + case Panel: + if ( cstyle == Plain ) + qDrawPlainRect( p, r, g.foreground(), lwidth ); + else + qDrawShadePanel( p, r, g, cstyle == Sunken, lwidth ); + break; + + case WinPanel: + if ( cstyle == Plain ) + qDrawPlainRect( p, r, g.foreground(), wpwidth ); + else + qDrawWinPanel( p, r, g, cstyle == Sunken ); + break; + case HLine: + case VLine: + if ( type == HLine ) { + p1 = QPoint( r.x(), r.height()/2 ); + p2 = QPoint( r.x()+r.width(), p1.y() ); + } + else { + p1 = QPoint( r.x()+r.width()/2, 0 ); + p2 = QPoint( p1.x(), r.height() ); + } + if ( cstyle == Plain ) { + QPen oldPen = p->pen(); + p->setPen( QPen(g.foreground(),lwidth) ); + p->drawLine( p1, p2 ); + p->setPen( oldPen ); + } + else + qDrawShadeLine( p, p1, p2, g, cstyle == Sunken, + lwidth, midLineWidth() ); + break; + } +#endif // QT_NO_DRAWUTIL +} + + +/*! + Virtual function that draws the contents of the frame. + + The QPainter is already open when you get it, and you must leave + it open. Painter \link QPainter::setWorldMatrix() + transformations\endlink are switched off on entry. If you + transform the painter, remember to take the frame into account and + \link QPainter::resetXForm() reset transformation\endlink before + returning. + + This function is reimplemented by subclasses that draw something + inside the frame. It should only draw inside contentsRect(). The + default function does nothing. + + \sa contentsRect(), QPainter::setClipRect() +*/ + +void QFrame::drawContents( QPainter * ) +{ +} + + +/*! + Virtual function that is called when the frame style, line width + or mid-line width changes. + + This function can be reimplemented by subclasses that need to know + when the frame attributes change. + + The default implementation calls update(). +*/ + +void QFrame::frameChanged() +{ + update(); + updateGeometry(); +} + +/*!\reimp + */ +void QFrame::styleChange( QStyle& old ) +{ + updateFrameWidth( TRUE ); + QWidget::styleChange( old ); +} + +#endif //QT_NO_FRAME diff --git a/src/widgets/qframe.h b/src/widgets/qframe.h new file mode 100644 index 0000000..3e0fabf --- /dev/null +++ b/src/widgets/qframe.h @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Definition of QFrame widget class +** +** Created : 950201 +** +** 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. +** +**********************************************************************/ + +#ifndef QFRAME_H +#define QFRAME_H + +#ifndef QT_H +#include "qwidget.h" +#endif // QT_H + +#ifndef QT_NO_FRAME + +class Q_EXPORT QFrame : public QWidget +{ + Q_OBJECT + Q_ENUMS( Shape Shadow ) + Q_PROPERTY( int frameWidth READ frameWidth ) + Q_PROPERTY( QRect contentsRect READ contentsRect ) + Q_PROPERTY( Shape frameShape READ frameShape WRITE setFrameShape ) + Q_PROPERTY( Shadow frameShadow READ frameShadow WRITE setFrameShadow ) + Q_PROPERTY( int lineWidth READ lineWidth WRITE setLineWidth ) + Q_PROPERTY( int margin READ margin WRITE setMargin ) + Q_PROPERTY( int midLineWidth READ midLineWidth WRITE setMidLineWidth ) + Q_PROPERTY( QRect frameRect READ frameRect WRITE setFrameRect DESIGNABLE false ) + +public: + QFrame( QWidget* parent=0, const char* name=0, WFlags f=0 ); + + int frameStyle() const; + virtual void setFrameStyle( int ); + + int frameWidth() const; + QRect contentsRect() const; + +#ifndef Q_QDOC + bool lineShapesOk() const { return TRUE; } +#endif + + QSize sizeHint() const; + + enum Shape { NoFrame = 0, // no frame + Box = 0x0001, // rectangular box + Panel = 0x0002, // rectangular panel + WinPanel = 0x0003, // rectangular panel (Windows) + HLine = 0x0004, // horizontal line + VLine = 0x0005, // vertical line + StyledPanel = 0x0006, // rectangular panel depending on the GUI style + PopupPanel = 0x0007, // rectangular panel depending on the GUI style + MenuBarPanel = 0x0008, + ToolBarPanel = 0x0009, + LineEditPanel = 0x000a, + TabWidgetPanel = 0x000b, + GroupBoxPanel = 0x000c, + MShape = 0x000f // mask for the shape + }; + enum Shadow { Plain = 0x0010, // plain line + Raised = 0x0020, // raised shadow effect + Sunken = 0x0030, // sunken shadow effect + MShadow = 0x00f0 }; // mask for the shadow + + Shape frameShape() const; + void setFrameShape( Shape ); + Shadow frameShadow() const; + void setFrameShadow( Shadow ); + + int lineWidth() const; + virtual void setLineWidth( int ); + + int margin() const; + virtual void setMargin( int ); + + int midLineWidth() const; + virtual void setMidLineWidth( int ); + + QRect frameRect() const; + virtual void setFrameRect( const QRect & ); + +protected: + void paintEvent( QPaintEvent * ); + void resizeEvent( QResizeEvent * ); + virtual void drawFrame( QPainter * ); + virtual void drawContents( QPainter * ); + virtual void frameChanged(); + void styleChange( QStyle& ); + +private: + void updateFrameWidth(bool=FALSE); + QRect frect; + int fstyle; + short lwidth; + short mwidth; + short mlwidth; + short fwidth; + + void * d; +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QFrame( const QFrame & ); + QFrame &operator=( const QFrame & ); +#endif +}; + + +inline int QFrame::frameStyle() const +{ return fstyle; } + +inline QFrame::Shape QFrame::frameShape() const +{ return (Shape) ( fstyle & MShape ); } + +inline QFrame::Shadow QFrame::frameShadow() const +{ return (Shadow) ( fstyle & MShadow ); } + +inline void QFrame::setFrameShape( QFrame::Shape s ) +{ setFrameStyle( ( fstyle & MShadow ) | s ); } + +inline void QFrame::setFrameShadow( QFrame::Shadow s ) +{ setFrameStyle( ( fstyle & MShape ) | s ); } + +inline int QFrame::lineWidth() const +{ return lwidth; } + +inline int QFrame::midLineWidth() const +{ return mlwidth; } + +inline int QFrame::margin() const +{ return mwidth; } + +inline int QFrame::frameWidth() const +{ return fwidth; } + + +#endif // QT_NO_FRAME + +#endif // QFRAME_H diff --git a/src/widgets/qgrid.cpp b/src/widgets/qgrid.cpp new file mode 100644 index 0000000..47fe88c --- /dev/null +++ b/src/widgets/qgrid.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** 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 "qgrid.h" +#ifndef QT_NO_GRID +#include "qlayout.h" +#include "qapplication.h" + +/*! + \class QGrid qgrid.h + \brief The QGrid widget provides simple geometry management of its children. + + \ingroup geomanagement + \ingroup appearance + + The grid places its widgets either in columns or in rows depending + on its orientation. + + The number of rows \e or columns is defined in the constructor. + All the grid's children will be placed and sized in accordance + with their sizeHint() and sizePolicy(). + + Use setMargin() to add space around the grid itself, and + setSpacing() to add space between the widgets. + + \img qgrid-m.png QGrid + + \sa QVBox QHBox QGridLayout +*/ + +/*! \enum QGrid::Direction + \internal +*/ + +/*! + Constructs a grid widget with parent \a parent, called \a name. + If \a orient is \c Horizontal, \a n specifies the number of + columns. If \a orient is \c Vertical, \a n specifies the number of + rows. The widget flags \a f are passed to the QFrame constructor. +*/ +QGrid::QGrid( int n, Orientation orient, QWidget *parent, const char *name, + WFlags f ) + : QFrame( parent, name, f ) +{ + int nCols, nRows; + if ( orient == Horizontal ) { + nCols = n; + nRows = -1; + } else { + nCols = -1; + nRows = n; + } + lay = new QGridLayout( this, nRows, nCols, 0, 0, name ); + lay->setAutoAdd( TRUE ); +} + + + +/*! + Constructs a grid widget with parent \a parent, called \a name. + \a n specifies the number of columns. The widget flags \a f are + passed to the QFrame constructor. + */ +QGrid::QGrid( int n, QWidget *parent, const char *name, WFlags f ) + : QFrame( parent, name, f ) +{ + lay = new QGridLayout( this, -1, n, 0, 0, name ); + lay->setAutoAdd( TRUE ); +} + + +/*! + Sets the spacing between the child widgets to \a space. +*/ + +void QGrid::setSpacing( int space ) +{ + if ( layout() ) + layout()->setSpacing( space ); +} + + +/*!\reimp + */ +void QGrid::frameChanged() +{ + if ( !layout() ) + return; + layout()->setMargin( frameWidth() ); +} + + +/*! + \reimp +*/ + +QSize QGrid::sizeHint() const +{ + QWidget *mThis = (QWidget*)this; + QApplication::sendPostedEvents( mThis, QEvent::ChildInserted ); + return QFrame::sizeHint(); +} +#endif diff --git a/src/widgets/qgrid.h b/src/widgets/qgrid.h new file mode 100644 index 0000000..24ec9b6 --- /dev/null +++ b/src/widgets/qgrid.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** 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. +** +**********************************************************************/ + +#ifndef QGRID_H +#define QGRID_H + +#ifndef QT_H +#include "qframe.h" +#endif // QT_H + +#ifndef QT_NO_GRID + +class QGridLayout; + +class Q_EXPORT QGrid : public QFrame +{ + Q_OBJECT +public: + QGrid( int n, QWidget* parent=0, const char* name=0, WFlags f = 0 ); + QGrid( int n, Orientation orient, QWidget* parent=0, const char* name=0, + WFlags f = 0 ); + + void setSpacing( int ); + QSize sizeHint() const; + +#ifndef QT_NO_COMPAT + typedef Orientation Direction; +#endif + +protected: + void frameChanged(); + +private: + QGridLayout *lay; +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QGrid( const QGrid & ); + QGrid& operator=( const QGrid & ); +#endif +}; + +#endif // QT_NO_GRID + +#endif // QGRID_H diff --git a/src/widgets/qgridview.cpp b/src/widgets/qgridview.cpp new file mode 100644 index 0000000..902d14c --- /dev/null +++ b/src/widgets/qgridview.cpp @@ -0,0 +1,369 @@ +/**************************************************************************** +** +** Implementation of QGridView class +** +** Created : 010523 +** +** 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 "qgridview.h" + +#ifndef QT_NO_GRIDVIEW + +#include "qpainter.h" + +/*! + \class QGridView qgridview.h + \brief The QGridView class provides an abstract base for + fixed-size grids. + + \ingroup abstractwidgets + + A grid view consists of a number of abstract cells organized in + rows and columns. The cells have a fixed size and are identified + with a row index and a column index. The top-left cell is in row + 0, column 0. The bottom-right cell is in row numRows()-1, column + numCols()-1. + + You can define \l numRows, \l numCols, \l cellWidth and \l + cellHeight. Reimplement the pure virtual function paintCell() to + draw the contents of a cell. + + With ensureCellVisible(), you can ensure a certain cell is + visible. With rowAt() and columnAt() you can find a cell based on + the given x- and y-coordinates. + + If you need to monitor changes to the grid's dimensions (i.e. when + numRows or numCols is changed), reimplement the dimensionChange() + change handler. + + Note: the row and column indices are always given in the order, + row (vertical offset) then column (horizontal offset). This order + is the opposite of all pixel operations, which are given in the + order x (horizontal offset), y (vertical offset). + + QGridView is a very simple abstract class based on QScrollView. It + is designed to simplify the task of drawing many cells of the same + size in a potentially scrollable canvas. If you need rows and + columns with different sizes, use a QTable instead. If you need a + simple list of items, use a QListBox. If you need to present + hierachical data use a QListView, and if you need random objects + at random positions, consider using either a QIconView or a + QCanvas. +*/ + + +/*! + Constructs a grid view. + + The \a parent, \a name and widget flag, \a f, arguments are passed + to the QScrollView constructor. +*/ +QGridView::QGridView( QWidget *parent, const char *name, WFlags f ) + :QScrollView( parent, name, f | WStaticContents ), + nrows( 5 ), ncols( 5 ), cellw( 12 ), cellh( 12 ) +{ + viewport()->setBackgroundMode( PaletteBase ); + setBackgroundMode( PaletteBackground, PaletteBase ); + viewport()->setFocusProxy( this ); +} + +/*! + Destroys the grid view. +*/ +QGridView::~QGridView() +{ +} + +void QGridView::updateGrid() +{ + resizeContents( ncols * cellw, nrows * cellh ); +} + +/*! + \property QGridView::numRows + \brief The number of rows in the grid + + \sa numCols +*/ +void QGridView::setNumRows( int numRows ) +{ + int oldnrows = nrows; + nrows = numRows; + dimensionChange( oldnrows, ncols ); + updateGrid(); +} + +/*! + \property QGridView::numCols + \brief The number of columns in the grid + + \sa numRows +*/ +void QGridView::setNumCols( int numCols ) +{ + int oldncols = ncols; + ncols = numCols; + dimensionChange( nrows, oldncols ); + updateGrid(); +} + +/*! + \property QGridView::cellWidth + \brief The width of a grid column + + All columns in a grid view have the same width. + + \sa cellHeight +*/ +void QGridView::setCellWidth( int cellWidth ) +{ + cellw = cellWidth; + updateGrid(); + updateContents(); +} + +/*! + \property QGridView::cellHeight + \brief The height of a grid row + + All rows in a grid view have the same height. + + \sa cellWidth +*/ +void QGridView::setCellHeight( int cellHeight ) +{ + cellh = cellHeight; + updateGrid(); + updateContents(); +} + +/*! + Returns the geometry of cell (\a row, \a column) in the content + coordinate system. + + \sa cellRect() + */ +QRect QGridView::cellGeometry( int row, int column ) +{ + QRect r; + if ( row >= 0 && row < nrows && column >= 0 && column < ncols ) + r.setRect( cellw * column, cellh * row, cellw, cellh ); + return r; +} + +/*! + Repaints cell (\a row, \a column). + + If \a erase is TRUE, Qt erases the area of the cell before the + paintCell() call; otherwise no erasing takes place. + + \sa QWidget::repaint() +*/ +void QGridView::repaintCell( int row, int column, bool erase ) +{ + repaintContents( cellGeometry( row, column ), erase ); +} + +/*! + Updates cell (\a row, \a column). + + \sa QWidget::update() +*/ +void QGridView::updateCell( int row, int column ) +{ + updateContents( cellGeometry( row, column ) ); +} + +/*! + Ensures cell (\a row, \a column) is visible, scrolling the grid + view if necessary. +*/ +void QGridView::ensureCellVisible( int row, int column ) +{ + QRect r = cellGeometry( row, column ); + ensureVisible( r.x(), r.y(), r.width(), r.height() ); +} + +/*! + This function fills the \a cw pixels wide and \a ch pixels high + rectangle starting at position (\a cx, \a cy) with the background + color using the painter \a p. + + paintEmptyArea() is invoked by drawContents() to erase or fill + unused areas. +*/ + +void QGridView::paintEmptyArea( QPainter *p, int cx ,int cy, int cw, int ch) +{ + if ( gridSize().width() >= contentsWidth() && gridSize().height() >= contentsHeight() ) + return; + // Region of the rect we should draw + contentsToViewport( cx, cy, cx, cy ); + QRegion reg( QRect( cx, cy, cw, ch ) ); + // Subtract the table from it + reg = reg.subtract( QRect( contentsToViewport( QPoint( 0, 0 ) ), gridSize() ) ); + + // And draw the rectangles (transformed as needed) + QMemArray<QRect> r = reg.rects(); + const QBrush &brush = backgroundBrush(); + for ( int i = 0; i < (int)r.count(); ++i) + p->fillRect( r[ i ], brush ); +} + +/*!\reimp + */ +void QGridView::drawContents( QPainter *p, int cx, int cy, int cw, int ch ) +{ + int colfirst = columnAt( cx ); + int collast = columnAt( cx + cw ); + int rowfirst = rowAt( cy ); + int rowlast = rowAt( cy + ch ); + + if ( rowfirst == -1 || colfirst == -1 ) { + paintEmptyArea( p, cx, cy, cw, ch ); + return; + } + + if ( collast < 0 || collast >= ncols ) + collast = ncols-1; + if ( rowlast < 0 || rowlast >= nrows ) + rowlast = nrows-1; + + // Go through the rows + for ( int r = rowfirst; r <= rowlast; ++r ) { + // get row position and height + int rowp = r * cellh; + + // Go through the columns in the row r + // if we know from where to where, go through [colfirst, collast], + // else go through all of them + for ( int c = colfirst; c <= collast; ++c ) { + // get position and width of column c + int colp = c * cellw; + // Translate painter and draw the cell + p->translate( colp, rowp ); + paintCell( p, r, c ); + p->translate( -colp, -rowp ); + } + } + + // Paint empty rects + paintEmptyArea( p, cx, cy, cw, ch ); +} + +/*! + \reimp + + (Implemented to get rid of a compiler warning.) +*/ +void QGridView::drawContents( QPainter * ) +{ +} + +/*! + \fn void QGridView::dimensionChange( int oldNumRows, int oldNumCols ) + + This change handler is called whenever any of the grid's + dimensions change. \a oldNumRows and \a oldNumCols contain the + old dimensions, numRows() and numCols() contain the new + dimensions. +*/ +void QGridView::dimensionChange( int, int ) {} + + + +/*! + \fn int QGridView::rowAt( int y ) const + + Returns the number of the row at position \a y. \a y must be given + in content coordinates. + + \sa columnAt() +*/ + +/*! + \fn int QGridView::columnAt( int x ) const + + Returns the number of the column at position \a x. \a x must be + given in content coordinates. + + \sa rowAt() +*/ + +/*! + \fn void QGridView::paintCell( QPainter *p, int row, int col ) + + This pure virtual function is called to paint the single cell at + (\a row, \a col) using painter \a p. The painter must be open when + paintCell() is called and must remain open. + + The coordinate system is \link QPainter::translate() translated + \endlink so that the origin is at the top-left corner of the cell + to be painted, i.e. \e cell coordinates. Do not scale or shear + the coordinate system (or if you do, restore the transformation + matrix before you return). + + The painter is not clipped by default in order to get maximum + efficiency. If you want clipping, use + + \code + p->setClipRect( cellRect(), QPainter::CoordPainter ); + //... your drawing code + p->setClipping( FALSE ); + + \endcode +*/ + +/*! + \fn QRect QGridView::cellRect() const + + Returns the geometry of a cell in a cell's coordinate system. This + is a convenience function useful in paintCell(). It is equivalent + to QRect( 0, 0, cellWidth(), cellHeight() ). + + \sa cellGeometry() + +*/ + +/*! + \fn QSize QGridView::gridSize() const + + Returns the size of the grid in pixels. + +*/ + +#endif // QT_NO_GRIDVIEW diff --git a/src/widgets/qgridview.h b/src/widgets/qgridview.h new file mode 100644 index 0000000..860aa37 --- /dev/null +++ b/src/widgets/qgridview.h @@ -0,0 +1,139 @@ +/********************************************************************** +** +** Definition of QGridView class +** +** Created : 010523 +** +** 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. +** +**********************************************************************/ + +#ifndef QGRIDVIEW_H +#define QGRIDVIEW_H + +#ifndef QT_H +#include "qscrollview.h" +#endif // QT_H + +#ifndef QT_NO_GRIDVIEW + +class QGridViewPrivate; + +class Q_EXPORT QGridView : public QScrollView +{ + Q_OBJECT + Q_PROPERTY( int numRows READ numRows WRITE setNumRows ) + Q_PROPERTY( int numCols READ numCols WRITE setNumCols ) + Q_PROPERTY( int cellWidth READ cellWidth WRITE setCellWidth ) + Q_PROPERTY( int cellHeight READ cellHeight WRITE setCellHeight ) +public: + + QGridView( QWidget *parent=0, const char *name=0, WFlags f=0 ); + ~QGridView(); + + int numRows() const; + virtual void setNumRows( int ); + int numCols() const; + virtual void setNumCols( int ); + + int cellWidth() const; + virtual void setCellWidth( int ); + int cellHeight() const; + virtual void setCellHeight( int ); + + QRect cellRect() const; + QRect cellGeometry( int row, int column ); + QSize gridSize() const; + + int rowAt( int y ) const; + int columnAt( int x ) const; + + void repaintCell( int row, int column, bool erase=TRUE ); + void updateCell( int row, int column ); + void ensureCellVisible( int row, int column ); + +protected: + virtual void paintCell( QPainter *, int row, int col ) = 0; + virtual void paintEmptyArea( QPainter *p, int cx, int cy, int cw, int ch ); + + void drawContents( QPainter *p, int cx, int cy, int cw, int ch ); + + virtual void dimensionChange( int, int ); + +private: + void drawContents( QPainter* ); + void updateGrid(); + + int nrows; + int ncols; + int cellw; + int cellh; + QGridViewPrivate* d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QGridView( const QGridView & ); + QGridView &operator=( const QGridView & ); +#endif +}; + +inline int QGridView::cellWidth() const +{ return cellw; } + +inline int QGridView::cellHeight() const +{ return cellh; } + +inline int QGridView::rowAt( int y ) const +{ return y / cellh; } + +inline int QGridView::columnAt( int x ) const +{ return x / cellw; } + +inline int QGridView::numRows() const +{ return nrows; } + +inline int QGridView::numCols() const +{return ncols; } + +inline QRect QGridView::cellRect() const +{ return QRect( 0, 0, cellw, cellh ); } + +inline QSize QGridView::gridSize() const +{ return QSize( ncols * cellw, nrows * cellh ); } + + + +#endif // QT_NO_GRIDVIEW + + +#endif // QTABLEVIEW_H diff --git a/src/widgets/qgroupbox.cpp b/src/widgets/qgroupbox.cpp new file mode 100644 index 0000000..6097ece --- /dev/null +++ b/src/widgets/qgroupbox.cpp @@ -0,0 +1,989 @@ +/********************************************************************** +** +** Implementation of QGroupBox widget class +** +** Created : 950203 +** +** 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 "qgroupbox.h" +#ifndef QT_NO_GROUPBOX +#include "qlayout.h" +#include "qpainter.h" +#include "qbitmap.h" +#include "qaccel.h" +#include "qradiobutton.h" +#include "qfocusdata.h" +#include "qobjectlist.h" +#include "qdrawutil.h" +#include "qapplication.h" +#include "qstyle.h" +#include "qcheckbox.h" +#include "qbuttongroup.h" +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif + +/*! + \class QGroupBox qgroupbox.h + \brief The QGroupBox widget provides a group box frame with a title. + + \ingroup organizers + \ingroup geomanagement + \ingroup appearance + \mainclass + + A group box provides a frame, a title and a keyboard shortcut, and + displays various other widgets inside itself. The title is on top, + the keyboard shortcut moves keyboard focus to one of the group + box's child widgets, and the child widgets are usually laid out + horizontally (or vertically) inside the frame. + + The simplest way to use it is to create a group box with the + desired number of columns (or rows) and orientation, and then just + create widgets with the group box as parent. + + It is also possible to change the orientation() and number of + columns() after construction, or to ignore all the automatic + layout support and manage the layout yourself. You can add 'empty' + spaces to the group box with addSpace(). + + QGroupBox also lets you set the title() (normally set in the + constructor) and the title's alignment(). + + You can change the spacing used by the group box with + setInsideMargin() and setInsideSpacing(). To minimize space + consumption, you can remove the right, left and bottom edges of + the frame with setFlat(). + + <img src=qgrpbox-w.png> + + \sa QButtonGroup +*/ + +class QCheckBox; + +class QGroupBoxPrivate +{ +public: + QGroupBoxPrivate(): + spacer( 0 ), + checkbox( 0 ) {} + + QSpacerItem *spacer; + QCheckBox *checkbox; +}; + + + + +/*! + Constructs a group box widget with no title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. + + This constructor does not do automatic layout. +*/ + +QGroupBox::QGroupBox( QWidget *parent, const char *name ) + : QFrame( parent, name ) +{ + init(); +} + +/*! + Constructs a group box with the title \a title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. + + This constructor does not do automatic layout. +*/ + +QGroupBox::QGroupBox( const QString &title, QWidget *parent, const char *name ) + : QFrame( parent, name ) +{ + init(); + setTitle( title ); +} + +/*! + Constructs a group box with no title. Child widgets will be + arranged in \a strips rows or columns (depending on \a + orientation). + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +QGroupBox::QGroupBox( int strips, Orientation orientation, + QWidget *parent, const char *name ) + : QFrame( parent, name ) +{ + init(); + setColumnLayout( strips, orientation ); +} + +/*! + Constructs a group box titled \a title. Child widgets will be + arranged in \a strips rows or columns (depending on \a + orientation). + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +QGroupBox::QGroupBox( int strips, Orientation orientation, + const QString &title, QWidget *parent, + const char *name ) + : QFrame( parent, name ) +{ + init(); + setTitle( title ); + setColumnLayout( strips, orientation ); +} + +/*! + Destroys the group box. +*/ +QGroupBox::~QGroupBox() +{ + delete d; +} + +void QGroupBox::init() +{ + align = AlignAuto; + setFrameStyle( QFrame::GroupBoxPanel | QFrame::Sunken ); +#ifndef QT_NO_ACCEL + accel = 0; +#endif + vbox = 0; + grid = 0; + d = new QGroupBoxPrivate(); + lenvisible = 0; + nCols = nRows = 0; + dir = Horizontal; + marg = 11; + spac = 5; + bFlat = FALSE; +} + +void QGroupBox::setTextSpacer() +{ + if ( !d->spacer ) + return; + int h = 0; + int w = 0; + if ( isCheckable() || lenvisible ) { + QFontMetrics fm = fontMetrics(); + int fh = fm.height(); + if ( isCheckable() ) { +#ifndef QT_NO_CHECKBOX + fh = d->checkbox->sizeHint().height() + 2; + w = d->checkbox->sizeHint().width() + 2*fm.width( "xx" ); +#endif + } else { + fh = fm.height(); + w = fm.width( str, lenvisible ) + 2*fm.width( "xx" ); + } + h = frameRect().y(); + if ( layout() ) { + int m = layout()->margin(); + int sp = layout()->spacing(); + // do we have a child layout? + for ( QLayoutIterator it = layout()->iterator(); it.current(); ++it ) { + if ( it.current()->layout() ) { + m += it.current()->layout()->margin(); + sp = QMAX( sp, it.current()->layout()->spacing() ); + break; + } + } + h = QMAX( fh-m, h ); + h += QMAX( sp - (h+m - fh), 0 ); + } + } + d->spacer->changeSize( w, h, QSizePolicy::Minimum, QSizePolicy::Fixed ); +} + + +void QGroupBox::setTitle( const QString &title ) +{ + if ( str == title ) // no change + return; + str = title; +#ifndef QT_NO_ACCEL + if ( accel ) + delete accel; + accel = 0; + int s = QAccel::shortcutKey( title ); + if ( s ) { + accel = new QAccel( this, "automatic focus-change accelerator" ); + accel->connectItem( accel->insertItem( s, 0 ), + this, SLOT(fixFocus()) ); + } +#endif +#ifndef QT_NO_CHECKBOX + if ( d->checkbox ) { + d->checkbox->setText( str ); + updateCheckBoxGeometry(); + } +#endif + calculateFrame(); + setTextSpacer(); + + update(); + updateGeometry(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::NameChanged ); +#endif +} + +/*! + \property QGroupBox::title + \brief the group box title text. + + The group box title text will have a focus-change keyboard + accelerator if the title contains \&, followed by a letter. + + \code + g->setTitle( "&User information" ); + \endcode + This produces "<u>U</u>ser information"; Alt+U moves the keyboard + focus to the group box. + + There is no default title text. +*/ + +/*! + \property QGroupBox::alignment + \brief the alignment of the group box title. + + The title is always placed on the upper frame line. The horizontal + alignment can be specified by the alignment parameter. + + The alignment is one of the following flags: + \list + \i \c AlignAuto aligns the title according to the language, + usually to the left. + \i \c AlignLeft aligns the title text to the left. + \i \c AlignRight aligns the title text to the right. + \i \c AlignHCenter aligns the title text centered. + \endlist + + The default alignment is \c AlignAuto. + + \sa Qt::AlignmentFlags +*/ + +void QGroupBox::setAlignment( int alignment ) +{ + align = alignment; +#ifndef QT_NO_CHECKBOX + updateCheckBoxGeometry(); +#endif + update(); +} + +/*! \reimp +*/ +void QGroupBox::resizeEvent( QResizeEvent *e ) +{ + QFrame::resizeEvent(e); +#ifndef QT_NO_CHECKBOX + if ( align & AlignRight || align & AlignCenter || + ( QApplication::reverseLayout() && !(align & AlignLeft) ) ) + updateCheckBoxGeometry(); +#endif + calculateFrame(); +} + +/*! \reimp + + \internal + overrides QFrame::paintEvent +*/ + +void QGroupBox::paintEvent( QPaintEvent *event ) +{ + QPainter paint( this ); + + if ( lenvisible && !isCheckable() ) { // draw title + QFontMetrics fm = paint.fontMetrics(); + int h = fm.height(); + int tw = fm.width( str, lenvisible ) + fm.width(QChar(' ')); + int x; + int marg = bFlat ? 0 : 8; + if ( align & AlignHCenter ) // center alignment + x = frameRect().width()/2 - tw/2; + else if ( align & AlignRight ) // right alignment + x = frameRect().width() - tw - marg; + else if ( align & AlignLeft ) // left alignment + x = marg; + else { // auto align + if( QApplication::reverseLayout() ) + x = frameRect().width() - tw - marg; + else + x = marg; + } + QRect r( x, 0, tw, h ); + int va = style().styleHint(QStyle::SH_GroupBox_TextLabelVerticalAlignment, this); + if(va & AlignTop) + r.moveBy(0, fm.descent()); + QColor pen( (QRgb) style().styleHint(QStyle::SH_GroupBox_TextLabelColor, this ) ); + if (!style().styleHint(QStyle::SH_UnderlineAccelerator, this)) + va |= NoAccel; + style().drawItem( &paint, r, ShowPrefix | AlignHCenter | va, colorGroup(), + isEnabled(), 0, str, -1, ownPalette() ? 0 : &pen ); + paint.setClipRegion( event->region().subtract( r ) ); // clip everything but title +#ifndef QT_NO_CHECKBOX + } else if ( d->checkbox ) { + QRect cbClip = d->checkbox->geometry(); + QFontMetrics fm = paint.fontMetrics(); + cbClip.setX( cbClip.x() - fm.width(QChar(' ')) ); + cbClip.setWidth( cbClip.width() + fm.width(QChar(' ')) ); + paint.setClipRegion( event->region().subtract( cbClip ) ); +#endif + } + if ( bFlat ) { + QRect fr = frameRect(); + QPoint p1( fr.x(), fr.y() + 1 ); + QPoint p2( fr.x() + fr.width(), p1.y() ); + // ### This should probably be a style primitive. + qDrawShadeLine( &paint, p1, p2, colorGroup(), TRUE, + lineWidth(), midLineWidth() ); + } else { + drawFrame(&paint); + } + drawContents( &paint ); // draw the contents +} + + +/*! + Adds an empty cell at the next free position. If \a size is + greater than 0, the empty cell takes \a size to be its fixed width + (if orientation() is \c Horizontal) or height (if orientation() is + \c Vertical). + + Use this method to separate the widgets in the group box or to + skip the next free cell. For performance reasons, call this method + after calling setColumnLayout() or by changing the \l + QGroupBox::columns or \l QGroupBox::orientation properties. It is + generally a good idea to call these methods first (if needed at + all), and insert the widgets and spaces afterwards. +*/ +void QGroupBox::addSpace( int size ) +{ + QApplication::sendPostedEvents( this, QEvent::ChildInserted ); + + if ( nCols <= 0 || nRows <= 0 ) + return; + + if ( row >= nRows || col >= nCols ) + grid->expand( row+1, col+1 ); + + if ( size > 0 ) { + QSpacerItem *spacer + = new QSpacerItem( ( dir == Horizontal ) ? 0 : size, + ( dir == Vertical ) ? 0 : size, + QSizePolicy::Fixed, QSizePolicy::Fixed ); + grid->addItem( spacer, row, col ); + } + + skip(); +} + +/*! + \property QGroupBox::columns + \brief the number of columns or rows (depending on \l QGroupBox::orientation) in the group box + + Usually it is not a good idea to set this property because it is + slow (it does a complete layout). It is best to set the number + of columns directly in the constructor. +*/ +int QGroupBox::columns() const +{ + if ( dir == Horizontal ) + return nCols; + return nRows; +} + +void QGroupBox::setColumns( int c ) +{ + setColumnLayout( c, dir ); +} + +/*! + Returns the width of the empty space between the items in the + group and the frame of the group. + + Only applies if the group box has a defined orientation. + + The default is usually 11, by may vary depending on the platform + and style. + + \sa setInsideMargin(), orientation +*/ +int QGroupBox::insideMargin() const +{ + return marg; +} + +/*! + Returns the width of the empty space between each of the items + in the group. + + Only applies if the group box has a defined orientation. + + The default is usually 5, by may vary depending on the platform + and style. + + \sa setInsideSpacing(), orientation +*/ +int QGroupBox::insideSpacing() const +{ + return spac; +} + +/*! + Sets the the width of the inside margin to \a m pixels. + + \sa insideMargin() +*/ +void QGroupBox::setInsideMargin( int m ) +{ + marg = m; + setColumnLayout( columns(), dir ); +} + +/*! + Sets the width of the empty space between each of the items in + the group to \a s pixels. + + \sa insideSpacing() +*/ +void QGroupBox::setInsideSpacing( int s ) +{ + spac = s; + setColumnLayout( columns(), dir ); +} + +/*! + \property QGroupBox::orientation + \brief the group box's orientation + + A horizontal group box arranges it's children in columns, while a + vertical group box arranges them in rows. + + Usually it is not a good idea to set this property because it is + slow (it does a complete layout). It is better to set the + orientation directly in the constructor. +*/ +void QGroupBox::setOrientation( Qt::Orientation o ) +{ + setColumnLayout( columns(), o ); +} + +/*! + Changes the layout of the group box. This function is only useful + in combination with the default constructor that does not take any + layout information. This function will put all existing children + in the new layout. It is not good Qt programming style to call + this function after children have been inserted. Sets the number + of columns or rows to be \a strips, depending on \a direction. + + \sa orientation columns +*/ +void QGroupBox::setColumnLayout(int strips, Orientation direction) +{ + if ( layout() ) + delete layout(); + + vbox = 0; + grid = 0; + + if ( strips < 0 ) // if 0, we create the vbox but not the grid. See below. + return; + + vbox = new QVBoxLayout( this, marg, 0 ); + + d->spacer = new QSpacerItem( 0, 0, QSizePolicy::Minimum, + QSizePolicy::Fixed ); + + setTextSpacer(); + vbox->addItem( d->spacer ); + + nCols = 0; + nRows = 0; + dir = direction; + + // Send all child events and ignore them. Otherwise we will end up + // with doubled insertion. This won't do anything because nCols == + // nRows == 0. + QApplication::sendPostedEvents( this, QEvent::ChildInserted ); + + // if 0 or smaller , create a vbox-layout but no grid. This allows + // the designer to handle its own grid layout in a group box. + if ( strips <= 0 ) + return; + + dir = direction; + if ( dir == Horizontal ) { + nCols = strips; + nRows = 1; + } else { + nCols = 1; + nRows = strips; + } + grid = new QGridLayout( nRows, nCols, spac ); + row = col = 0; + grid->setAlignment( AlignTop ); + vbox->addLayout( grid ); + + // Add all children + if ( children() ) { + QObjectListIt it( *children() ); + QWidget *w; + while( (w=(QWidget *)it.current()) != 0 ) { + ++it; + if ( w->isWidgetType() +#ifndef QT_NO_CHECKBOX + && w != d->checkbox +#endif + ) + insertWid( w ); + } + } +} + + +/*! \reimp */ +bool QGroupBox::event( QEvent * e ) +{ + if ( e->type() == QEvent::LayoutHint && layout() ) + setTextSpacer(); + return QFrame::event( e ); +} + +/*!\reimp */ +void QGroupBox::childEvent( QChildEvent *c ) +{ + if ( !c->inserted() || !c->child()->isWidgetType() ) + return; + QWidget *w = (QWidget*)c->child(); +#ifndef QT_NO_CHECKBOX + if ( d->checkbox ) { + if ( w == d->checkbox ) + return; + if ( d->checkbox->isChecked() ) { + if ( !w->testWState( WState_ForceDisabled ) ) + w->setEnabled( TRUE ); + } else { + if ( w->isEnabled() ) { + w->setEnabled( FALSE ); + ((QGroupBox*)w)->clearWState( WState_ForceDisabled ); + } + } + } +#endif + if ( !grid ) + return; + insertWid( w ); +} + +void QGroupBox::insertWid( QWidget* w ) +{ + if ( row >= nRows || col >= nCols ) + grid->expand( row+1, col+1 ); + grid->addWidget( w, row, col ); + skip(); + QApplication::postEvent( this, new QEvent( QEvent::LayoutHint ) ); +} + + +void QGroupBox::skip() +{ + // Same as QGrid::skip() + if ( dir == Horizontal ) { + if ( col+1 < nCols ) { + col++; + } else { + col = 0; + row++; + } + } else { //Vertical + if ( row+1 < nRows ) { + row++; + } else { + row = 0; + col++; + } + } +} + + +/*! + \internal + + This private slot finds a widget in this group box that can accept + focus, and gives the focus to that widget. +*/ + +void QGroupBox::fixFocus() +{ + QFocusData * fd = focusData(); + QWidget * orig = fd->home(); + QWidget * best = 0; + QWidget * candidate = 0; + QWidget * w = orig; + do { + QWidget * p = w; + while( p && p != this && !p->isTopLevel() ) + p = p->parentWidget(); + if ( p == this && ( w->focusPolicy() & TabFocus ) == TabFocus + && w->isVisibleTo(this) ) { + if ( w->hasFocus() +#ifndef QT_NO_RADIOBUTTON + || ( !best && ::qt_cast<QRadioButton*>(w) + && ((QRadioButton*)w)->isChecked() ) +#endif + ) + // we prefer a checked radio button or a widget that + // already has focus, if there is one + best = w; + else if ( !candidate ) + // but we'll accept anything that takes focus + candidate = w; + } + w = fd->next(); + } while( w != orig ); + if ( best ) + best->setFocus(); + else if ( candidate ) + candidate->setFocus(); +} + + +/* + Sets the right frame rect depending on the title. Also calculates + the visible part of the title. +*/ +void QGroupBox::calculateFrame() +{ + lenvisible = str.length(); + + if ( lenvisible && !isCheckable() ) { // do we have a label? + QFontMetrics fm = fontMetrics(); + while ( lenvisible ) { + int tw = fm.width( str, lenvisible ) + 4*fm.width(QChar(' ')); + if ( tw < width() ) + break; + lenvisible--; + } + if ( lenvisible ) { // but do we also have a visible label? + QRect r = rect(); + int va = style().styleHint(QStyle::SH_GroupBox_TextLabelVerticalAlignment, this); + if(va & AlignVCenter) + r.setTop( fm.height()/2 ); // frame rect should be + else if(va & AlignTop) + r.setTop(fm.ascent()); + setFrameRect( r ); // smaller than client rect + return; + } + } else if ( isCheckable() ) { +#ifndef QT_NO_CHECKBOX + QRect r = rect(); + int va = style().styleHint(QStyle::SH_GroupBox_TextLabelVerticalAlignment, this); + if( va & AlignVCenter ) + r.setTop( d->checkbox->rect().height()/2 ); + else if( va & AlignTop ) + r.setTop( fontMetrics().ascent() ); + setFrameRect( r ); + return; +#endif + } + + // no visible label + setFrameRect( QRect(0,0,0,0) ); // then use client rect +} + + + +/*! \reimp + */ +void QGroupBox::focusInEvent( QFocusEvent * ) +{ // note no call to super + fixFocus(); +} + + +/*!\reimp + */ +void QGroupBox::fontChange( const QFont & oldFont ) +{ + QWidget::fontChange( oldFont ); +#ifndef QT_NO_CHECKBOX + updateCheckBoxGeometry(); +#endif + calculateFrame(); + setTextSpacer(); +} + +/*! + \reimp +*/ + +QSize QGroupBox::sizeHint() const +{ + QFontMetrics fm( font() ); + int tw, th; + if ( isCheckable() ) { +#ifndef QT_NO_CHECKBOX + tw = d->checkbox->sizeHint().width() + 2*fm.width( "xx" ); + th = d->checkbox->sizeHint().height() + fm.width( QChar(' ') ); +#endif + } else { + tw = fm.width( title() ) + 2 * fm.width( "xx" ); + th = fm.height() + fm.width( QChar(' ') ); + } + + QSize s; + if ( layout() ) { + s = QFrame::sizeHint(); + return s.expandedTo( QSize( tw, 0 ) ); + } else { + QRect r = childrenRect(); + QSize s( 100, 50 ); + s = s.expandedTo( QSize( tw, th ) ); + if ( r.isNull() ) + return s; + + return s.expandedTo( QSize( r.width() + 2 * r.x(), r.height()+ 2 * r.y() ) ); + } +} + +/*! + \property QGroupBox::flat + \brief whether the group box is painted flat or has a frame + + By default a group box has a surrounding frame, with the title + being placed on the upper frame line. In flat mode the right, left + and bottom frame lines are omitted, and only the thin line at the + top is drawn. + + \sa title +*/ +bool QGroupBox::isFlat() const +{ + return bFlat; +} + +void QGroupBox::setFlat( bool b ) +{ + if ( (bool)bFlat == b ) + return; + bFlat = b; + update(); +} + + +/*! + \property QGroupBox::checkable + \brief Whether the group box has a checkbox in its title. + + If this property is TRUE, the group box has a checkbox. If the + checkbox is checked (which is the default), the group box's + children are enabled. + + setCheckable() controls whether or not the group box has a + checkbox, and isCheckable() controls whether the checkbox is + checked or not. +*/ +#ifndef QT_NO_CHECKBOX +void QGroupBox::setCheckable( bool b ) +{ + if ( (d->checkbox != 0) == b ) + return; + + if ( b ) { + if ( !d->checkbox ) { + d->checkbox = new QCheckBox( title(), this, "qt_groupbox_checkbox" ); + if (QButtonGroup *meAsButtonGroup = ::qt_cast<QButtonGroup*>(this)) + meAsButtonGroup->remove(d->checkbox); + setChecked( TRUE ); + setChildrenEnabled( TRUE ); + connect( d->checkbox, SIGNAL( toggled(bool) ), + this, SLOT( setChildrenEnabled(bool) ) ); + connect( d->checkbox, SIGNAL( toggled(bool) ), + this, SIGNAL( toggled(bool) ) ); + updateCheckBoxGeometry(); + } + d->checkbox->show(); + } else { + setChildrenEnabled( TRUE ); + delete d->checkbox; + d->checkbox = 0; + } + calculateFrame(); + setTextSpacer(); + update(); +} +#endif //QT_NO_CHECKBOX + +bool QGroupBox::isCheckable() const +{ +#ifndef QT_NO_CHECKBOX + return ( d->checkbox != 0 ); +#else + return FALSE; +#endif +} + + +bool QGroupBox::isChecked() const +{ +#ifndef QT_NO_CHECKBOX + return d->checkbox && d->checkbox->isChecked(); +#else + return FALSE; +#endif +} + + +/*! + \fn void QGroupBox::toggled( bool on ) + + If the group box has a check box (see \l isCheckable()) this signal + is emitted when the check box is toggled. \a on is TRUE if the check + box is checked; otherwise it is FALSE. +*/ + +/*! + \property QGroupBox::checked + \brief Whether the group box's checkbox is checked. + + If the group box has a check box (see \l isCheckable()), and the + check box is checked (see \l isChecked()), the group box's children + are enabled. If the checkbox is unchecked the children are + disabled. +*/ +#ifndef QT_NO_CHECKBOX +void QGroupBox::setChecked( bool b ) +{ + if ( d->checkbox ) + d->checkbox->setChecked( b ); +} +#endif + +/* + sets all children of the group box except the qt_groupbox_checkbox + to either disabled/enabled +*/ +void QGroupBox::setChildrenEnabled( bool b ) +{ + if ( !children() ) + return; + QObjectListIt it( *children() ); + QObject *o; + while( (o = it.current()) ) { + ++it; + if ( o->isWidgetType() +#ifndef QT_NO_CHECKBOX + && o != d->checkbox +#endif + ) { + QWidget *w = (QWidget*)o; + if ( b ) { + if ( !w->testWState( WState_ForceDisabled ) ) + w->setEnabled( TRUE ); + } else { + if ( w->isEnabled() ) { + w->setEnabled( FALSE ); + ((QGroupBox*)w)->clearWState( WState_ForceDisabled ); + } + } + } + } +} + +/*! \reimp */ +void QGroupBox::setEnabled(bool on) +{ + QFrame::setEnabled(on); + if ( !d->checkbox || !on ) + return; + +#ifndef QT_NO_CHECKBOX + // we are being enabled - disable children + if ( !d->checkbox->isChecked() ) + setChildrenEnabled( FALSE ); +#endif +} + +/* + recalculates and sets the checkbox setGeometry +*/ +#ifndef QT_NO_CHECKBOX +void QGroupBox::updateCheckBoxGeometry() +{ + if ( d->checkbox ) { + QSize cbSize = d->checkbox->sizeHint(); + QRect cbRect( 0, 0, cbSize.width(), cbSize.height() ); + + int marg = bFlat ? 2 : 8; + marg += fontMetrics().width( QChar(' ') ); + + if ( align & AlignHCenter ) { + cbRect.moveCenter( frameRect().center() ); + cbRect.moveTop( 0 ); + } else if ( align & AlignRight ) { + cbRect.moveRight( frameRect().right() - marg ); + } else if ( align & AlignLeft ) { + cbRect.moveLeft( frameRect().left() + marg ); + } else { // auto align + if( QApplication::reverseLayout() ) + cbRect.moveRight( frameRect().right() - marg ); + else + cbRect.moveLeft( frameRect().left() + marg ); + } + + d->checkbox->setGeometry( cbRect ); + } +} +#endif //QT_NO_CHECKBOX + + +#endif //QT_NO_GROUPBOX diff --git a/src/widgets/qgroupbox.h b/src/widgets/qgroupbox.h new file mode 100644 index 0000000..ba464ef --- /dev/null +++ b/src/widgets/qgroupbox.h @@ -0,0 +1,165 @@ +/********************************************************************** +** +** Definition of QGroupBox widget class +** +** Created : 950203 +** +** 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. +** +**********************************************************************/ + +#ifndef QGROUPBOX_H +#define QGROUPBOX_H + +#ifndef QT_H +#include "qframe.h" +#endif // QT_H + +#ifndef QT_NO_GROUPBOX + + +class QAccel; +class QGroupBoxPrivate; +class QVBoxLayout; +class QGridLayout; +class QSpacerItem; + +class Q_EXPORT QGroupBox : public QFrame +{ + Q_OBJECT + Q_PROPERTY( QString title READ title WRITE setTitle ) + Q_PROPERTY( Alignment alignment READ alignment WRITE setAlignment ) + Q_PROPERTY( Orientation orientation READ orientation WRITE setOrientation DESIGNABLE false ) + Q_PROPERTY( int columns READ columns WRITE setColumns DESIGNABLE false ) + Q_PROPERTY( bool flat READ isFlat WRITE setFlat ) +#ifndef QT_NO_CHECKBOX + Q_PROPERTY( bool checkable READ isCheckable WRITE setCheckable ) + Q_PROPERTY( bool checked READ isChecked WRITE setChecked ) +#endif +public: + QGroupBox( QWidget* parent=0, const char* name=0 ); + QGroupBox( const QString &title, + QWidget* parent=0, const char* name=0 ); + QGroupBox( int strips, Orientation o, + QWidget* parent=0, const char* name=0 ); + QGroupBox( int strips, Orientation o, const QString &title, + QWidget* parent=0, const char* name=0 ); + ~QGroupBox(); + + virtual void setColumnLayout(int strips, Orientation o); + + QString title() const { return str; } + virtual void setTitle( const QString &); + + int alignment() const { return align; } + virtual void setAlignment( int ); + + int columns() const; + void setColumns( int ); + + Orientation orientation() const { return dir; } + void setOrientation( Orientation ); + + int insideMargin() const; + int insideSpacing() const; + void setInsideMargin( int m ); + void setInsideSpacing( int s ); + + void addSpace( int ); + QSize sizeHint() const; + + bool isFlat() const; + void setFlat( bool b ); + bool isCheckable() const; +#ifndef QT_NO_CHECKBOX + void setCheckable( bool b ); +#endif + bool isChecked() const; + void setEnabled(bool on); + +#ifndef QT_NO_CHECKBOX +public slots: + void setChecked( bool b ); + +signals: + void toggled( bool ); +#endif +protected: + bool event( QEvent * ); + void childEvent( QChildEvent * ); + void resizeEvent( QResizeEvent * ); + void paintEvent( QPaintEvent * ); + void focusInEvent( QFocusEvent * ); + void fontChange( const QFont & ); + +private slots: + void fixFocus(); + void setChildrenEnabled( bool b ); + +private: + void skip(); + void init(); + void calculateFrame(); + void insertWid( QWidget* ); + void setTextSpacer(); +#ifndef QT_NO_CHECKBOX + void updateCheckBoxGeometry(); +#endif + QString str; + int align; + int lenvisible; +#ifndef QT_NO_ACCEL + QAccel * accel; +#endif + QGroupBoxPrivate * d; + + QVBoxLayout *vbox; + QGridLayout *grid; + int row; + int col : 30; + uint bFlat : 1; + int nRows, nCols; + Orientation dir; + int spac, marg; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QGroupBox( const QGroupBox & ); + QGroupBox &operator=( const QGroupBox & ); +#endif +}; + + +#endif // QT_NO_GROUPBOX + +#endif // QGROUPBOX_H diff --git a/src/widgets/qhbox.cpp b/src/widgets/qhbox.cpp new file mode 100644 index 0000000..bed032b --- /dev/null +++ b/src/widgets/qhbox.cpp @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** 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 "qhbox.h" +#ifndef QT_NO_HBOX +#include "qlayout.h" +#include "qapplication.h" +#include "qobjectlist.h" + + +/*! + \class QHBox qhbox.h + \brief The QHBox widget provides horizontal geometry management + for its child widgets. + + \ingroup organizers + \ingroup geomanagement + \ingroup appearance + + All the horizontal box's child widgets will be placed alongside + each other and sized according to their sizeHint()s. + + Use setMargin() to add space around the edges, and use + setSpacing() to add space between the widgets. Use + setStretchFactor() if you want the widgets to be different sizes + in proportion to one another. (See \link layout.html + Layouts\endlink for more information on stretch factors.) + + \img qhbox-m.png QHBox + + \sa QHBoxLayout QVBox QGrid +*/ + + +/*! + Constructs an hbox widget with parent \a parent, called \a name. + The parent, name and widget flags, \a f, are passed to the QFrame + constructor. +*/ +QHBox::QHBox( QWidget *parent, const char *name, WFlags f ) + :QFrame( parent, name, f ) +{ + lay = new QHBoxLayout( this, frameWidth(), frameWidth(), name ); + lay->setAutoAdd( TRUE ); +} + + +/*! + Constructs a horizontal hbox if \a horizontal is TRUE, otherwise + constructs a vertical hbox (also known as a vbox). + + This constructor is provided for the QVBox class. You should never + need to use it directly. + + The \a parent, \a name and widget flags, \a f, are passed to the + QFrame constructor. +*/ + +QHBox::QHBox( bool horizontal, QWidget *parent , const char *name, WFlags f ) + :QFrame( parent, name, f ) +{ + lay = new QBoxLayout( this, + horizontal ? QBoxLayout::LeftToRight : QBoxLayout::Down, + frameWidth(), frameWidth(), name ); + lay->setAutoAdd( TRUE ); +} + +/*!\reimp + */ +void QHBox::frameChanged() +{ + if ( !layout() ) + return; + layout()->setMargin( frameWidth() ); +} + + +/*! + Sets the spacing between the child widgets to \a space. +*/ + +void QHBox::setSpacing( int space ) +{ + if ( layout() ) // ### why not use this->lay? + layout()->setSpacing( space ); +} + + +/*! + \reimp +*/ + +QSize QHBox::sizeHint() const +{ + QWidget *mThis = (QWidget*)this; + QApplication::sendPostedEvents( mThis, QEvent::ChildInserted ); + return QFrame::sizeHint(); +} + +/*! + Sets the stretch factor of widget \a w to \a stretch. Returns TRUE if + \a w is found. Otherwise returns FALSE. + + \sa QBoxLayout::setStretchFactor() \link layout.html Layouts\endlink +*/ +bool QHBox::setStretchFactor( QWidget* w, int stretch ) +{ + QWidget *mThis = (QWidget*)this; + QApplication::sendPostedEvents( mThis, QEvent::ChildInserted ); + return lay->setStretchFactor( w, stretch ); +} +#endif diff --git a/src/widgets/qhbox.h b/src/widgets/qhbox.h new file mode 100644 index 0000000..f52e358 --- /dev/null +++ b/src/widgets/qhbox.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** 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. +** +**********************************************************************/ + + +#ifndef QHBOX_H +#define QHBOX_H + +#ifndef QT_H +#include "qwidget.h" +#endif // QT_H + +#ifndef QT_NO_HBOX + +#include "qframe.h" + +class QBoxLayout; + +class Q_EXPORT QHBox : public QFrame +{ + Q_OBJECT +public: + QHBox( QWidget* parent=0, const char* name=0, WFlags f=0 ); + + void setSpacing( int ); + bool setStretchFactor( QWidget*, int stretch ); + QSize sizeHint() const; + +protected: + QHBox( bool horizontal, QWidget* parent, const char* name, WFlags f = 0 ); + void frameChanged(); + +private: + QBoxLayout *lay; + +#if defined(Q_DISABLE_COPY) + QHBox( const QHBox & ); + QHBox &operator=( const QHBox & ); +#endif +}; + +#endif // QT_NO_HBOX + +#endif // QHBOX_H diff --git a/src/widgets/qhbuttongroup.cpp b/src/widgets/qhbuttongroup.cpp new file mode 100644 index 0000000..d300833 --- /dev/null +++ b/src/widgets/qhbuttongroup.cpp @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Implementation of QHButtonGroup class +** +** Created : 990602 +** +** 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 "qhbuttongroup.h" +#ifndef QT_NO_HBUTTONGROUP + +/*! + \class QHButtonGroup qhbuttongroup.h + \brief The QHButtonGroup widget organizes QButton widgets in a + group with one horizontal row. + + \ingroup organizers + \ingroup geomanagement + \ingroup appearance + + QHButtonGroup is a convenience class that offers a thin layer on + top of QButtonGroup. From a layout point of view it is effectively + a QHBox that offers a frame with a title and is specifically + designed for buttons. From a functionality point of view it is a + QButtonGroup. + + \img qbuttongroup-h.png QButtonGroup + + \sa QVButtonGroup +*/ + +/*! + Constructs a horizontal button group with no title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ +QHButtonGroup::QHButtonGroup( QWidget *parent, const char *name ) + : QButtonGroup( 1, Vertical /* sic! */, parent, name ) +{ +} + +/*! + Constructs a horizontal button group with the title \a title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +QHButtonGroup::QHButtonGroup( const QString &title, QWidget *parent, + const char *name ) + : QButtonGroup( 1, Vertical /* sic! */, title, parent, name ) +{ +} + +/*! + Destroys the horizontal button group, deleting its child widgets. +*/ +QHButtonGroup::~QHButtonGroup() +{ +} +#endif diff --git a/src/widgets/qhbuttongroup.h b/src/widgets/qhbuttongroup.h new file mode 100644 index 0000000..839c026 --- /dev/null +++ b/src/widgets/qhbuttongroup.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Definition of QHButtonGroup class +** +** Created : 990602 +** +** Copyright (C) 1999-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. +** +**********************************************************************/ + +#ifndef QHBUTTONGROUP_H +#define QHBUTTONGROUP_H + +#ifndef QT_H +#include "qbuttongroup.h" +#endif // QT_H + +#ifndef QT_NO_HBUTTONGROUP + +class Q_EXPORT QHButtonGroup : public QButtonGroup +{ + Q_OBJECT +public: + QHButtonGroup( QWidget* parent=0, const char* name=0 ); + QHButtonGroup( const QString &title, QWidget* parent=0, const char* name=0 ); + ~QHButtonGroup(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QHButtonGroup( const QHButtonGroup & ); + QHButtonGroup &operator=( const QHButtonGroup & ); +#endif +}; + + +#endif // QT_NO_HBUTTONGROUP + +#endif // QHBUTTONGROUP_H diff --git a/src/widgets/qheader.cpp b/src/widgets/qheader.cpp new file mode 100644 index 0000000..82fd012 --- /dev/null +++ b/src/widgets/qheader.cpp @@ -0,0 +1,2049 @@ +/**************************************************************************** +** +** Implementation of QHeader widget class (table header) +** +** Created : 961105 +** +** 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 "qheader.h" +#ifndef QT_NO_HEADER +#include "qpainter.h" +#include "qdrawutil.h" +#include "qpixmap.h" +#include "qbitarray.h" +#include "qptrvector.h" +#include "qapplication.h" +#include "qstyle.h" + +class QHeaderData +{ +public: + QHeaderData(int n) + { + count = n; + labels.setAutoDelete( TRUE ); + iconsets.setAutoDelete( TRUE ); + sizes.resize(n); + positions.resize(n); + labels.resize(n); + if ( int( iconsets.size() ) < n ) + iconsets.resize( n ); + i2s.resize(n); + s2i.resize(n); + clicks.resize(n); + resize.resize(n); + int p =0; + for ( int i = 0; i < n; i ++ ) { + sizes[i] = 88; + i2s[i] = i; + s2i[i] = i; + positions[i] = p; + p += sizes[i]; + } + clicks_default = TRUE; + resize_default = TRUE; + clicks.fill( clicks_default ); + resize.fill( resize_default ); + move = TRUE; + sortSection = -1; + sortDirection = TRUE; + positionsDirty = TRUE; + lastPos = 0; + fullSize = -2; + pos_dirty = FALSE; + is_a_table_header = FALSE; + focusIdx = 0; + } + + + QMemArray<QCOORD> sizes; + int height; // we abuse the heights as widths for vertical layout + bool heightDirty; + QMemArray<QCOORD> positions; // sorted by index + QPtrVector<QString> labels; + QPtrVector<QIconSet> iconsets; + QMemArray<int> i2s; + QMemArray<int> s2i; + + QBitArray clicks; + QBitArray resize; + uint move : 1; + uint clicks_default : 1; // default value for new clicks bits + uint resize_default : 1; // default value for new resize bits + uint pos_dirty : 1; + uint is_a_table_header : 1; + bool sortDirection; + bool positionsDirty; + int sortSection; + int count; + int lastPos; + int fullSize; + int focusIdx; + int pressDelta; + + int sectionAt( int pos ) { + // positions is sorted by index, not by section + if ( !count ) + return -1; + int l = 0; + int r = count - 1; + int i = ( (l+r+1) / 2 ); + while ( r - l ) { + if ( positions[i] > pos ) + r = i -1; + else + l = i; + i = ( (l+r+1) / 2 ); + } + if ( positions[i] <= pos && pos <= positions[i] + sizes[ i2s[i] ] ) + return i2s[i]; + return -1; + } +}; + + +/*! + \class QHeader qheader.h + \brief The QHeader class provides a header row or column, e.g. for + tables and listviews. + + \ingroup advanced + + This class provides a header, e.g. a vertical header to display + row labels, or a horizontal header to display column labels. It is + used by QTable and QListView for example. + + A header is composed of one or more \e sections, each of which can + display a text label and an \link QIconSet iconset\endlink. A sort + indicator (an arrow) can also be displayed using + setSortIndicator(). + + Sections are added with addLabel() and removed with removeLabel(). + The label and iconset are set in addLabel() and can be changed + later with setLabel(). Use count() to retrieve the number of + sections in the header. + + The orientation of the header is set with setOrientation(). If + setStretchEnabled() is TRUE, the sections will expand to take up + the full width (height for vertical headers) of the header. The + user can resize the sections manually if setResizeEnabled() is + TRUE. Call adjustHeaderSize() to have the sections resize to + occupy the full width (or height). + + A section can be moved with moveSection(). If setMovingEnabled() + is TRUE (the default)the user may drag a section from one position + to another. If a section is moved, the index positions at which + sections were added (with addLabel()), may not be the same after the + move. You don't have to worry about this in practice because the + QHeader API works in terms of section numbers, so it doesn't matter + where a particular section has been moved to. + + If you want the current index position of a section call + mapToIndex() giving it the section number. (This is the number + returned by the addLabel() call which created the section.) If you + want to get the section number of a section at a particular index + position call mapToSection() giving it the index number. + + Here's an example to clarify mapToSection() and mapToIndex(): + + \table + \header \i41 Index positions + \row \i 0 \i 1 \i 2 \i 3 + \header \i41 Original section ordering + \row \i Sect 0 \i Sect 1 \i Sect 2 \i Sect 3 + \header \i41 Ordering after the user moves a section + \row \i Sect 0 \i Sect 2 \i Sect 3 \i Sect 1 + \endtable + + \table + \header \i \e k \i mapToSection(\e k) \i mapToIndex(\e k) + \row \i 0 \i 0 \i 0 + \row \i 1 \i 2 \i 3 + \row \i 2 \i 3 \i 1 + \row \i 3 \i 1 \i 2 + \endtable + + In the example above, if we wanted to find out which section is at + index position 3 we'd call mapToSection(3) and get a section + number of 1 since section 1 was moved. Similarly, if we wanted to + know which index position section 2 occupied we'd call + mapToIndex(2) and get an index of 1. + + QHeader provides the clicked(), pressed() and released() signals. + If the user changes the size of a section, the sizeChange() signal + is emitted. If you want to have a sizeChange() signal emitted + continuously whilst the user is resizing (rather than just after + the resizing is finished), use setTracking(). If the user moves a + section the indexChange() signal is emitted. + + <img src=qheader-m.png> <img src=qheader-w.png> + + \sa QListView QTable +*/ + + + +/*! + Constructs a horizontal header called \a name, with parent \a + parent. +*/ + +QHeader::QHeader( QWidget *parent, const char *name ) + : QWidget( parent, name, WStaticContents ) +{ + orient = Horizontal; + init( 0 ); +} + +/*! + Constructs a horizontal header called \a name, with \a n sections + and parent \a parent. +*/ + +QHeader::QHeader( int n, QWidget *parent, const char *name ) + : QWidget( parent, name, WStaticContents ) +{ + orient = Horizontal; + init( n ); +} + +/*! + Destroys the header and all its sections. +*/ + +QHeader::~QHeader() +{ + delete d; + d = 0; +} + +/*! \reimp + */ + +void QHeader::showEvent( QShowEvent *e ) +{ + calculatePositions(); + QWidget::showEvent( e ); +} + +/*! + \fn void QHeader::sizeChange( int section, int oldSize, int newSize ) + + This signal is emitted when the user has changed the size of a \a + section from \a oldSize to \a newSize. This signal is typically + connected to a slot that repaints the table or list that contains + the header. +*/ + +/*! + \fn void QHeader::clicked( int section ) + + If isClickEnabled() is TRUE, this signal is emitted when the user + clicks section \a section. + + \sa pressed(), released() +*/ + +/*! + \fn void QHeader::pressed( int section ) + + This signal is emitted when the user presses section \a section + down. + + \sa released() +*/ + +/*! + \fn void QHeader::released( int section ) + + This signal is emitted when section \a section is released. + + \sa pressed() +*/ + + +/*! + \fn void QHeader::indexChange( int section, int fromIndex, int toIndex ) + + This signal is emitted when the user moves section \a section from + index position \a fromIndex, to index position \a toIndex. +*/ + +/*! + \fn void QHeader::moved( int fromIndex, int toIndex ) + \obsolete + + Use indexChange() instead. + + This signal is emitted when the user has moved the section which + is displayed at the index \a fromIndex to the index \a toIndex. +*/ + +/*! + \fn void QHeader::sectionClicked( int index ) + \obsolete + + Use clicked() instead. + + This signal is emitted when a part of the header is clicked. \a + index is the index at which the section is displayed. + + In a list view this signal would typically be connected to a slot + that sorts the specified column (or row). +*/ + +/*! \fn int QHeader::cellSize( int ) const + \obsolete + + Use sectionSize() instead. + + Returns the size in pixels of the section that is displayed at + the index \a i. +*/ + +/*! + \fn void QHeader::sectionHandleDoubleClicked( int section ) + + This signal is emitted when the user doubleclicks on the edge + (handle) of section \a section. +*/ + +/*! + \obsolete + + Use sectionPos() instead. + + Returns the position in pixels of the section that is displayed at the + index \a i. The position is measured from the start of the header. +*/ + +int QHeader::cellPos( int i ) const +{ + if ( i == count() && i > 0 ) + return d->positions[i-1] + d->sizes[d->i2s[i-1]]; // compatibility + return sectionPos( mapToSection(i) ); +} + + +/*! + \property QHeader::count + \brief the number of sections in the header +*/ + +int QHeader::count() const +{ + return d->count; +} + + +/*! + \property QHeader::tracking + \brief whether the sizeChange() signal is emitted continuously + + If tracking is on, the sizeChange() signal is emitted continuously + while the mouse is moved (i.e. when the header is resized), + otherwise it is only emitted when the mouse button is released at + the end of resizing. + + Tracking defaults to FALSE. +*/ + + +/* + Initializes with \a n columns. +*/ +void QHeader::init( int n ) +{ + state = Idle; + cachedPos = 0; // unused + d = new QHeaderData( n ); + d->height = 0; + d->heightDirty = TRUE; + offs = 0; + if( reverse() ) + offs = d->lastPos - width(); + oldHandleIdx = oldHIdxSize = handleIdx = 0; + + setMouseTracking( TRUE ); + trackingIsOn = FALSE; + setBackgroundMode( PaletteButton ); + setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) ); +} + +/*! + \property QHeader::orientation + \brief the header's orientation + + The orientation is either \c Vertical or \c Horizontal (the + default). + + Call setOrientation() before adding labels if you don't provide a + size parameter otherwise the sizes will be incorrect. +*/ + +void QHeader::setOrientation( Orientation orientation ) +{ + if ( orient == orientation ) + return; + orient = orientation; + if ( orient == Horizontal ) + setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) ); + else + setSizePolicy( QSizePolicy( QSizePolicy::Fixed, QSizePolicy::Preferred ) ); + update(); + updateGeometry(); +} + + +/* + Paints a rectangle starting at \a p, with length \s. +*/ +void QHeader::paintRect( int p, int s ) +{ + QPainter paint( this ); + paint.setPen( QPen( black, 1, DotLine ) ); + if ( reverse() ) + paint.drawRect( p - s, 3, s, height() - 5 ); + else if ( orient == Horizontal ) + paint.drawRect( p, 3, s, height() - 5 ); + else + paint.drawRect( 3, p, height() - 5, s ); +} + +/* + Marks the division line at \a idx. +*/ +void QHeader::markLine( int idx ) +{ + QPainter paint( this ); + paint.setPen( QPen( black, 1, DotLine ) ); + int MARKSIZE = style().pixelMetric( QStyle::PM_HeaderMarkSize ); + int p = pPos( idx ); + int x = p - MARKSIZE/2; + int y = 2; + int x2 = p + MARKSIZE/2; + int y2 = height() - 3; + if ( orient == Vertical ) { + int t = x; x = y; y = t; + t = x2; x2 = y2; y2 = t; + } + + paint.drawLine( x, y, x2, y ); + paint.drawLine( x, y+1, x2, y+1 ); + + paint.drawLine( x, y2, x2, y2 ); + paint.drawLine( x, y2-1, x2, y2-1 ); + + paint.drawLine( x, y, x, y2 ); + paint.drawLine( x+1, y, x+1, y2 ); + + paint.drawLine( x2, y, x2, y2 ); + paint.drawLine( x2-1, y, x2-1, y2 ); +} + +/* + Removes the mark at the division line at \a idx. +*/ +void QHeader::unMarkLine( int idx ) +{ + if ( idx < 0 ) + return; + int MARKSIZE = style().pixelMetric( QStyle::PM_HeaderMarkSize ); + int p = pPos( idx ); + int x = p - MARKSIZE/2; + int y = 2; + int x2 = p + MARKSIZE/2; + int y2 = height() - 3; + if ( orient == Vertical ) { + int t = x; x = y; y = t; + t = x2; x2 = y2; y2 = t; + } + repaint( x, y, x2-x+1, y2-y+1 ); +} + +/*! \fn int QHeader::cellAt( int ) const + \obsolete + + Use sectionAt() instead. + + Returns the index at which the section is displayed, which contains + \a pos in widget coordinates, or -1 if \a pos is outside the header + sections. +*/ + +/* + Tries to find a line that is not a neighbor of \c handleIdx. +*/ +int QHeader::findLine( int c ) +{ + int i = 0; + if ( c > d->lastPos || (reverse() && c < 0 )) { + return d->count; + } else { + int section = sectionAt( c ); + if ( section < 0 ) + return handleIdx; + i = d->s2i[section]; + } + int MARKSIZE = style().pixelMetric( QStyle::PM_HeaderMarkSize ); + if ( i == handleIdx ) + return i; + if ( i == handleIdx - 1 && pPos( handleIdx ) - c > MARKSIZE/2 ) + return i; + if ( i == handleIdx + 1 && c - pPos( i ) > MARKSIZE/2 ) + return i + 1; + if ( c - pPos( i ) > pSize( i ) / 2 ) + return i + 1; + else + return i; +} + +/*! + Returns the handle at position \a p, or -1 if there is no handle at \a p. +*/ +int QHeader::handleAt(int p) +{ + int section = d->sectionAt( p ); + if ( section >= 0 ) { + int GripMargin = (bool)d->resize[ section ] ? + style().pixelMetric( QStyle::PM_HeaderGripMargin ) : 0; + int index = d->s2i[section]; + if ( (index > 0 && p < d->positions[index] + GripMargin) || + (p > d->positions[index] + d->sizes[section] - GripMargin) ) { + if ( index > 0 && p < d->positions[index] + GripMargin ) + section = d->i2s[--index]; + // dont show icon if streaching is enabled it is at the end of the last section + if ( d->resize.testBit(section) && (d->fullSize == -2 || index != count() - 1)) { + return section; + } + } + } + + return -1; +} + +/*! + \obsolete + + Use moveSection() instead. + + Moves the section that is currently displayed at index \a fromIdx + to index \a toIdx. +*/ + +void QHeader::moveCell( int fromIdx, int toIdx ) +{ + moveSection( mapToSection(fromIdx), toIdx ); +} + + + +/*! + Move and signal and repaint. + */ + +void QHeader::handleColumnMove( int fromIdx, int toIdx ) +{ + int s = d->i2s[fromIdx]; + if ( fromIdx < toIdx ) + toIdx++; //Convert to + QRect r = sRect( fromIdx ); + r |= sRect( toIdx ); + moveSection( s, toIdx ); + update( r ); + emit moved( fromIdx, toIdx ); + emit indexChange( s, fromIdx, toIdx ); +} + +/*! + \reimp +*/ +void QHeader::keyPressEvent( QKeyEvent *e ) +{ + int i = d->focusIdx; + if ( e->key() == Key_Space ) { + //don't do it if we're doing something with the mouse + if ( state == Idle && d->clicks[ d->i2s[d->focusIdx] ] ) { + handleIdx = i; + state = Pressed; + repaint( sRect( handleIdx ) ); + emit pressed( d->i2s[i] ); + } + } else if ( orientation() == Horizontal && + (e->key() == Key_Right || e->key() == Key_Left) + || orientation() == Vertical && + (e->key() == Key_Up || e->key() == Key_Down) ) { + int dir = e->key() == Key_Right || e->key() == Key_Down ? 1 : -1; + int s = d->i2s[i]; + if ( e->state() & ControlButton && d->resize[s] ) { + //resize + int step = e->state() & ShiftButton ? dir : 10*dir; + int c = d->positions[i] + d->sizes[s] + step; + handleColumnResize( i, c, TRUE ); + } else if ( e->state() & (AltButton|MetaButton) && d->move ) { + //move section + int i2 = ( i + count() + dir ) % count(); + d->focusIdx = i2; + handleColumnMove( i, i2 ); + } else { + //focus on different section + QRect r = sRect( d->focusIdx ); + d->focusIdx = (d->focusIdx + count() + dir) % count(); + r |= sRect( d->focusIdx ); + update( r ); + } + } else { + e->ignore(); + } +} + +/*! + \reimp +*/ +void QHeader::keyReleaseEvent( QKeyEvent *e ) +{ + switch ( e->key() ) { + case Key_Space: + //double check that this wasn't started with the mouse + if ( state == Pressed && handleIdx == d->focusIdx ) { + repaint(sRect( handleIdx ), FALSE); + int section = d->i2s[d->focusIdx]; + emit released( section ); + emit sectionClicked( handleIdx ); + emit clicked( section ); + state = Idle; + handleIdx = -1; + } + break; + default: + e->ignore(); + } +} + + +/*! + \reimp +*/ +void QHeader::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() != LeftButton || state != Idle ) + return; + oldHIdxSize = handleIdx; + handleIdx = 0; + int c = orient == Horizontal ? e->pos().x() : e->pos().y(); + c += offset(); + if ( reverse() ) + c = d->lastPos - c; + + int section = d->sectionAt( c ); + if ( section < 0 ) + return; + int GripMargin = (bool)d->resize[ section ] ? + style().pixelMetric( QStyle::PM_HeaderGripMargin ) : 0; + int index = d->s2i[section]; + + if ( (index > 0 && c < d->positions[index] + GripMargin) || + (c > d->positions[index] + d->sizes[section] - GripMargin) ) { + if ( c < d->positions[index] + GripMargin ) + handleIdx = index-1; + else + handleIdx = index; + if ( d->lastPos <= ( orient == Horizontal ? width() : + height() ) && d->fullSize != -2 && handleIdx == count() - 1 ) { + handleIdx = -1; + return; + } + oldHIdxSize = d->sizes[ d->i2s[handleIdx] ]; + state = d->resize[ d->i2s[handleIdx] ] ? Sliding : Blocked; + } else if ( index >= 0 ) { + oldHandleIdx = handleIdx = index; + moveToIdx = -1; + state = d->clicks[ d->i2s[handleIdx] ] ? Pressed : Blocked; + clickPos = c; + repaint( sRect( handleIdx ) ); + if(oldHandleIdx != handleIdx) + repaint( sRect( oldHandleIdx ) ); + emit pressed( section ); + } + + d->pressDelta = c - ( d->positions[handleIdx] + d->sizes[ d->i2s[handleIdx] ] ); +} + +/*! + \reimp +*/ +void QHeader::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( e->button() != LeftButton ) + return; + int oldOldHandleIdx = oldHandleIdx; + State oldState = state; + state = Idle; + switch ( oldState ) { + case Pressed: { + int section = d->i2s[handleIdx]; + emit released( section ); + if ( sRect( handleIdx ).contains( e->pos() ) ) { + oldHandleIdx = handleIdx; + emit sectionClicked( handleIdx ); + emit clicked( section ); + } else { + handleIdx = oldHandleIdx; + } + repaint(sRect( handleIdx ), FALSE); + if ( oldOldHandleIdx != handleIdx ) + repaint(sRect(oldOldHandleIdx ), FALSE ); + } break; + case Sliding: { + int c = orient == Horizontal ? e->pos().x() : e->pos().y(); + c += offset(); + if ( reverse() ) + c = d->lastPos - c; + handleColumnResize( handleIdx, c - d->pressDelta, TRUE ); + } break; + case Moving: { +#ifndef QT_NO_CURSOR + unsetCursor(); +#endif + int section = d->i2s[handleIdx]; + if ( handleIdx != moveToIdx && moveToIdx != -1 ) { + moveSection( section, moveToIdx ); + handleIdx = oldHandleIdx; + emit moved( handleIdx, moveToIdx ); + emit indexChange( section, handleIdx, moveToIdx ); + emit released( section ); + repaint(); // a bit overkill, but removes the handle as well + } else { + if ( sRect( handleIdx).contains( e->pos() ) ) { + oldHandleIdx = handleIdx; + emit released( section ); + emit sectionClicked( handleIdx ); + emit clicked( section ); + } else { + handleIdx = oldHandleIdx; + } + repaint(sRect( handleIdx ), FALSE ); + if(oldOldHandleIdx != handleIdx) + repaint(sRect(oldOldHandleIdx ), FALSE ); + } + break; + } + case Blocked: + //nothing + break; + default: + // empty, probably. Idle, at any rate. + break; + } +} + +/*! + \reimp +*/ +void QHeader::mouseMoveEvent( QMouseEvent *e ) +{ + int c = orient == Horizontal ? e->pos().x() : e->pos().y(); + c += offset(); + + int pos = c; + if( reverse() ) + c = d->lastPos - c; + + switch( state ) { + case Idle: +#ifndef QT_NO_CURSOR + if ( handleAt(c) < 0 ) + unsetCursor(); + else if ( orient == Horizontal ) + setCursor( splitHCursor ); + else + setCursor( splitVCursor ); +#endif + break; + case Blocked: + break; + case Pressed: + if ( QABS( c - clickPos ) > 4 && d->move ) { + state = Moving; + moveToIdx = -1; +#ifndef QT_NO_CURSOR + if ( orient == Horizontal ) + setCursor( sizeHorCursor ); + else + setCursor( sizeVerCursor ); +#endif + } + break; + case Sliding: + handleColumnResize( handleIdx, c, FALSE, FALSE ); + break; + case Moving: { + int newPos = findLine( pos ); + if ( newPos != moveToIdx ) { + if ( moveToIdx == handleIdx || moveToIdx == handleIdx + 1 ) + repaint( sRect(handleIdx) ); + else + unMarkLine( moveToIdx ); + moveToIdx = newPos; + if ( moveToIdx == handleIdx || moveToIdx == handleIdx + 1 ) + paintRect( pPos( handleIdx ), pSize( handleIdx ) ); + else + markLine( moveToIdx ); + } + break; + } + default: + qWarning( "QHeader::mouseMoveEvent: (%s) unknown state", name() ); + break; + } +} + +/*! \reimp */ + +void QHeader::mouseDoubleClickEvent( QMouseEvent *e ) +{ + int p = orient == Horizontal ? e->pos().x() : e->pos().y(); + p += offset(); + if( reverse() ) + p = d->lastPos - p; + + int header = handleAt(p); + if (header >= 0) + emit sectionHandleDoubleClicked( header ); +} + +/* + Handles resizing of sections. This means it redraws the relevant parts + of the header. +*/ + +void QHeader::handleColumnResize( int index, int c, bool final, bool recalcAll ) +{ + int section = d->i2s[index]; + int GripMargin = (bool)d->resize[ section ] ? + style().pixelMetric( QStyle::PM_HeaderGripMargin ) : 0; + int lim = d->positions[index] + 2*GripMargin; + if ( c == lim ) + return; + if ( c < lim ) + c = lim; + int oldSize = d->sizes[section]; + int newSize = c - d->positions[index]; + d->sizes[section] = newSize; + + calculatePositions( !recalcAll, !recalcAll ? section : 0 ); + + int pos = d->positions[index]-offset(); + if( reverse() ) // repaint the whole thing. Could be optimized (lars) + repaint( 0, 0, width(), height() ); + else if ( orient == Horizontal ) + repaint( pos, 0, width() - pos, height() ); + else + repaint( 0, pos, width(), height() - pos ); + + int os = 0, ns = 0; + if ( tracking() && oldSize != newSize ) { + os = oldSize; + ns = newSize; + emit sizeChange( section, oldSize, newSize ); + } else if ( !tracking() && final && oldHIdxSize != newSize ) { + os = oldHIdxSize; + ns = newSize; + emit sizeChange( section, oldHIdxSize, newSize ); + } + + if ( os != ns ) { + if ( d->fullSize == -1 ) { + d->fullSize = count() - 1; + adjustHeaderSize(); + d->fullSize = -1; + } else if ( d->fullSize >= 0 ) { + int old = d->fullSize; + d->fullSize = count() - 1; + adjustHeaderSize(); + d->fullSize = old; + } + } +} + +/*! + Returns the rectangle covered by the section at index \a index. +*/ + +QRect QHeader::sRect( int index ) +{ + + int section = mapToSection( index ); + if ( count() > 0 && index >= count() ) { + int s = d->positions[count() - 1] - offset() + + d->sizes[mapToSection(count() - 1)]; + if ( orient == Horizontal ) + return QRect( s, 0, width() - s + 10, height() ); + else + return QRect( 0, s, width(), height() - s + 10 ); + } + if ( section < 0 ) + return rect(); // ### eeeeevil + + if ( reverse() ) + return QRect( d->lastPos - d->positions[index] - d->sizes[section] -offset(), + 0, d->sizes[section], height() ); + else if ( orient == Horizontal ) + return QRect( d->positions[index]-offset(), 0, d->sizes[section], height() ); + else + return QRect( 0, d->positions[index]-offset(), width(), d->sizes[section] ); +} + +/*! + Returns the rectangle covered by section \a section. +*/ + +QRect QHeader::sectionRect( int section ) const +{ + int index = mapToIndex( section ); + if ( section < 0 ) + return rect(); // ### eeeeevil + + if ( reverse() ) + return QRect( d->lastPos - d->positions[index] - d->sizes[section] -offset(), + 0, d->sizes[section], height() ); + else if ( orient == Horizontal ) + return QRect( d->positions[index]-offset(), 0, d->sizes[section], height() ); + else + return QRect( 0, d->positions[index]-offset(), width(), d->sizes[section] ); +} + +/*! + \overload + + Sets the icon for section \a section to \a iconset and the text to + \a s. The section's width is set to \a size if \a size \>= 0; + otherwise it is left unchanged. + + If the section does not exist, nothing happens. +*/ + +void QHeader::setLabel( int section, const QIconSet& iconset, + const QString &s, int size ) +{ + if ( section < 0 || section >= count() ) + return; + d->iconsets.insert( section, new QIconSet( iconset ) ); + setLabel( section, s, size ); +} + +/*! + Sets the text of section \a section to \a s. The section's width + is set to \a size if \a size \>= 0; otherwise it is left + unchanged. Any icon set that has been set for this section remains + unchanged. + + If the section does not exist, nothing happens. +*/ +void QHeader::setLabel( int section, const QString &s, int size ) +{ + if ( section < 0 || section >= count() ) + return; + if ( s.isNull() ) + d->labels.remove( section ); + else + d->labels.insert( section, new QString( s ) ); + + setSectionSizeAndHeight( section, size ); + + if ( isUpdatesEnabled() ) { + updateGeometry(); + calculatePositions(); + update(); + } +} + + +bool qt_qheader_label_return_null_strings = FALSE; +/*! + Returns the text for section \a section. If the section does not + exist, a QString::null is returned. +*/ +QString QHeader::label( int section ) const +{ + if ( section < 0 || section >= count() ) + return QString::null; + if ( d->labels[ section ] ) + return *( d->labels[ section ] ); + else if ( qt_qheader_label_return_null_strings ) + return QString::null; + else + return QString::number( section + 1 ); +} + +/*! + Returns the icon set for section \a section. If the section does + not exist, 0 is returned. +*/ + +QIconSet *QHeader::iconSet( int section ) const +{ + if ( section < 0 || section >= count() ) + return 0; + return d->iconsets[ section ]; +} + + +/*! + \overload + + Adds a new section with iconset \a iconset and label text \a s. + Returns the index position where the section was added (at the + right for horizontal headers, at the bottom for vertical headers). + The section's width is set to \a size, unless size is negative in + which case the size is calculated taking account of the size of + the text. +*/ +int QHeader::addLabel( const QIconSet& iconset, const QString &s, int size ) +{ + int n = count() + 1; + d->iconsets.resize( n + 1 ); + d->iconsets.insert( n - 1, new QIconSet( iconset ) ); + return addLabel( s, size ); +} + +/*! + Removes section \a section. If the section does not exist, nothing + happens. +*/ +void QHeader::removeLabel( int section ) +{ + if ( section < 0 || section > count() - 1 ) + return; + + int index = d->s2i[section]; + int n = --d->count; + int i; + for ( i = section; i < n; ++i ) { + d->sizes[i] = d->sizes[i+1]; + d->labels.insert( i, d->labels.take( i + 1 ) ); + d->iconsets.insert( i, d->iconsets.take( i + 1 ) ); + } + + d->sizes.resize( n ); + d->positions.resize( n ); + d->labels.resize( n ); + d->iconsets.resize( n ); + + for ( i = section; i < n; ++i ) + d->s2i[i] = d->s2i[i+1]; + d->s2i.resize( n ); + + if ( isUpdatesEnabled() ) { + for ( i = 0; i < n; ++i ) + if ( d->s2i[i] > index ) + --d->s2i[i]; + } + + for ( i = index; i < n; ++i ) + d->i2s[i] = d->i2s[i+1]; + d->i2s.resize( n ); + + if ( isUpdatesEnabled() ) { + for ( i = 0; i < n; ++i ) + if ( d->i2s[i] > section ) + --d->i2s[i]; + } + + if ( isUpdatesEnabled() ) { + updateGeometry(); + calculatePositions(); + update(); + } +} + +QSize QHeader::sectionSizeHint( int section, const QFontMetrics& fm ) const +{ + int iw = 0; + int ih = 0; + if ( d->iconsets[section] != 0 ) { + QSize isize = d->iconsets[section]->pixmap( QIconSet::Small, + QIconSet::Normal ).size(); + iw = isize.width() + 2; + ih = isize.height(); + } + + QRect bound; + QString *label = d->labels[section]; + if ( label ) { + int lines = label->contains( '\n' ) + 1; + int w = 0; + if (lines > 1) { + bound.setHeight(fm.height() + fm.lineSpacing() * (lines - 1)); + QStringList list = QStringList::split('\n', *label); + for (int i=0; i <(int)list.count(); ++i) { + int tmpw = fm.width(*(list.at(i))); + w = QMAX(w, tmpw); + } + } else { + bound.setHeight(fm.height()); + w = fm.width(*label); + } + bound.setWidth( w ); + } + int arrowWidth = 0; + if ( d->sortSection == section ) + arrowWidth = ( ( orient == Qt::Horizontal ? height() : width() ) / 2 ) + 8; + int height = QMAX( bound.height() + 2, ih ) + 4; + int width = bound.width() + style().pixelMetric( QStyle::PM_HeaderMargin ) * 4 + + iw + arrowWidth; + return QSize( width, height ); +} + +/* + Sets d->sizes[\a section] to a bounding rect based on its size + hint and font metrics, but constrained by \a size. It also updates + d->height. +*/ +void QHeader::setSectionSizeAndHeight( int section, int size ) +{ + QSize sz = sectionSizeHint( section, fontMetrics() ); + + if ( size < 0 ) { + if ( d->sizes[section] < 0 ) + d->sizes[section] = ( orient == Horizontal ) ? sz.width() + : sz.height(); + } else { + d->sizes[section] = size; + } + + int newHeight = ( orient == Horizontal ) ? sz.height() : sz.width(); + if ( newHeight > d->height ) { + d->height = newHeight; + } else if ( newHeight < d->height ) { + /* + We could be smarter, but we aren't. This makes a difference + only for users with many columns and '\n's in their headers + at the same time. + */ + d->heightDirty = TRUE; + } +} + +/*! + Adds a new section with label text \a s. Returns the index + position where the section was added (at the right for horizontal + headers, at the bottom for vertical headers). The section's width + is set to \a size. If \a size \< 0, an appropriate size for the + text \a s is chosen. +*/ +int QHeader::addLabel( const QString &s, int size ) +{ + int n = ++d->count; + if ( (int)d->iconsets.size() < n ) + d->iconsets.resize( n ); + if ( (int)d->sizes.size() < n ) { + d->labels.resize( n ); + d->sizes.resize( n ); + d->positions.resize( n ); + d->i2s.resize( n ); + d->s2i.resize( n ); + d->clicks.resize( n ); + d->resize.resize( n ); + } + int section = d->count - 1; + if ( !d->is_a_table_header || !s.isNull() ) + d->labels.insert( section, new QString( s ) ); + + if ( size >= 0 && s.isNull() && d->is_a_table_header ) { + d->sizes[section] = size; + } else { + d->sizes[section] = -1; + setSectionSizeAndHeight( section, size ); + } + + int index = section; + d->positions[index] = d->lastPos; + + d->s2i[section] = index; + d->i2s[index] = section; + d->clicks.setBit( section, d->clicks_default ); + d->resize.setBit( section, d->resize_default ); + + if ( isUpdatesEnabled() ) { + updateGeometry(); + calculatePositions(); + update(); + } + return index; +} + +void QHeader::resizeArrays( int size ) +{ + d->iconsets.resize( size ); + d->labels.resize( size ); + d->sizes.resize( size ); + d->positions.resize( size ); + d->i2s.resize( size ); + d->s2i.resize( size ); + d->clicks.resize( size ); + d->resize.resize( size ); +} + +void QHeader::setIsATableHeader( bool b ) +{ + d->is_a_table_header = b; +} + +/*! \reimp */ +QSize QHeader::sizeHint() const +{ + int width; + int height; + + constPolish(); + QFontMetrics fm = fontMetrics(); + + if ( d->heightDirty ) { + d->height = fm.lineSpacing() + 6; + for ( int i = 0; i < count(); i++ ) { + int h = orient == Horizontal ? + sectionSizeHint( i, fm ).height() : sectionSizeHint( i, fm ).width(); + d->height = QMAX( d->height, h ); + } + d->heightDirty = FALSE; + } + + if ( orient == Horizontal ) { + height = fm.lineSpacing() + 6; + width = 0; + height = QMAX( height, d->height ); + for ( int i = 0; i < count(); i++ ) + width += d->sizes[i]; + } else { + width = fm.width( ' ' ); + height = 0; + width = QMAX( width, d->height ); + for ( int i = 0; i < count(); i++ ) + height += d->sizes[i]; + } + return (style().sizeFromContents(QStyle::CT_Header, this, + QSize(width, height)).expandedTo(QApplication::globalStrut())); +} + +/*! + \property QHeader::offset + \brief the header's left-most (or top-most) visible pixel + + Setting this property will scroll the header so that \e offset + becomes the left-most (or top-most for vertical headers) visible + pixel. +*/ +int QHeader::offset() const +{ + if ( reverse() ) + return d->lastPos - width() - offs; + return offs; +} + +void QHeader::setOffset( int x ) +{ + int oldOff = offset(); + offs = x; + if( d->lastPos < ( orient == Horizontal ? width() : height() ) ) + offs = 0; + else if ( reverse() ) + offs = d->lastPos - width() - x; + if ( orient == Horizontal ) + scroll( oldOff-offset(), 0 ); + else + scroll( 0, oldOff-offset()); +} + + + +/* + Returns the position of actual division line \a i in widget + coordinates. May return a position outside the widget. + + Note that the last division line is numbered count(). (There is one + more line than the number of sections). +*/ +int QHeader::pPos( int i ) const +{ + int pos; + if ( i == count() ) + pos = d->lastPos; + else + pos = d->positions[i]; + if ( reverse() ) + pos = d->lastPos - pos; + return pos - offset(); +} + + +/* + Returns the size of the section at index position \a i. +*/ +int QHeader::pSize( int i ) const +{ + return d->sizes[ d->i2s[i] ]; +} + +/*! + \obsolete + + Use mapToSection() instead. + + Translates from actual index \a a (index at which the section is displayed) to + logical index of the section. Returns -1 if \a a is outside the legal range. + + \sa mapToActual() +*/ + +int QHeader::mapToLogical( int a ) const +{ + return mapToSection( a ); +} + + +/*! + \obsolete + + Use mapToIndex() instead. + + Translates from logical index \a l to actual index (index at which the section \a l is displayed) . + Returns -1 if \a l is outside the legal range. + + \sa mapToLogical() +*/ + +int QHeader::mapToActual( int l ) const +{ + return mapToIndex( l ); +} + + +/*! + \obsolete + + Use resizeSection() instead. + + Sets the size of the section \a section to \a s pixels. + + \warning does not repaint or send out signals +*/ + +void QHeader::setCellSize( int section, int s ) +{ + if ( section < 0 || section >= count() ) + return; + d->sizes[ section ] = s; + if ( isUpdatesEnabled() ) + calculatePositions(); +} + + +/*! + If \a enable is TRUE the user may resize section \a section; + otherwise the section may not be manually resized. + + If \a section is negative (the default) then the \a enable value + is set for all existing sections and will be applied to any new + sections that are added. + Example: + \code + // Allow resizing of all current and future sections + header->setResizeEnabled(TRUE); + // Disable resizing of section 3, (the fourth section added) + header->setResizeEnabled(FALSE, 3); + \endcode + + If the user resizes a section, a sizeChange() signal is emitted. + + \sa setMovingEnabled() setClickEnabled() setTracking() +*/ + +void QHeader::setResizeEnabled( bool enable, int section ) +{ + if ( section < 0 ) { + d->resize.fill( enable ); + // and future ones... + d->resize_default = enable; + } else if ( section < count() ) { + d->resize[ section ] = enable; + } +} + + +/*! + \property QHeader::moving + \brief whether the header sections can be moved + + If this property is TRUE (the default) the user can move sections. + If the user moves a section the indexChange() signal is emitted. + + \sa setClickEnabled(), setResizeEnabled() +*/ + +void QHeader::setMovingEnabled( bool enable ) +{ + d->move = enable; +} + + +/*! + If \a enable is TRUE, any clicks on section \a section will result + in clicked() signals being emitted; otherwise the section will + ignore clicks. + + If \a section is -1 (the default) then the \a enable value is set + for all existing sections and will be applied to any new sections + that are added. + + \sa setMovingEnabled(), setResizeEnabled() +*/ + +void QHeader::setClickEnabled( bool enable, int section ) +{ + if ( section < 0 ) { + d->clicks.fill( enable ); + // and future ones... + d->clicks_default = enable; + } else if ( section < count() ) { + d->clicks[ section ] = enable; + } +} + + +/*! + Paints the section at position \a index, inside rectangle \a fr + (which uses widget coordinates) using painter \a p. + + Calls paintSectionLabel(). +*/ + +void QHeader::paintSection( QPainter *p, int index, const QRect& fr ) +{ + int section = mapToSection( index ); + + if ( section < 0 ) { + style().drawPrimitive( QStyle::PE_HeaderSection, p, fr, + colorGroup(), QStyle::Style_Raised | + (isEnabled() ? QStyle::Style_Enabled : 0) | + ( orient == Horizontal ? QStyle::Style_Horizontal : 0 ), + QStyleOption( this ) ); + return; + } + + if ( sectionSize( section ) <= 0 ) + return; + + QStyle::SFlags flags = (orient == Horizontal ? QStyle::Style_Horizontal : QStyle::Style_Default); + //pass in some hint about the sort indicator if it is used + if(d->sortSection != section) + flags |= QStyle::Style_Off; + else if(!d->sortDirection) + flags |= QStyle::Style_Up; + if(isEnabled()) + flags |= QStyle::Style_Enabled; + if(isClickEnabled(section)) { + if(section == d->sortSection) + flags |= QStyle::Style_Sunken; //currently selected + if((state == Pressed || state == Moving) && index == handleIdx) + flags |= QStyle::Style_Down; //currently pressed + + } + if(!(flags & QStyle::Style_Down)) + flags |= QStyle::Style_Raised; + p->setBrushOrigin( fr.topLeft() ); + if ( d->clicks[section] ) { + style().drawPrimitive( QStyle::PE_HeaderSection, p, fr, + colorGroup(), flags, + QStyleOption( this ) ); + } else { + p->save(); + p->setClipRect( fr ); // hack to keep styles working + if ( orientation() == Horizontal ) { + style().drawPrimitive( QStyle::PE_HeaderSection, p, + QRect(fr.x() - 2, fr.y() - 2, fr.width() + 4, fr.height() + 4), + colorGroup(), flags, + QStyleOption( this ) ); + + p->setPen( colorGroup().color( QColorGroup::Mid ) ); + p->drawLine( fr.x(), fr.y() + fr.height() - 1, + fr.x() + fr.width() - 1, fr.y() + fr.height() - 1 ); + p->drawLine( fr.x() + fr.width() - 1, fr.y(), + fr.x() + fr.width() - 1, fr.y() + fr.height() - 1 ); + p->setPen( colorGroup().color( QColorGroup::Light ) ); + if ( index > 0 ) + p->drawLine( fr.x(), fr.y(), fr.x(), fr.y() + fr.height() - 1 ); + if ( index == count() - 1 ) { + p->drawLine( fr.x() + fr.width() - 1, fr.y(), + fr.x() + fr.width() - 1, fr.y() + fr.height() - 1 ); + p->setPen( colorGroup().color( QColorGroup::Mid ) ); + p->drawLine( fr.x() + fr.width() - 2, fr.y(), + fr.x() + fr.width() - 2, fr.y() + fr.height() - 1 ); + } + } else { + style().drawPrimitive( QStyle::PE_HeaderSection, p, + QRect(fr.x() - 2, fr.y() - 2, fr.width() + 4, fr.height() + 4), + colorGroup(), flags, + QStyleOption( this ) ); + + p->setPen( colorGroup().color( QColorGroup::Mid ) ); + p->drawLine( fr.x() + width() - 1, fr.y(), + fr.x() + fr.width() - 1, fr.y() + fr.height() - 1 ); + p->drawLine( fr.x(), fr.y() + fr.height() - 1, + fr.x() + fr.width() - 1, fr.y() + fr.height() - 1 ); + p->setPen( colorGroup().color( QColorGroup::Light ) ); + if ( index > 0 ) + p->drawLine( fr.x(), fr.y(), fr.x() + fr.width() - 1, fr.y() ); + if ( index == count() - 1 ) { + p->drawLine( fr.x(), fr.y() + fr.height() - 1, + fr.x() + fr.width() - 1, fr.y() + fr.height() - 1 ); + p->setPen( colorGroup().color( QColorGroup::Mid ) ); + p->drawLine( fr.x(), fr.y() + fr.height() - 2, + fr.x() + fr.width() - 1, fr.y() + fr.height() - 2 ); + } + } + p->restore(); + } + + paintSectionLabel( p, index, fr ); +} + +/*! + Paints the label of the section at position \a index, inside + rectangle \a fr (which uses widget coordinates) using painter \a + p. + + Called by paintSection() +*/ +void QHeader::paintSectionLabel( QPainter *p, int index, const QRect& fr ) +{ + int section = mapToSection( index ); + if ( section < 0 ) + return; + + int dx = 0, dy = 0; + QStyle::SFlags flags = QStyle::Style_Default; + if ( index == handleIdx && ( state == Pressed || state == Moving ) ) { + dx = style().pixelMetric( QStyle::PM_ButtonShiftHorizontal, this ); + dy = style().pixelMetric( QStyle::PM_ButtonShiftVertical, this ); + flags |= QStyle::Style_Sunken; + } + if ( isEnabled() ) + flags |= QStyle::Style_Enabled; + + + QRect r( fr.x() + style().pixelMetric( QStyle::PM_HeaderMargin ) + dx, fr.y() + 2 + dy, + fr.width() - 6, fr.height() - 4 ); + + style().drawControl( QStyle::CE_HeaderLabel, p, this, r, colorGroup(), flags, + QStyleOption( section ) ); + + int arrowWidth = ( orient == Qt::Horizontal ? height() : width() ) / 2; + int arrowHeight = fr.height() - 6; + QSize ssh = sectionSizeHint( section, p->fontMetrics() ); + int tw = ( orient == Qt::Horizontal ? ssh.width() : ssh.height() ); + int ew = 0; + + if ( style().styleHint( QStyle::SH_Header_ArrowAlignment, this ) & AlignRight ) + ew = fr.width() - tw - 8; + if ( d->sortSection == section && tw <= fr.width() ) { + if ( reverse() ) { + tw = fr.width() - tw; + ew = fr.width() - ew - tw; + } + QStyle::SFlags flags = QStyle::Style_Default; + if ( isEnabled() ) + flags |= QStyle::Style_Enabled; + if ( d->sortDirection ) + flags |= QStyle::Style_Down; + else + flags |= QStyle::Style_Up; + QRect ar(fr.x() + tw - arrowWidth - 6 + ew, 4, arrowWidth, arrowHeight); + if (label(section).isRightToLeft()) + ar.moveBy( 2*(fr.right() - ar.right()) + ar.width() - fr.width(), 0 ); + style().drawPrimitive( QStyle::PE_HeaderArrow, p, + ar, colorGroup(), flags, QStyleOption( this ) ); + } +} + + +/*! \reimp */ +void QHeader::paintEvent( QPaintEvent *e ) +{ + QPainter p( this ); + p.setPen( colorGroup().buttonText() ); + int pos = orient == Horizontal ? e->rect().left() : e->rect().top(); + int id = mapToIndex( sectionAt( pos + offset() ) ); + if ( id < 0 ) { + if ( pos > 0 ) + id = d->count; + else if ( reverse() ) + id = d->count - 1; + else + id = 0; + } + if ( reverse() ) { + for ( int i = id; i >= 0; i-- ) { + QRect r = sRect( i ); + paintSection( &p, i, r ); + if ( r.right() >= e->rect().right() ) + return; + } + } else { + if ( count() > 0 ) { + for ( int i = id; i <= count(); i++ ) { + QRect r = sRect( i ); + /* + If the last section is clickable (and thus is + painted raised), draw the virtual section count() + as well. Otherwise it looks ugly. + */ + if ( i < count() || d->clicks[ mapToSection( count() - 1 ) ] ) + paintSection( &p, i, r ); + if ( hasFocus() && d->focusIdx == i ) { + QRect fr( r.x()+2, r.y()+2, r.width()-4, r.height()-4 ); + style().drawPrimitive( QStyle::PE_FocusRect, &p, fr, + colorGroup() ); + } + if ( orient == Horizontal && r. right() >= e->rect().right() || + orient == Vertical && r. bottom() >= e->rect().bottom() ) + return; + } + } + } +} + +/*! \overload + \obsolete + Use the other overload instead. +*/ + +void QHeader::setSortIndicator( int section, bool ascending ) +{ + d->sortSection = section; + if ( section != -1 ) + oldHandleIdx = section; + d->sortDirection = ascending; + update(); + updateGeometry(); +} + +/*! + \fn void QHeader::setSortIndicator(int section, SortOrder order) + + Sets a sort indicator onto the specified \a section. The indicator's + \a order is either Ascending or Descending. + + Only one section can show a sort indicator at any one time. If you + don't want any section to show a sort indicator pass a \a section + number of -1. + + \sa sortIndicatorSection(), sortIndicatorOrder() +*/ + +/*! + Returns the section showing the sort indicator or -1 if there is no sort indicator. + + \sa setSortIndicator(), sortIndicatorOrder() +*/ + +int QHeader::sortIndicatorSection() const +{ + return d->sortSection; +} + +/*! + Returns the implied sort order of the QHeaders sort indicator. + + \sa setSortIndicator(), sortIndicatorSection() +*/ + +Qt::SortOrder QHeader::sortIndicatorOrder() const +{ + return d->sortDirection ? Ascending : Descending; +} + +/*! + Resizes section \a section to \a s pixels wide (or high). +*/ + +void QHeader::resizeSection( int section, int s ) +{ + setCellSize( section, s ); + update(); +} + +/*! + Returns the width (or height) of the \a section in pixels. +*/ + +int QHeader::sectionSize( int section ) const +{ + if ( section < 0 || section >= count() ) + return 0; + return d->sizes[section]; +} + +/*! + Returns the position (in pixels) at which the \a section starts. + + \sa offset() +*/ + +int QHeader::sectionPos( int section ) const +{ + if ( d->positionsDirty ) + ((QHeader *)this)->calculatePositions(); + if ( section < 0 || section >= count() ) + return 0; + return d->positions[ d->s2i[section] ]; +} + +/*! + Returns the index of the section which contains the position \a + pos given in pixels from the left (or top). + + \sa offset() +*/ + +int QHeader::sectionAt( int pos ) const +{ + if ( reverse() ) + pos = d->lastPos - pos; + return d->sectionAt( pos ); +} + +/*! + Returns the number of the section that corresponds to the specified \a index. + + \warning If QTable is used to move header sections as a result of user + interaction, the mapping exposed by this function will not reflect the + order of the headers in the table; i.e., QTable does not call moveSection() + to move sections but handles move operations internally. + + \sa mapToIndex() +*/ + +int QHeader::mapToSection( int index ) const +{ + return ( index >= 0 && index < count() ) ? d->i2s[ index ] : -1; +} + +/*! + Returns the index position corresponding to the specified \a section number. + + \warning If QTable is used to move header sections as a result of user + interaction, the mapping exposed by this function will not reflect the + order of the headers in the table; i.e., QTable does not call moveSection() + to move sections but handles move operations internally. + + \sa mapToSection() +*/ + +int QHeader::mapToIndex( int section ) const +{ + return ( section >= 0 && section < count() ) ? d->s2i[ section ] : -1; +} + +/*! + Moves section \a section to index position \a toIndex. +*/ + +void QHeader::moveSection( int section, int toIndex ) +{ + int fromIndex = mapToIndex( section ); + if ( fromIndex == toIndex || + fromIndex < 0 || fromIndex > count() || + toIndex < 0 || toIndex > count() ) + return; + int i; + int idx = d->i2s[fromIndex]; + if ( fromIndex < toIndex ) { + for ( i = fromIndex; i < toIndex - 1; i++ ) { + int t; + d->i2s[i] = t = d->i2s[i+1]; + d->s2i[t] = i; + } + d->i2s[toIndex-1] = idx; + d->s2i[idx] = toIndex-1; + } else { + for ( i = fromIndex; i > toIndex; i-- ) { + int t; + d->i2s[i] = t = d->i2s[i-1]; + d->s2i[t] = i; + } + d->i2s[toIndex] = idx; + d->s2i[idx] = toIndex; + } + calculatePositions(); +} + +/*! + Returns TRUE if section \a section is clickable; otherwise returns + FALSE. + + If \a section is out of range (negative or larger than count() - + 1): returns TRUE if all sections are clickable; otherwise returns + FALSE. + + \sa setClickEnabled() +*/ + +bool QHeader::isClickEnabled( int section ) const +{ + if ( section >= 0 && section < count() ) { + return (bool)d->clicks[ section ]; + } + + for ( int i = 0; i < count(); ++i ) { + if ( !d->clicks[ i ] ) + return FALSE; + } + return TRUE; +} + +/*! + Returns TRUE if section \a section is resizeable; otherwise + returns FALSE. + + If \a section is -1 then this function applies to all sections, + i.e. returns TRUE if all sections are resizeable; otherwise + returns FALSE. + + \sa setResizeEnabled() +*/ + +bool QHeader::isResizeEnabled( int section ) const +{ + if ( section >= 0 && section < count() ) { + return (bool)d->resize[ section ]; + } + + for ( int i = 0; i < count();++i ) { + if ( !d->resize[ i ] ) + return FALSE; + } + return TRUE; +} + +bool QHeader::isMovingEnabled() const +{ + return d->move; +} + +/*! \reimp */ + +void QHeader::setUpdatesEnabled( bool enable ) +{ + if ( enable ) + calculatePositions(); + QWidget::setUpdatesEnabled( enable ); +} + + +bool QHeader::reverse () const +{ +#if 0 + return ( orient == Qt::Horizontal && QApplication::reverseLayout() ); +#else + return FALSE; +#endif +} + +/*! \reimp */ +void QHeader::resizeEvent( QResizeEvent *e ) +{ + if ( e ) + QWidget::resizeEvent( e ); + + if( d->lastPos < width() ) { + offs = 0; + } + + if ( e ) { + adjustHeaderSize( orientation() == Horizontal ? + width() - e->oldSize().width() : height() - e->oldSize().height() ); + if ( (orientation() == Horizontal && height() != e->oldSize().height()) + || (orientation() == Vertical && width() != e->oldSize().width()) ) + update(); + } else + adjustHeaderSize(); +} + +/*! + \fn void QHeader::adjustHeaderSize() + + Adjusts the size of the sections to fit the size of the header as + completely as possible. Only sections for which isStretchEnabled() + is TRUE will be resized. +*/ + +void QHeader::adjustHeaderSize( int diff ) +{ + if ( !count() ) + return; + + // we skip the adjustHeaderSize when trying to resize the last column which is set to stretchable + if ( d->fullSize == (count() -1) && + (d->lastPos - d->sizes[count() -1]) > ( orient == Horizontal ? width() : height() ) ) + return; + + if ( d->fullSize >= 0 ) { + int sec = mapToSection( d->fullSize ); + int lsec = mapToSection( count() - 1 ); + int ns = sectionSize( sec ) + + ( orientation() == Horizontal ? + width() : height() ) - ( sectionPos( lsec ) + sectionSize( lsec ) ); + int os = sectionSize( sec ); + if ( ns < 20 ) + ns = 20; + setCellSize( sec, ns ); + repaint( FALSE ); + emit sizeChange( sec, os, ns ); + } else if ( d->fullSize == -1 ) { + int df = diff / count(); + int part = orientation() == Horizontal ? width() / count() : height() / count(); + for ( int i = 0; i < count() - 1; ++i ) { + int sec = mapToIndex( i ); + int os = sectionSize( sec ); + int ns = diff != -1 ? os + df : part; + if ( ns < 20 ) + ns = 20; + setCellSize( sec, ns ); + emit sizeChange( sec, os, ns ); + } + int sec = mapToIndex( count() - 1 ); + int ns = ( orientation() == Horizontal ? width() : height() ) - sectionPos( sec ); + int os = sectionSize( sec ); + if ( ns < 20 ) + ns = 20; + setCellSize( sec, ns ); + repaint( FALSE ); + emit sizeChange( sec, os, ns ); + } +} + +/*! + Returns the total width of all the header columns. +*/ +int QHeader::headerWidth() const +{ + if ( d->pos_dirty ) { + ( (QHeader*)this )->calculatePositions(); + d->pos_dirty = FALSE; + } + return d->lastPos; +} + +void QHeader::calculatePositions( bool onlyVisible, int start ) +{ + d->positionsDirty = FALSE; + d->lastPos = count() > 0 ? d->positions[start] : 0; + for ( int i = start; i < count(); i++ ) { + d->positions[i] = d->lastPos; + d->lastPos += d->sizes[d->i2s[i]]; + if ( onlyVisible && d->lastPos > offset() + + ( orientation() == Horizontal ? width() : height() ) ) + break; + } + d->pos_dirty = onlyVisible; +} + + +/*! + \property QHeader::stretching + \brief whether the header sections always take up the full width + (or height) of the header +*/ + + +/*! + If \a b is TRUE, section \a section will be resized when the + header is resized, so that the sections take up the full width (or + height for vertical headers) of the header; otherwise section \a + section will be set to be unstretchable and will not resize when + the header is resized. + + If \a section is -1, and if \a b is TRUE, then all sections will + be resized equally when the header is resized so that they take up + the full width (or height for vertical headers) of the header; + otherwise all the sections will be set to be unstretchable and + will not resize when the header is resized. + + \sa adjustHeaderSize() +*/ + +void QHeader::setStretchEnabled( bool b, int section ) +{ + if ( b ) + d->fullSize = section; + else + d->fullSize = -2; + adjustHeaderSize(); +} + +bool QHeader::isStretchEnabled() const +{ + return d->fullSize == -1; +} + +/*! + \overload + + Returns TRUE if section \a section will resize to take up the full + width (or height) of the header; otherwise returns FALSE. If at + least one section has stretch enabled the sections will always + take up the full width of the header. + + \sa setStretchEnabled() +*/ + +bool QHeader::isStretchEnabled( int section ) const +{ + return d->fullSize == section; +} + +/*! + \reimp +*/ +void QHeader::fontChange( const QFont &oldFont ) +{ + QFontMetrics fm = fontMetrics(); + d->height = ( orient == Horizontal ) ? fm.lineSpacing() + 6 : fm.width( ' ' ); + QWidget::fontChange( oldFont ); +} + +#endif // QT_NO_HEADER diff --git a/src/widgets/qheader.h b/src/widgets/qheader.h new file mode 100644 index 0000000..1f5cf03 --- /dev/null +++ b/src/widgets/qheader.h @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Definition of QHeader widget class (table header) +** +** Created : 961105 +** +** 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. +** +**********************************************************************/ + +#ifndef QHEADER_H +#define QHEADER_H + +#ifndef QT_H +#include "qwidget.h" +#include "qstring.h" +#include "qiconset.h" // conversion QPixmap->QIconset +#endif // QT_H + +#ifndef QT_NO_HEADER + +class QShowEvent; +class QHeaderData; +class QTable; + +class Q_EXPORT QHeader : public QWidget +{ + friend class QTable; + friend class QTableHeader; + friend class QListView; + + Q_OBJECT + Q_PROPERTY( Orientation orientation READ orientation WRITE setOrientation ) + Q_PROPERTY( bool tracking READ tracking WRITE setTracking ) + Q_PROPERTY( int count READ count ) + Q_PROPERTY( int offset READ offset WRITE setOffset ) + Q_PROPERTY( bool moving READ isMovingEnabled WRITE setMovingEnabled ) + Q_PROPERTY( bool stretching READ isStretchEnabled WRITE setStretchEnabled ) + +public: + QHeader( QWidget* parent=0, const char* name=0 ); + QHeader( int, QWidget* parent=0, const char* name=0 ); + ~QHeader(); + + int addLabel( const QString &, int size = -1 ); + int addLabel( const QIconSet&, const QString &, int size = -1 ); + void removeLabel( int section ); + virtual void setLabel( int, const QString &, int size = -1 ); + virtual void setLabel( int, const QIconSet&, const QString &, int size = -1 ); + QString label( int section ) const; + QIconSet* iconSet( int section ) const; + + virtual void setOrientation( Orientation ); + Orientation orientation() const; + virtual void setTracking( bool enable ); + bool tracking() const; + + virtual void setClickEnabled( bool, int section = -1 ); + virtual void setResizeEnabled( bool, int section = -1 ); + virtual void setMovingEnabled( bool ); + virtual void setStretchEnabled( bool b, int section ); + void setStretchEnabled( bool b ) { setStretchEnabled( b, -1 ); } + bool isClickEnabled( int section = -1 ) const; + bool isResizeEnabled( int section = -1 ) const; + bool isMovingEnabled() const; + bool isStretchEnabled() const; + bool isStretchEnabled( int section ) const; + + void resizeSection( int section, int s ); + int sectionSize( int section ) const; + int sectionPos( int section ) const; + int sectionAt( int pos ) const; + int count() const; + int headerWidth() const; + QRect sectionRect( int section ) const; + + virtual void setCellSize( int , int ); // obsolete, do not use + int cellSize( int i ) const { return sectionSize( mapToSection(i) ); } // obsolete, do not use + int cellPos( int ) const; // obsolete, do not use + int cellAt( int pos ) const { return mapToIndex( sectionAt(pos + offset()) ); } // obsolete, do not use + + int offset() const; + + QSize sizeHint() const; + + int mapToSection( int index ) const; + int mapToIndex( int section ) const; + int mapToLogical( int ) const; // obsolete, do not use + int mapToActual( int ) const; // obsolete, do not use + + void moveSection( int section, int toIndex ); + virtual void moveCell( int, int); // obsolete, do not use + + void setSortIndicator( int section, bool ascending = TRUE ); // obsolete, do not use + inline void setSortIndicator( int section, SortOrder order ) + { setSortIndicator( section, (order == Ascending) ); } + int sortIndicatorSection() const; + SortOrder sortIndicatorOrder() const; + + void adjustHeaderSize() { adjustHeaderSize( -1 ); } + +public slots: + void setUpdatesEnabled( bool enable ); + virtual void setOffset( int pos ); + +signals: + void clicked( int section ); + void pressed( int section ); + void released( int section ); + void sizeChange( int section, int oldSize, int newSize ); + void indexChange( int section, int fromIndex, int toIndex ); + void sectionClicked( int ); // obsolete, do not use + void moved( int, int ); // obsolete, do not use + void sectionHandleDoubleClicked( int section ); + +protected: + void paintEvent( QPaintEvent * ); + void showEvent( QShowEvent *e ); + void resizeEvent( QResizeEvent *e ); + QRect sRect( int index ); + + virtual void paintSection( QPainter *p, int index, const QRect& fr); + virtual void paintSectionLabel( QPainter* p, int index, const QRect& fr ); + + void fontChange( const QFont & ); + + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void mouseDoubleClickEvent( QMouseEvent * ); + + void keyPressEvent( QKeyEvent * ); + void keyReleaseEvent( QKeyEvent * ); + +private: + void handleColumnMove( int fromIdx, int toIdx ); + void adjustHeaderSize( int diff ); + void init( int ); + + void paintRect( int p, int s ); + void markLine( int idx ); + void unMarkLine( int idx ); + int pPos( int i ) const; + int pSize( int i ) const; + int findLine( int ); + int handleAt( int p ); + bool reverse() const; + void calculatePositions( bool onlyVisible = FALSE, int start = 0 ); + void handleColumnResize(int, int, bool, bool = TRUE ); + QSize sectionSizeHint( int section, const QFontMetrics& fm ) const; + void setSectionSizeAndHeight( int section, int size ); + + void resizeArrays( int size ); + void setIsATableHeader( bool b ); + int offs; + int handleIdx; + int oldHIdxSize; + int moveToIdx; + enum State { Idle, Sliding, Pressed, Moving, Blocked }; + State state; + QCOORD clickPos; + bool trackingIsOn; + int oldHandleIdx; + int cachedPos; // not used + Orientation orient; + + QHeaderData *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QHeader( const QHeader & ); + QHeader &operator=( const QHeader & ); +#endif +}; + + +inline QHeader::Orientation QHeader::orientation() const +{ + return orient; +} + +inline void QHeader::setTracking( bool enable ) { trackingIsOn = enable; } +inline bool QHeader::tracking() const { return trackingIsOn; } + +extern Q_EXPORT bool qt_qheader_label_return_null_strings; // needed for professional edition + +#endif // QT_NO_HEADER + +#endif // QHEADER_H diff --git a/src/widgets/qhgroupbox.cpp b/src/widgets/qhgroupbox.cpp new file mode 100644 index 0000000..5cc4555 --- /dev/null +++ b/src/widgets/qhgroupbox.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Implementation of QHGroupBox class +** +** Created : 990602 +** +** 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 "qhgroupbox.h" +#ifndef QT_NO_HGROUPBOX + +/*! + \class QHGroupBox qhgroupbox.h + + \brief The QHGroupBox widget organizes widgets in a group with one + horizontal row. + + \ingroup organizers + \ingroup geomanagement + \ingroup appearance + + QHGroupBox is a convenience class that offers a thin layer on top + of QGroupBox. Think of it as a QHBox that offers a frame with a + title. + + \img qgroupboxes.png Group Boxes + + \sa QVGroupBox +*/ + +/*! + Constructs a horizontal group box with no title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ +QHGroupBox::QHGroupBox( QWidget *parent, const char *name ) + : QGroupBox( 1, Vertical /* sic! */, parent, name ) +{ +} + +/*! + Constructs a horizontal group box with the title \a title. + + The \a parent and \a name arguments are passed to the QWidget + constructor. +*/ + +QHGroupBox::QHGroupBox( const QString &title, QWidget *parent, + const char *name ) + : QGroupBox( 1, Vertical /* sic! */, title, parent, name ) +{ +} + +/*! + Destroys the horizontal group box, deleting its child widgets. +*/ +QHGroupBox::~QHGroupBox() +{ +} +#endif diff --git a/src/widgets/qhgroupbox.h b/src/widgets/qhgroupbox.h new file mode 100644 index 0000000..66b4809 --- /dev/null +++ b/src/widgets/qhgroupbox.h @@ -0,0 +1,67 @@ +/********************************************************************** +** +** Definition of QHGroupBox widget class +** +** Created : 990602 +** +** Copyright (C) 1999-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. +** +**********************************************************************/ + +#ifndef QHGROUPBOX_H +#define QHGROUPBOX_H + +#ifndef QT_H +#include "qgroupbox.h" +#endif // QT_H + +#ifndef QT_NO_HGROUPBOX + +class Q_EXPORT QHGroupBox : public QGroupBox +{ + Q_OBJECT +public: + QHGroupBox( QWidget* parent=0, const char* name=0 ); + QHGroupBox( const QString &title, QWidget* parent=0, const char* name=0 ); + ~QHGroupBox(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QHGroupBox( const QHGroupBox & ); + QHGroupBox &operator=( const QHGroupBox & ); +#endif +}; + +#endif // QT_NO_HGROUPBOX + +#endif // QHGROUPBOX_H diff --git a/src/widgets/qlabel.cpp b/src/widgets/qlabel.cpp new file mode 100644 index 0000000..40f2b38 --- /dev/null +++ b/src/widgets/qlabel.cpp @@ -0,0 +1,1190 @@ +/********************************************************************** +** +** Implementation of QLabel widget class +** +** Created : 941215 +** +** 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 "qlabel.h" +#ifndef QT_NO_LABEL +#include "qpainter.h" +#include "qdrawutil.h" +#include "qaccel.h" +#include "qmovie.h" +#include "qimage.h" +#include "qbitmap.h" +#include "qpicture.h" +#include "qapplication.h" +#include "qsimplerichtext.h" +#include "qstylesheet.h" +#include "qstyle.h" + +class QLabelPrivate +{ +public: + QLabelPrivate() + :img(0), pix(0), valid_hints( -1 ) + {} + QImage* img; // for scaled contents + QPixmap* pix; // for scaled contents + QSize sh; + QSize msh; + int valid_hints; // stores the frameWidth() for the stored size hint, -1 otherwise +}; + + +/*! + \class QLabel qlabel.h + \brief The QLabel widget provides a text or image display. + + \ingroup basic + \ingroup text + \mainclass + + QLabel is used for displaying text or an image. No user + interaction functionality is provided. The visual appearance of + the label can be configured in various ways, and it can be used + for specifying a focus accelerator key for another widget. + + A QLabel can contain any of the following content types: + \table + \header \i Content \i Setting + \row \i Plain text + \i Pass a QString to setText(). + \row \i Rich text + \i Pass a QString that contains rich text to setText(). + \row \i A pixmap + \i Pass a QPixmap to setPixmap(). + \row \i A movie + \i Pass a QMovie to setMovie(). + \row \i A number + \i Pass an \e int or a \e double to setNum(), which converts + the number to plain text. + \row \i Nothing + \i The same as an empty plain text. This is the default. Set + by clear(). + \endtable + + When the content is changed using any of these functions, any + previous content is cleared. + + The look of a QLabel can be tuned in several ways. All the + settings of QFrame are available for specifying a widget frame. + The positioning of the content within the QLabel widget area can + be tuned with setAlignment() and setIndent(). For example, this + code sets up a sunken panel with a two-line text in the bottom + right corner (both lines being flush with the right side of the + label): + \code + QLabel *label = new QLabel( this ); + label->setFrameStyle( QFrame::Panel | QFrame::Sunken ); + label->setText( "first line\nsecond line" ); + label->setAlignment( AlignBottom | AlignRight ); + \endcode + + A QLabel is often used as a label for an interactive widget. For + this use QLabel provides a useful mechanism for adding an + accelerator key (see QAccel) that will set the keyboard focus to + the other widget (called the QLabel's "buddy"). For example: + \code + QLineEdit* phoneEdit = new QLineEdit( this, "phoneEdit" ); + QLabel* phoneLabel = new QLabel( phoneEdit, "&Phone:", this, "phoneLabel" ); + \endcode + + In this example, keyboard focus is transferred to the label's + buddy (the QLineEdit) when the user presses Alt+P. You can + also use the setBuddy() function to accomplish the same thing. + + <img src=qlabel-m.png> <img src=qlabel-w.png> + + \sa QLineEdit, QTextEdit, QPixmap, QMovie, + \link guibooks.html#fowler GUI Design Handbook: Label\endlink +*/ + +/*! + \fn QPicture * QLabel::picture() const + + Returns the label's picture or 0 if the label doesn't have a + picture. +*/ + + +/*! + Constructs an empty label. + + The \a parent, \a name and widget flag \a f, arguments are passed + to the QFrame constructor. + + \sa setAlignment(), setFrameStyle(), setIndent() +*/ + +QLabel::QLabel( QWidget *parent, const char *name, WFlags f ) + : QFrame( parent, name, f | WMouseNoMask ) +{ + init(); +} + + +/*! + Constructs a label that displays the text, \a text. + + The \a parent, \a name and widget flag \a f, arguments are passed + to the QFrame constructor. + + \sa setText(), setAlignment(), setFrameStyle(), setIndent() +*/ + +QLabel::QLabel( const QString &text, QWidget *parent, const char *name, + WFlags f ) + : QFrame( parent, name, f | WMouseNoMask ) +{ + init(); + setText( text ); +} + + +/*! + Constructs a label that displays the text \a text. The label has a + buddy widget, \a buddy. + + If the \a text contains an underlined letter (a letter preceded by + an ampersand, \&), and the text is in plain text format, when the + user presses Alt+ the underlined letter, focus is passed to the + buddy widget. + + The \a parent, \a name and widget flag, \a f, arguments are passed + to the QFrame constructor. + + \sa setText(), setBuddy(), setAlignment(), setFrameStyle(), + setIndent() +*/ +QLabel::QLabel( QWidget *buddy, const QString &text, + QWidget *parent, const char *name, WFlags f ) + : QFrame( parent, name, f | WMouseNoMask ) +{ + init(); +#ifndef QT_NO_ACCEL + setBuddy( buddy ); +#endif + setText( text ); +} + +/*! + Destroys the label. +*/ + +QLabel::~QLabel() +{ + clearContents(); + delete d; +} + + +void QLabel::init() +{ + lpixmap = 0; +#ifndef QT_NO_MOVIE + lmovie = 0; +#endif +#ifndef QT_NO_ACCEL + lbuddy = 0; + accel = 0; +#endif + lpixmap = 0; +#ifndef QT_NO_PICTURE + lpicture = 0; +#endif + align = AlignAuto | AlignVCenter | ExpandTabs; + extraMargin = -1; + autoresize = FALSE; + scaledcontents = FALSE; + textformat = Qt::AutoText; +#ifndef QT_NO_RICHTEXT + doc = 0; +#endif + + setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Preferred ) ); + d = new QLabelPrivate; +} + + +/*! + \property QLabel::text + \brief the label's text + + If no text has been set this will return an empty string. Setting + the text clears any previous content, unless they are the same. + + The text will be interpreted either as a plain text or as a rich + text, depending on the text format setting; see setTextFormat(). + The default setting is \c AutoText, i.e. QLabel will try to + auto-detect the format of the text set. + + If the text is interpreted as a plain text and a buddy has been + set, the buddy accelerator key is updated from the new text. + + The label resizes itself if auto-resizing is enabled. + + Note that Qlabel is well-suited to display small rich text + documents, i.e. those small documents that get their document + specific settings (font, text color, link color) from the label's + palette and font properties. For large documents, use QTextEdit + in read-only mode instead. QTextEdit will flicker less on resize + and can also provide a scrollbar when necessary. + + \sa text, setTextFormat(), setBuddy(), alignment +*/ + +void QLabel::setText( const QString &text ) +{ + if ( ltext == text ) + return; + QSize osh = sizeHint(); +#ifndef QT_NO_RICHTEXT + bool hadRichtext = doc != 0; +#endif + clearContents(); + ltext = text; +#ifndef QT_NO_RICHTEXT + bool useRichText = (textformat == RichText || + ( ( textformat == AutoText ) && QStyleSheet::mightBeRichText(ltext) ) ); +#else + bool useRichText = TRUE; +#endif +#ifndef QT_NO_ACCEL + // ### Setting accelerators for rich text labels will not work. + // Eg. <b>>Hello</b> will return ALT+G which is clearly + // not intended. + if ( !useRichText ) { + int p = QAccel::shortcutKey( ltext ); + if ( p ) { + if ( !accel ) + accel = new QAccel( this, "accel label accel" ); + accel->connectItem( accel->insertItem( p ), + this, SLOT(acceleratorSlot()) ); + } + } +#endif +#ifndef QT_NO_RICHTEXT + if ( useRichText ) { + if ( !hadRichtext ) + align |= WordBreak; + QString t = ltext; + if ( align & AlignRight ) + t.prepend( "<div align=\"right\">"); + else if ( align & AlignHCenter ) + t.prepend( "<div align=\"center\">"); + if ( (align & WordBreak) == 0 ) + t.prepend( "<nobr>" ); + doc = new QSimpleRichText( t, font() ); + } +#endif + + updateLabel( osh ); +} + + +/*! + Clears any label contents. Equivalent to setText( "" ). +*/ + +void QLabel::clear() +{ + setText( QString::fromLatin1("") ); +} + +/*! + \property QLabel::pixmap + \brief the label's pixmap + + If no pixmap has been set this will return an invalid pixmap. + + Setting the pixmap clears any previous content, and resizes the + label if \l QLabel::autoResize() is TRUE. The buddy accelerator, + if any, is disabled. +*/ +void QLabel::setPixmap( const QPixmap &pixmap ) +{ + QSize osh = sizeHint(); + + if ( !lpixmap || lpixmap->serialNumber() != pixmap.serialNumber() ) { + clearContents(); + lpixmap = new QPixmap( pixmap ); + } + + if ( lpixmap->depth() == 1 && !lpixmap->mask() ) + lpixmap->setMask( *((QBitmap *)lpixmap) ); + + updateLabel( osh ); +} + +#ifndef QT_NO_PICTURE +/*! + Sets the label contents to \a picture. Any previous content is + cleared. + + The buddy accelerator, if any, is disabled. + + \sa picture(), setBuddy() +*/ + +void QLabel::setPicture( const QPicture &picture ) +{ + QSize osh = sizeHint(); + clearContents(); + lpicture = new QPicture( picture ); + + updateLabel( osh ); +} +#endif // QT_NO_PICTURE + +/*! + Sets the label contents to plain text containing the textual + representation of integer \a num. Any previous content is cleared. + Does nothing if the integer's string representation is the same as + the current contents of the label. + + The buddy accelerator, if any, is disabled. + + The label resizes itself if auto-resizing is enabled. + + \sa setText(), QString::setNum(), setBuddy() +*/ + +void QLabel::setNum( int num ) +{ + QString str; + str.setNum( num ); + setText( str ); +} + +/*! + \overload + + Sets the label contents to plain text containing the textual + representation of double \a num. Any previous content is cleared. + Does nothing if the double's string representation is the same as + the current contents of the label. + + The buddy accelerator, if any, is disabled. + + The label resizes itself if auto-resizing is enabled. + + \sa setText(), QString::setNum(), setBuddy() +*/ + +void QLabel::setNum( double num ) +{ + QString str; + str.setNum( num ); + setText( str ); +} + +/*! + \property QLabel::alignment + \brief the alignment of the label's contents + + The alignment is a bitwise OR of \c Qt::AlignmentFlags and \c + Qt::TextFlags values. The \c ExpandTabs, \c SingleLine and \c + ShowPrefix flags apply only if the label contains plain text; + otherwise they are ignored. The \c DontClip flag is always + ignored. \c WordBreak applies to both rich text and plain text + labels. The \c BreakAnywhere flag is not supported in QLabel. + + If the label has a buddy, the \c ShowPrefix flag is forced to + TRUE. + + The default alignment is \c{AlignAuto | AlignVCenter | ExpandTabs} + if the label doesn't have a buddy and \c{AlignAuto | AlignVCenter + | ExpandTabs | ShowPrefix} if the label has a buddy. If the label + contains rich text, additionally \c WordBreak is turned on. + + \sa Qt::AlignmentFlags, alignment, setBuddy(), text +*/ + +void QLabel::setAlignment( int alignment ) +{ + if ( alignment == align ) + return; + QSize osh = sizeHint(); +#ifndef QT_NO_ACCEL + if ( lbuddy ) + align = alignment | ShowPrefix; + else +#endif + align = alignment; + +#ifndef QT_NO_RICHTEXT + QString t = ltext; + if ( !t.isNull() ) { + ltext = QString::null; + setText( t ); + } +#endif + + updateLabel( osh ); +} + + +/*! + \property QLabel::indent + \brief the label's text indent in pixels + + If a label displays text, the indent applies to the left edge if + alignment() is \c AlignLeft, to the right edge if alignment() is + \c AlignRight, to the top edge if alignment() is \c AlignTop, and + to to the bottom edge if alignment() is \c AlignBottom. + + If indent is negative, or if no indent has been set, the label + computes the effective indent as follows: If frameWidth() is 0, + the effective indent becomes 0. If frameWidth() is greater than 0, + the effective indent becomes half the width of the "x" character + of the widget's current font(). + + \sa alignment, frameWidth(), font() +*/ + +void QLabel::setIndent( int indent ) +{ + extraMargin = indent; + updateLabel( QSize( -1, -1 ) ); +} + + +/*! + \fn bool QLabel::autoResize() const + + \obsolete + + Returns TRUE if auto-resizing is enabled, or FALSE if auto-resizing + is disabled. + + Auto-resizing is disabled by default. + + \sa setAutoResize() +*/ + +/*! \obsolete + Enables auto-resizing if \a enable is TRUE, or disables it if \a + enable is FALSE. + + When auto-resizing is enabled the label will resize itself to fit + the contents whenever the contents change. The top-left corner is + not moved. This is useful for QLabel widgets that are not managed by + a QLayout (e.g., top-level widgets). + + Auto-resizing is disabled by default. + + \sa autoResize(), adjustSize(), sizeHint() +*/ + +void QLabel::setAutoResize( bool enable ) +{ + if ( (bool)autoresize != enable ) { + autoresize = enable; + if ( autoresize ) + adjustSize(); // calls resize which repaints + } +} + + + +/*! + Returns the size that will be used if the width of the label is \a + w. If \a w is -1, the sizeHint() is returned. +*/ + +QSize QLabel::sizeForWidth( int w ) const +{ + QRect br; + QPixmap *pix = pixmap(); +#ifndef QT_NO_PICTURE + QPicture *pic = picture(); +#else + const int pic = 0; +#endif +#ifndef QT_NO_MOVIE + QMovie *mov = movie(); +#else + const int mov = 0; +#endif + int hextra = 2 * frameWidth(); + int vextra = hextra; + QFontMetrics fm( fontMetrics() ); + int xw = fm.width( 'x' ); + if ( !mov && !pix && !pic ) { + int m = indent(); + if ( m < 0 && hextra ) // no indent, but we do have a frame + m = xw / 2 - margin(); + if ( m >= 0 ) { + int horizAlign = QApplication::horizontalAlignment( align ); + if ( (horizAlign & AlignLeft) || (horizAlign & AlignRight ) ) + hextra += m; + if ( (align & AlignTop) || (align & AlignBottom ) ) + vextra += m; + } + } + + if ( pix ) + br = pix->rect(); +#ifndef QT_NO_PICTURE + else if ( pic ) + br = pic->boundingRect(); +#endif +#ifndef QT_NO_MOVIE + else if ( mov ) + br = mov->framePixmap().rect(); +#endif +#ifndef QT_NO_RICHTEXT + else if ( doc ) { + int oldW = doc->width(); + if ( align & WordBreak ) { + if ( w < 0 ) + doc->adjustSize(); + else + doc->setWidth( w-hextra ); + } + br = QRect( 0, 0, doc->widthUsed(), doc->height() ); + doc->setWidth( oldW ); + } +#endif + else { + bool tryWidth = (w < 0) && (align & WordBreak); + if ( tryWidth ) + w = xw * 80; + else if ( w < 0 ) + w = 2000; + w -= hextra; + br = fm.boundingRect( 0, 0, w ,2000, alignment(), text() ); + if ( tryWidth && br.height() < 4*fm.lineSpacing() && br.width() > w/2 ) + br = fm.boundingRect( 0, 0, w/2, 2000, alignment(), text() ); + if ( tryWidth && br.height() < 2*fm.lineSpacing() && br.width() > w/4 ) + br = fm.boundingRect( 0, 0, w/4, 2000, alignment(), text() ); + } + int wid = br.width() + hextra; + int hei = br.height() + vextra; + + return QSize( wid, hei ); +} + + +/*! + \reimp +*/ + +int QLabel::heightForWidth( int w ) const +{ + if ( +#ifndef QT_NO_RICHTEXT + doc || +#endif + (align & WordBreak) ) + return sizeForWidth( w ).height(); + return QWidget::heightForWidth( w ); +} + + + +/*!\reimp +*/ +QSize QLabel::sizeHint() const +{ + if ( d->valid_hints != frameWidth() ) + (void) QLabel::minimumSizeHint(); + return d->sh; +} + +/*! + \reimp +*/ + +QSize QLabel::minimumSizeHint() const +{ + if ( d->valid_hints == frameWidth() ) + return d->msh; + + constPolish(); + d->valid_hints = frameWidth(); + d->sh = sizeForWidth( -1 ); + QSize sz( -1, -1 ); + + if ( +#ifndef QT_NO_RICHTEXT + !doc && +#endif + (align & WordBreak) == 0 ) { + sz = d->sh; + } else { + // think about caching these for performance + sz.rwidth() = sizeForWidth( 0 ).width(); + sz.rheight() = sizeForWidth(QWIDGETSIZE_MAX).height(); + if ( d->sh.height() < sz.height() ) + sz.rheight() = d->sh.height(); + } + if ( sizePolicy().horData() == QSizePolicy::Ignored ) + sz.rwidth() = -1; + if ( sizePolicy().verData() == QSizePolicy::Ignored ) + sz.rheight() = -1; + d->msh = sz; + return sz; +} + +/*! + \reimp +*/ +void QLabel::resizeEvent( QResizeEvent* e ) +{ + QFrame::resizeEvent( e ); + +#ifdef QT_NO_RICHTEXT + static const bool doc = FALSE; +#endif + + // optimize for standard labels + if ( frameShape() == NoFrame && (align & WordBreak) == 0 && !doc && + ( e->oldSize().width() >= e->size().width() && (align & AlignLeft ) == AlignLeft ) + && ( e->oldSize().height() >= e->size().height() && (align & AlignTop ) == AlignTop ) ) { + setWFlags( WResizeNoErase ); + return; + } + + clearWFlags( WResizeNoErase ); + QRect cr = contentsRect(); + if ( !lpixmap || !cr.isValid() || + // masked pixmaps can only reduce flicker when being top/left + // aligned and when we do not perform scaled contents + ( lpixmap->hasAlpha() && ( scaledcontents || ( ( align & (AlignLeft|AlignTop) ) != (AlignLeft|AlignTop) ) ) ) ) + return; + + setWFlags( WResizeNoErase ); + + if ( !scaledcontents ) { + // don't we all love QFrame? Reduce pixmap flicker + QRegion reg = QRect( QPoint(0, 0), e->size() ); + reg = reg.subtract( cr ); + int x = cr.x(); + int y = cr.y(); + int w = lpixmap->width(); + int h = lpixmap->height(); + if ( (align & Qt::AlignVCenter) == Qt::AlignVCenter ) + y += cr.height()/2 - h/2; + else if ( (align & Qt::AlignBottom) == Qt::AlignBottom) + y += cr.height() - h; + if ( (align & Qt::AlignRight) == Qt::AlignRight ) + x += cr.width() - w; + else if ( (align & Qt::AlignHCenter) == Qt::AlignHCenter ) + x += cr.width()/2 - w/2; + if ( x > cr.x() ) + reg = reg.unite( QRect( cr.x(), cr.y(), x - cr.x(), cr.height() ) ); + if ( y > cr.y() ) + reg = reg.unite( QRect( cr.x(), cr.y(), cr.width(), y - cr.y() ) ); + + if ( x + w < cr.right() ) + reg = reg.unite( QRect( x + w, cr.y(), cr.right() - x - w, cr.height() ) ); + if ( y + h < cr.bottom() ) + reg = reg.unite( QRect( cr.x(), y + h, cr.width(), cr.bottom() - y - h ) ); + + erase( reg ); + } +} + + +/*! + Draws the label contents using the painter \a p. +*/ + +void QLabel::drawContents( QPainter *p ) +{ + QRect cr = contentsRect(); + + QPixmap *pix = pixmap(); +#ifndef QT_NO_PICTURE + QPicture *pic = picture(); +#else + const int pic = 0; +#endif +#ifndef QT_NO_MOVIE + QMovie *mov = movie(); +#else + const int mov = 0; +#endif + + if ( !mov && !pix && !pic ) { + int m = indent(); + if ( m < 0 && frameWidth() ) // no indent, but we do have a frame + m = fontMetrics().width('x') / 2 - margin(); + if ( m > 0 ) { + int hAlign = QApplication::horizontalAlignment( align ); + if ( hAlign & AlignLeft ) + cr.setLeft( cr.left() + m ); + if ( hAlign & AlignRight ) + cr.setRight( cr.right() - m ); + if ( align & AlignTop ) + cr.setTop( cr.top() + m ); + if ( align & AlignBottom ) + cr.setBottom( cr.bottom() - m ); + } + } + +#ifndef QT_NO_MOVIE + if ( mov ) { + // ### should add movie to qDrawItem + QRect r = style().itemRect( p, cr, align, isEnabled(), &(mov->framePixmap()), + QString::null ); + // ### could resize movie frame at this point + p->drawPixmap(r.x(), r.y(), mov->framePixmap() ); + } + else +#endif +#ifndef QT_NO_RICHTEXT + if ( doc ) { + doc->setWidth(p, cr.width() ); + int rh = doc->height(); + int yo = 0; + if ( align & AlignVCenter ) + yo = (cr.height()-rh)/2; + else if ( align & AlignBottom ) + yo = cr.height()-rh; + if (! isEnabled() && + style().styleHint(QStyle::SH_EtchDisabledText, this)) { + QColorGroup cg = colorGroup(); + cg.setColor( QColorGroup::Text, cg.light() ); + doc->draw(p, cr.x()+1, cr.y()+yo+1, cr, cg, 0); + } + + // QSimpleRichText always draws with QColorGroup::Text as with + // background mode PaletteBase. QLabel typically has + // background mode PaletteBackground, so we create a temporary + // color group with the text color adjusted. + QColorGroup cg = colorGroup(); + if ( backgroundMode() != PaletteBase && isEnabled() ) + cg.setColor( QColorGroup::Text, paletteForegroundColor() ); + + doc->draw(p, cr.x(), cr.y()+yo, cr, cg, 0); + } else +#endif +#ifndef QT_NO_PICTURE + if ( pic ) { + QRect br = pic->boundingRect(); + int rw = br.width(); + int rh = br.height(); + if ( scaledcontents ) { + p->save(); + p->translate( cr.x(), cr.y() ); +#ifndef QT_NO_TRANSFORMATIONS + p->scale( (double)cr.width()/rw, (double)cr.height()/rh ); +#endif + p->drawPicture( -br.x(), -br.y(), *pic ); + p->restore(); + } else { + int xo = 0; + int yo = 0; + if ( align & AlignVCenter ) + yo = (cr.height()-rh)/2; + else if ( align & AlignBottom ) + yo = cr.height()-rh; + if ( align & AlignRight ) + xo = cr.width()-rw; + else if ( align & AlignHCenter ) + xo = (cr.width()-rw)/2; + p->drawPicture( cr.x()+xo-br.x(), cr.y()+yo-br.y(), *pic ); + } + } else +#endif + { +#ifndef QT_NO_IMAGE_SMOOTHSCALE + if ( scaledcontents && pix ) { + if ( !d->img ) + d->img = new QImage( lpixmap->convertToImage() ); + + if ( !d->pix ) + d->pix = new QPixmap; + if ( d->pix->size() != cr.size() ) + d->pix->convertFromImage( d->img->smoothScale( cr.width(), cr.height() ) ); + pix = d->pix; + } +#endif + int alignment = align; + if ((align & ShowPrefix) && !style().styleHint(QStyle::SH_UnderlineAccelerator, this)) + alignment |= NoAccel; + // ordinary text or pixmap label + style().drawItem( p, cr, alignment, colorGroup(), isEnabled(), + pix, ltext ); + } +} + + +/*! + Updates the label, but not the frame. +*/ + +void QLabel::updateLabel( QSize oldSizeHint ) +{ + d->valid_hints = -1; + QSizePolicy policy = sizePolicy(); + bool wordBreak = align & WordBreak; + policy.setHeightForWidth( wordBreak ); + if ( policy != sizePolicy() ) + setSizePolicy( policy ); + if ( sizeHint() != oldSizeHint ) + updateGeometry(); + if ( autoresize ) { + adjustSize(); + update( contentsRect() ); + } else { + update( contentsRect() ); + } +} + + +/*! + \internal + + Internal slot, used to set focus for accelerator labels. +*/ +#ifndef QT_NO_ACCEL +void QLabel::acceleratorSlot() +{ + if ( !lbuddy ) + return; + QWidget * w = lbuddy; + while ( w->focusProxy() ) + w = w->focusProxy(); + if ( !w->hasFocus() && + w->isEnabled() && + w->isVisible() && + w->focusPolicy() != NoFocus ) { + QFocusEvent::setReason( QFocusEvent::Shortcut ); + w->setFocus(); + QFocusEvent::resetReason(); + } +} +#endif + +/*! + \internal + + Internal slot, used to clean up if the buddy widget dies. +*/ +#ifndef QT_NO_ACCEL +void QLabel::buddyDied() // I can't remember if I cried. +{ + lbuddy = 0; +} + +/*! + Sets this label's buddy to \a buddy. + + When the user presses the accelerator key indicated by this label, + the keyboard focus is transferred to the label's buddy widget. + + The buddy mechanism is only available for QLabels that contain + plain text in which one letter is prefixed with an ampersand, \&. + This letter is set as the accelerator key. The letter is displayed + underlined, and the '\&' is not displayed (i.e. the \c ShowPrefix + alignment flag is turned on; see setAlignment()). + + In a dialog, you might create two data entry widgets and a label + for each, and set up the geometry layout so each label is just to + the left of its data entry widget (its "buddy"), for example: + \code + QLineEdit *nameEd = new QLineEdit( this ); + QLabel *nameLb = new QLabel( "&Name:", this ); + nameLb->setBuddy( nameEd ); + QLineEdit *phoneEd = new QLineEdit( this ); + QLabel *phoneLb = new QLabel( "&Phone:", this ); + phoneLb->setBuddy( phoneEd ); + // ( layout setup not shown ) + \endcode + + With the code above, the focus jumps to the Name field when the + user presses Alt+N, and to the Phone field when the user presses + Alt+P. + + To unset a previously set buddy, call this function with \a buddy + set to 0. + + \sa buddy(), setText(), QAccel, setAlignment() +*/ + +void QLabel::setBuddy( QWidget *buddy ) +{ + if ( buddy ) + setAlignment( alignment() | ShowPrefix ); + else + setAlignment( alignment() & ~ShowPrefix ); + + if ( lbuddy ) + disconnect( lbuddy, SIGNAL(destroyed()), this, SLOT(buddyDied()) ); + + lbuddy = buddy; + + if ( !lbuddy ) + return; +#ifndef QT_NO_RICHTEXT + if ( !( textformat == RichText || (textformat == AutoText && + QStyleSheet::mightBeRichText(ltext) ) ) ) +#endif + { + int p = QAccel::shortcutKey( ltext ); + if ( p ) { + if ( !accel ) + accel = new QAccel( this, "accel label accel" ); + accel->connectItem( accel->insertItem( p ), + this, SLOT(acceleratorSlot()) ); + } + } + + connect( lbuddy, SIGNAL(destroyed()), this, SLOT(buddyDied()) ); +} + + +/*! + Returns this label's buddy, or 0 if no buddy is currently set. + + \sa setBuddy() +*/ + +QWidget * QLabel::buddy() const +{ + return lbuddy; +} +#endif //QT_NO_ACCEL + + +#ifndef QT_NO_MOVIE +void QLabel::movieUpdated(const QRect& rect) +{ + QMovie *mov = movie(); + if ( mov && !mov->isNull() ) { + QRect r = contentsRect(); + r = style().itemRect( 0, r, align, isEnabled(), &(mov->framePixmap()), + QString::null ); + r.moveBy(rect.x(), rect.y()); + r.setWidth(QMIN(r.width(), rect.width())); + r.setHeight(QMIN(r.height(), rect.height())); + repaint( r, mov->framePixmap().mask() != 0 ); + } +} + +void QLabel::movieResized( const QSize& size ) +{ + d->valid_hints = -1; + if ( autoresize ) + adjustSize(); + movieUpdated( QRect( QPoint(0,0), size ) ); + updateGeometry(); +} + +/*! + Sets the label contents to \a movie. Any previous content is + cleared. + + The buddy accelerator, if any, is disabled. + + The label resizes itself if auto-resizing is enabled. + + \sa movie(), setBuddy() +*/ + +void QLabel::setMovie( const QMovie& movie ) +{ + QSize osh = sizeHint(); + clearContents(); + + lmovie = new QMovie( movie ); + lmovie->connectResize(this, SLOT(movieResized(const QSize&))); + lmovie->connectUpdate(this, SLOT(movieUpdated(const QRect&))); + + if ( !lmovie->running() ) // Assume that if the movie is running, + updateLabel( osh ); // resize/update signals will come soon enough +} + +#endif // QT_NO_MOVIE + +/*! + \internal + + Clears any contents, without updating/repainting the label. +*/ + +void QLabel::clearContents() +{ +#ifndef QT_NO_RICHTEXT + delete doc; + doc = 0; +#endif + + delete lpixmap; + lpixmap = 0; +#ifndef QT_NO_PICTURE + delete lpicture; + lpicture = 0; +#endif + delete d->img; + d->img = 0; + delete d->pix; + d->pix = 0; + + ltext = QString::null; +#ifndef QT_NO_ACCEL + if ( accel ) + accel->clear(); +#endif +#ifndef QT_NO_MOVIE + if ( lmovie ) { + lmovie->disconnectResize(this, SLOT(movieResized(const QSize&))); + lmovie->disconnectUpdate(this, SLOT(movieUpdated(const QRect&))); + delete lmovie; + lmovie = 0; + } +#endif +} + + +#ifndef QT_NO_MOVIE + +/*! + Returns a pointer to the label's movie, or 0 if no movie has been + set. + + \sa setMovie() +*/ + +QMovie* QLabel::movie() const +{ + return lmovie; +} + +#endif // QT_NO_MOVIE + +/*! + \property QLabel::backgroundMode + \brief the label's background mode + + Get this property with backgroundMode(). + + \sa QWidget::setBackgroundMode() +*/ + +/*! + \property QLabel::textFormat + \brief the label's text format + + See the \c Qt::TextFormat enum for an explanation of the possible + options. + + The default format is \c AutoText. + + \sa text +*/ + +Qt::TextFormat QLabel::textFormat() const +{ + return textformat; +} + +void QLabel::setTextFormat( Qt::TextFormat format ) +{ + if ( format != textformat ) { + textformat = format; + QString t = ltext; + if ( !t.isNull() ) { + ltext = QString::null; + setText( t ); + } + } +} + +/*! + \reimp +*/ + +void QLabel::fontChange( const QFont & ) +{ + if ( !ltext.isEmpty() ) { +#ifndef QT_NO_RICHTEXT + if ( doc ) + doc->setDefaultFont( font() ); +#endif + updateLabel( QSize( -1, -1 ) ); + } +} + +#ifndef QT_NO_IMAGE_SMOOTHSCALE +/*! + \property QLabel::scaledContents + \brief whether the label will scale its contents to fill all + available space. + + When enabled and the label shows a pixmap, it will scale the + pixmap to fill the available space. + + This property's default is FALSE. + + \sa setScaledContents() +*/ +bool QLabel::hasScaledContents() const +{ + return scaledcontents; +} + +void QLabel::setScaledContents( bool enable ) +{ + if ( (bool)scaledcontents == enable ) + return; + scaledcontents = enable; + if ( !enable ) { + delete d->img; + d->img = 0; + delete d->pix; + d->pix = 0; + } + update( contentsRect() ); +} + +#endif // QT_NO_IMAGE_SMOOTHSCALE + +/*! + Sets the font used on the QLabel to font \a f. +*/ + +void QLabel::setFont( const QFont &f ) +{ + QFrame::setFont( f ); +} + +#endif // QT_NO_LABEL diff --git a/src/widgets/qlabel.h b/src/widgets/qlabel.h new file mode 100644 index 0000000..ac1b83b --- /dev/null +++ b/src/widgets/qlabel.h @@ -0,0 +1,174 @@ +/********************************************************************** +** +** Definition of QLabel widget class +** +** Created : 941215 +** +** 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. +** +**********************************************************************/ + +#ifndef QLABEL_H +#define QLABEL_H + +#ifndef QT_H +#include "qframe.h" +#endif // QT_H + +#ifndef QT_NO_LABEL + +class QSimpleRichText; +class QLabelPrivate; + +class Q_EXPORT QLabel : public QFrame +{ + Q_OBJECT + Q_PROPERTY( QString text READ text WRITE setText ) + Q_PROPERTY( TextFormat textFormat READ textFormat WRITE setTextFormat ) + Q_PROPERTY( QPixmap pixmap READ pixmap WRITE setPixmap ) + Q_PROPERTY( bool scaledContents READ hasScaledContents WRITE setScaledContents ) + Q_PROPERTY( Alignment alignment READ alignment WRITE setAlignment ) + Q_PROPERTY( int indent READ indent WRITE setIndent ) + Q_OVERRIDE( BackgroundMode backgroundMode DESIGNABLE true) + +public: + QLabel( QWidget *parent, const char* name=0, WFlags f=0 ); + QLabel( const QString &text, QWidget *parent, const char* name=0, + WFlags f=0 ); + QLabel( QWidget *buddy, const QString &, + QWidget *parent, const char* name=0, WFlags f=0 ); + ~QLabel(); + + QString text() const { return ltext; } + QPixmap *pixmap() const { return lpixmap; } +#ifndef QT_NO_PICTURE + QPicture *picture() const { return lpicture; } +#endif +#ifndef QT_NO_MOVIE + QMovie *movie() const; +#endif + + TextFormat textFormat() const; + void setTextFormat( TextFormat ); + + int alignment() const { return align; } + virtual void setAlignment( int ); + int indent() const { return extraMargin; } + void setIndent( int ); + + bool autoResize() const { return autoresize; } + virtual void setAutoResize( bool ); +#ifndef QT_NO_IMAGE_SMOOTHSCALE + bool hasScaledContents() const; + void setScaledContents( bool ); +#endif + QSize sizeHint() const; + QSize minimumSizeHint() const; +#ifndef QT_NO_ACCEL + virtual void setBuddy( QWidget * ); + QWidget *buddy() const; +#endif + int heightForWidth(int) const; + + void setFont( const QFont &f ); + +public slots: + virtual void setText( const QString &); + virtual void setPixmap( const QPixmap & ); +#ifndef QT_NO_PICTURE + virtual void setPicture( const QPicture & ); +#endif +#ifndef QT_NO_MOVIE + virtual void setMovie( const QMovie & ); +#endif + virtual void setNum( int ); + virtual void setNum( double ); + void clear(); + +protected: + void drawContents( QPainter * ); + void fontChange( const QFont & ); + void resizeEvent( QResizeEvent* ); + +private slots: +#ifndef QT_NO_ACCEL + void acceleratorSlot(); + void buddyDied(); +#endif +#ifndef QT_NO_MOVIE + void movieUpdated(const QRect&); + void movieResized(const QSize&); +#endif + +private: + void init(); + void clearContents(); + void updateLabel( QSize oldSizeHint ); + QSize sizeForWidth( int w ) const; + QString ltext; + QPixmap *lpixmap; +#ifndef QT_NO_PICTURE + QPicture *lpicture; +#endif +#ifndef QT_NO_MOVIE + QMovie * lmovie; +#endif +#ifndef QT_NO_ACCEL + QWidget * lbuddy; +#endif + ushort align; + short extraMargin; + uint autoresize:1; + uint scaledcontents :1; + TextFormat textformat; +#ifndef QT_NO_RICHTEXT + QSimpleRichText* doc; +#endif +#ifndef QT_NO_ACCEL + QAccel * accel; +#endif + QLabelPrivate* d; + + friend class QTipLabel; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QLabel( const QLabel & ); + QLabel &operator=( const QLabel & ); +#endif +}; + + +#endif // QT_NO_LABEL + +#endif // QLABEL_H diff --git a/src/widgets/qlcdnumber.cpp b/src/widgets/qlcdnumber.cpp new file mode 100644 index 0000000..4398907 --- /dev/null +++ b/src/widgets/qlcdnumber.cpp @@ -0,0 +1,1170 @@ +/**************************************************************************** +** +** Implementation of QLCDNumber class +** +** Created : 940518 +** +** 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 "qlcdnumber.h" +#ifndef QT_NO_LCDNUMBER +#include "qbitarray.h" +#include "qpainter.h" + + +/*! + \class QLCDNumber qlcdnumber.h + + \brief The QLCDNumber widget displays a number with LCD-like digits. + + \ingroup basic + \mainclass + + It can display a number in just about any size. It can display + decimal, hexadecimal, octal or binary numbers. It is easy to + connect to data sources using the display() slot, which is + overloaded to take any of five argument types. + + There are also slots to change the base with setMode() and the + decimal point with setSmallDecimalPoint(). + + QLCDNumber emits the overflow() signal when it is asked to display + something beyond its range. The range is set by setNumDigits(), + but setSmallDecimalPoint() also influences it. If the display is + set to hexadecimal, octal or binary, the integer equivalent of the + value is displayed. + + These digits and other symbols can be shown: 0/O, 1, 2, 3, 4, 5/S, + 6, 7, 8, 9/g, minus, decimal point, A, B, C, D, E, F, h, H, L, o, + P, r, u, U, Y, colon, degree sign (which is specified as single + quote in the string) and space. QLCDNumber substitutes spaces for + illegal characters. + + It is not possible to retrieve the contents of a QLCDNumber + object, although you can retrieve the numeric value with value(). + If you really need the text, we recommend that you connect the + signals that feed the display() slot to another slot as well and + store the value there. + + Incidentally, QLCDNumber is the very oldest part of Qt, tracing + back to a BASIC program on the \link + http://www.nvg.ntnu.no/sinclair/computers/zxspectrum/zxspectrum.htm + Sinclair Spectrum\endlink. + + <img src=qlcdnum-m.png> <img src=qlcdnum-w.png> + + \sa QLabel, QFrame +*/ + +/*! + \enum QLCDNumber::Mode + + This type determines how numbers are shown. + + \value Hex Hexadecimal + \value Dec Decimal + \value Oct Octal + \value Bin Binary + + If the display is set to hexadecimal, octal or binary, the integer + equivalent of the value is displayed. +*/ + +/*! + \enum QLCDNumber::SegmentStyle + + This type determines the visual appearance of the QLCDNumber + widget. + + \value Outline gives raised segments filled with the background brush. + \value Filled gives raised segments filled with the foreground brush. + \value Flat gives flat segments filled with the foreground brush. +*/ + + + +/*! + \fn void QLCDNumber::overflow() + + This signal is emitted whenever the QLCDNumber is asked to display + a too-large number or a too-long string. + + It is never emitted by setNumDigits(). +*/ + + +static QString int2string( int num, int base, int ndigits, bool *oflow ) +{ + QString s; + bool negative; + if ( num < 0 ) { + negative = TRUE; + num = -num; + } else { + negative = FALSE; + } + switch( base ) { + case QLCDNumber::HEX: + s.sprintf( "%*x", ndigits, num ); + break; + case QLCDNumber::DEC: + s.sprintf( "%*i", ndigits, num ); + break; + case QLCDNumber::OCT: + s.sprintf( "%*o", ndigits, num ); + break; + case QLCDNumber::BIN: + { + char buf[42]; + char *p = &buf[41]; + uint n = num; + int len = 0; + *p = '\0'; + do { + *--p = (char)((n&1)+'0'); + n >>= 1; + len++; + } while ( n != 0 ); + len = ndigits - len; + if ( len > 0 ) + s.fill( ' ', len ); + s += QString::fromLatin1(p); + } + break; + } + if ( negative ) { + for ( int i=0; i<(int)s.length(); i++ ) { + if ( s[i] != ' ' ) { + if ( i != 0 ) { + s[i-1] = '-'; + } else { + s.insert( 0, '-' ); + } + break; + } + } + } + if ( oflow ) + *oflow = (int)s.length() > ndigits; + return s; +} + + +static QString double2string( double num, int base, int ndigits, bool *oflow ) +{ + QString s; + if ( base != QLCDNumber::DEC ) { + bool of = num >= 2147483648.0 || num < -2147483648.0; + if ( of ) { // oops, integer overflow + if ( oflow ) + *oflow = TRUE; + return s; + } + s = int2string( (int)num, base, ndigits, 0 ); + } else { // decimal base + int nd = ndigits; + do { + s.sprintf( "%*.*g", ndigits, nd, num ); + int i = s.find('e'); + if ( i > 0 && s[i+1]=='+' ) { + s[i] = ' '; + s[i+1] = 'e'; + } + } while (nd-- && (int)s.length() > ndigits); + } + if ( oflow ) + *oflow = (int)s.length() > ndigits; + return s; +} + + +static const char *getSegments( char ch ) // gets list of segments for ch +{ + static const char segments[30][8] = + { { 0, 1, 2, 4, 5, 6,99, 0}, // 0 0 / O + { 2, 5,99, 0, 0, 0, 0, 0}, // 1 1 + { 0, 2, 3, 4, 6,99, 0, 0}, // 2 2 + { 0, 2, 3, 5, 6,99, 0, 0}, // 3 3 + { 1, 2, 3, 5,99, 0, 0, 0}, // 4 4 + { 0, 1, 3, 5, 6,99, 0, 0}, // 5 5 / S + { 0, 1, 3, 4, 5, 6,99, 0}, // 6 6 + { 0, 2, 5,99, 0, 0, 0, 0}, // 7 7 + { 0, 1, 2, 3, 4, 5, 6,99}, // 8 8 + { 0, 1, 2, 3, 5, 6,99, 0}, // 9 9 / g + { 3,99, 0, 0, 0, 0, 0, 0}, // 10 - + { 7,99, 0, 0, 0, 0, 0, 0}, // 11 . + { 0, 1, 2, 3, 4, 5,99, 0}, // 12 A + { 1, 3, 4, 5, 6,99, 0, 0}, // 13 B + { 0, 1, 4, 6,99, 0, 0, 0}, // 14 C + { 2, 3, 4, 5, 6,99, 0, 0}, // 15 D + { 0, 1, 3, 4, 6,99, 0, 0}, // 16 E + { 0, 1, 3, 4,99, 0, 0, 0}, // 17 F + { 1, 3, 4, 5,99, 0, 0, 0}, // 18 h + { 1, 2, 3, 4, 5,99, 0, 0}, // 19 H + { 1, 4, 6,99, 0, 0, 0, 0}, // 20 L + { 3, 4, 5, 6,99, 0, 0, 0}, // 21 o + { 0, 1, 2, 3, 4,99, 0, 0}, // 22 P + { 3, 4,99, 0, 0, 0, 0, 0}, // 23 r + { 4, 5, 6,99, 0, 0, 0, 0}, // 24 u + { 1, 2, 4, 5, 6,99, 0, 0}, // 25 U + { 1, 2, 3, 5, 6,99, 0, 0}, // 26 Y + { 8, 9,99, 0, 0, 0, 0, 0}, // 27 : + { 0, 1, 2, 3,99, 0, 0, 0}, // 28 ' + {99, 0, 0, 0, 0, 0, 0, 0} }; // 29 empty + + if (ch >= '0' && ch <= '9') + return segments[ch - '0']; + if (ch >= 'A' && ch <= 'F') + return segments[ch - 'A' + 12]; + if (ch >= 'a' && ch <= 'f') + return segments[ch - 'a' + 12]; + + int n; + switch ( ch ) { + case '-': + n = 10; break; + case 'O': + n = 0; break; + case 'g': + n = 9; break; + case '.': + n = 11; break; + case 'h': + n = 18; break; + case 'H': + n = 19; break; + case 'l': + case 'L': + n = 20; break; + case 'o': + n = 21; break; + case 'p': + case 'P': + n = 22; break; + case 'r': + case 'R': + n = 23; break; + case 's': + case 'S': + n = 5; break; + case 'u': + n = 24; break; + case 'U': + n = 25; break; + case 'y': + case 'Y': + n = 26; break; + case ':': + n = 27; break; + case '\'': + n = 28; break; + default: + n = 29; break; + } + return segments[n]; +} + + +/*! + Constructs an LCD number, sets the number of digits to 5, the base + to decimal, the decimal point mode to 'small' and the frame style + to a raised box. The segmentStyle() is set to \c Outline. + + The \a parent and \a name arguments are passed to the QFrame + constructor. + + \sa setNumDigits(), setSmallDecimalPoint() +*/ + +QLCDNumber::QLCDNumber( QWidget *parent, const char *name ) + : QFrame( parent, name ) +{ + ndigits = 5; + init(); +} + + +/*! + Constructs an LCD number, sets the number of digits to \a + numDigits, the base to decimal, the decimal point mode to 'small' + and the frame style to a raised box. The segmentStyle() is set to + \c Outline. + + The \a parent and \a name arguments are passed to the QFrame + constructor. + + \sa setNumDigits(), setSmallDecimalPoint() +*/ + +QLCDNumber::QLCDNumber( uint numDigits, QWidget *parent, const char *name ) + : QFrame( parent, name ) +{ + ndigits = numDigits; + init(); +} + +/*! + \internal +*/ + +void QLCDNumber::init() +{ + setFrameStyle( QFrame::Box | QFrame::Raised ); + val = 0; + base = DEC; + smallPoint = FALSE; + setNumDigits( ndigits ); + setSegmentStyle( Outline ); + d = 0; + setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ) ); +} + +/*! + Destroys the LCD number. +*/ + +QLCDNumber::~QLCDNumber() +{ +} + + +/*! + \property QLCDNumber::numDigits + \brief the current number of digits displayed + + Corresponds to the current number of digits. If \l + QLCDNumber::smallDecimalPoint is FALSE, the decimal point occupies + one digit position. + + \sa numDigits, smallDecimalPoint +*/ + +void QLCDNumber::setNumDigits( int numDigits ) +{ + if ( numDigits > 99 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QLCDNumber::setNumDigits: (%s) Max 99 digits allowed", + name( "unnamed" ) ); +#endif + numDigits = 99; + } + if (numDigits < 0 ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QLCDNumber::setNumDigits: (%s) Min 0 digits allowed", + name( "unnamed" ) ); +#endif + numDigits = 0; + } + if ( digitStr.isNull() ) { // from constructor + ndigits = numDigits; + digitStr.fill( ' ', ndigits ); + points.fill( 0, ndigits ); + digitStr[ndigits - 1] = '0'; // "0" is the default number + } else { + bool doDisplay = ndigits == 0; + if ( numDigits == ndigits ) // no change + return; + register int i; + int dif; + if ( numDigits > ndigits ) { // expand + dif = numDigits - ndigits; + QString buf; + buf.fill( ' ', dif ); + digitStr.insert( 0, buf ); + points.resize( numDigits ); + for ( i=numDigits-1; i>=dif; i-- ) + points.setBit( i, points.testBit(i-dif) ); + for ( i=0; i<dif; i++ ) + points.clearBit( i ); + } else { // shrink + dif = ndigits - numDigits; + digitStr = digitStr.right( numDigits ); + QBitArray tmpPoints = points.copy(); + points.resize( numDigits ); + for ( i=0; i<(int)numDigits; i++ ) + points.setBit( i, tmpPoints.testBit(i+dif) ); + } + ndigits = numDigits; + if ( doDisplay ) + display( value() ); + update(); + } +} + + +/*! + \overload + + Returns TRUE if \a num is too big to be displayed in its entirety; + otherwise returns FALSE. + + \sa display(), numDigits(), smallDecimalPoint() +*/ + +bool QLCDNumber::checkOverflow( int num ) const +{ + bool of; + int2string( num, base, ndigits, &of ); + return of; +} + + +/*! + Returns TRUE if \a num is too big to be displayed in its entirety; + otherwise returns FALSE. + + \sa display(), numDigits(), smallDecimalPoint() +*/ + +bool QLCDNumber::checkOverflow( double num ) const +{ + bool of; + double2string( num, base, ndigits, &of ); + return of; +} + + +/*! + \property QLCDNumber::mode + \brief the current display mode (number base) + + Corresponds to the current display mode, which is one of \c BIN, + \c OCT, \c DEC (the default) and \c HEX. \c DEC mode can display + floating point values, the other modes display the integer + equivalent. + + \sa smallDecimalPoint(), setHexMode(), setDecMode(), setOctMode(), setBinMode() +*/ + +QLCDNumber::Mode QLCDNumber::mode() const +{ + return (QLCDNumber::Mode) base; +} + +void QLCDNumber::setMode( Mode m ) +{ + base = m; + + display( val ); +} + + +/*! + \property QLCDNumber::value + \brief the displayed value + + This property corresponds to the current value displayed by the + LCDNumber. + + If the displayed value is not a number, the property has a value + of 0. +*/ + +double QLCDNumber::value() const +{ + return val; +} + +/*! + \overload + + Displays the number \a num. +*/ +void QLCDNumber::display( double num ) +{ + val = num; + bool of; + QString s = double2string( num, base, ndigits, &of ); + if ( of ) + emit overflow(); + else + internalSetString( s ); +} + +/*! + \property QLCDNumber::intValue + \brief the displayed value rounded to the nearest integer + + This property corresponds to the nearest integer to the current + value displayed by the LCDNumber. This is the value used for + hexadecimal, octal and binary modes. + + If the displayed value is not a number, the property has a value + of 0. +*/ +int QLCDNumber::intValue() const +{ + return (int)(val < 0 ? val - 0.5 : val + 0.5); +} + + +/*! + \overload + + Displays the number \a num. +*/ +void QLCDNumber::display( int num ) +{ + val = (double)num; + bool of; + QString s = int2string( num, base, ndigits, &of ); + if ( of ) + emit overflow(); + else + internalSetString( s ); +} + + +/*! + Displays the number represented by the string \a s. + + This version of the function disregards mode() and + smallDecimalPoint(). + + These digits and other symbols can be shown: 0/O, 1, 2, 3, 4, 5/S, + 6, 7, 8, 9/g, minus, decimal point, A, B, C, D, E, F, h, H, L, o, + P, r, u, U, Y, colon, degree sign (which is specified as single + quote in the string) and space. QLCDNumber substitutes spaces for + illegal characters. +*/ + +void QLCDNumber::display( const QString &s ) +{ + val = 0; + bool ok = FALSE; + double v = s.toDouble( &ok ); + if ( ok ) + val = v; + internalSetString( s ); +} + +/*! + Calls setMode( HEX ). Provided for convenience (e.g. for + connecting buttons to it). + + \sa setMode(), setDecMode(), setOctMode(), setBinMode(), mode() +*/ + +void QLCDNumber::setHexMode() +{ + setMode( HEX ); +} + + +/*! + Calls setMode( DEC ). Provided for convenience (e.g. for + connecting buttons to it). + + \sa setMode(), setHexMode(), setOctMode(), setBinMode(), mode() +*/ + +void QLCDNumber::setDecMode() +{ + setMode( DEC ); +} + + +/*! + Calls setMode( OCT ). Provided for convenience (e.g. for + connecting buttons to it). + + \sa setMode(), setHexMode(), setDecMode(), setBinMode(), mode() +*/ + +void QLCDNumber::setOctMode() +{ + setMode( OCT ); +} + + +/*! + Calls setMode( BIN ). Provided for convenience (e.g. for + connecting buttons to it). + + \sa setMode(), setHexMode(), setDecMode(), setOctMode(), mode() +*/ + +void QLCDNumber::setBinMode() +{ + setMode( BIN ); +} + + +/*! + \property QLCDNumber::smallDecimalPoint + \brief the style of the decimal point + + If TRUE the decimal point is drawn between two digit positions. + Otherwise it occupies a digit position of its own, i.e. is drawn + in a digit position. The default is FALSE. + + The inter-digit space is made slightly wider when the decimal + point is drawn between the digits. + + \sa mode +*/ + +void QLCDNumber::setSmallDecimalPoint( bool b ) +{ + smallPoint = b; +} + + +/*! + Draws the LCD number using painter \a p. This function is called + from QFrame::paintEvent(). +*/ + + +void QLCDNumber::drawContents( QPainter *p ) +{ + if ( smallPoint ) + drawString( digitStr, *p, &points, FALSE ); + else + drawString( digitStr, *p, 0, FALSE ); +} + + +/*! + \internal +*/ + +void QLCDNumber::internalDisplay( const QString & ) +{ + // Not used anymore +} + +void QLCDNumber::internalSetString( const QString& s ) +{ + QString buffer; + int i; + int len = s.length(); + QBitArray newPoints(ndigits); + + if ( !smallPoint ) { + if ( len == ndigits ) + buffer = s; + else + buffer = s.right( ndigits ).rightJustify( ndigits, ' ' ); + } else { + int index = -1; + bool lastWasPoint = TRUE; + newPoints.clearBit(0); + for ( i=0; i<len; i++ ) { + if ( s[i] == '.' ) { + if ( lastWasPoint ) { // point already set for digit? + if ( index == ndigits - 1 ) // no more digits + break; + index++; + buffer[index] = ' '; // 2 points in a row, add space + } + newPoints.setBit(index); // set decimal point + lastWasPoint = TRUE; + } else { + if ( index == ndigits - 1 ) + break; + index++; + buffer[index] = s[i]; + newPoints.clearBit(index); // decimal point default off + lastWasPoint = FALSE; + } + } + if ( index < ((int) ndigits) - 1 ) { + for( i=index; i>=0; i-- ) { + buffer[ndigits - 1 - index + i] = buffer[i]; + newPoints.setBit( ndigits - 1 - index + i, + newPoints.testBit(i) ); + } + for( i=0; i<ndigits-index-1; i++ ) { + buffer[i] = ' '; + newPoints.clearBit(i); + } + } + } + + if ( buffer == digitStr ) + return; + + if ( backgroundMode() == FixedPixmap + || colorGroup().brush( QColorGroup::Background ).pixmap() ) { + digitStr = buffer; + if ( smallPoint ) + points = newPoints; + repaint( contentsRect() ); + } else { + QPainter p( this ); + if ( !smallPoint ) + drawString( buffer, p ); + else + drawString( buffer, p, &newPoints ); + } +} + +/*! + \internal +*/ + +void QLCDNumber::drawString( const QString &s, QPainter &p, + QBitArray *newPoints, bool newString ) +{ + QPoint pos; + + int digitSpace = smallPoint ? 2 : 1; + int xSegLen = width()*5/(ndigits*(5 + digitSpace) + digitSpace); + int ySegLen = height()*5/12; + int segLen = ySegLen > xSegLen ? xSegLen : ySegLen; + int xAdvance = segLen*( 5 + digitSpace )/5; + int xOffset = ( width() - ndigits*xAdvance + segLen/5 )/2; + int yOffset = ( height() - segLen*2 )/2; + + for ( int i=0; i<ndigits; i++ ) { + pos = QPoint( xOffset + xAdvance*i, yOffset ); + if ( newString ) + drawDigit( pos, p, segLen, s[i], digitStr[i].latin1() ); + else + drawDigit( pos, p, segLen, s[i]); + if ( newPoints ) { + char newPoint = newPoints->testBit(i) ? '.' : ' '; + if ( newString ) { + char oldPoint = points.testBit(i) ? '.' : ' '; + drawDigit( pos, p, segLen, newPoint, oldPoint ); + } else { + drawDigit( pos, p, segLen, newPoint ); + } + } + } + if ( newString ) { + digitStr = s; + if ( (int)digitStr.length() > ndigits ) + digitStr.truncate( ndigits ); + if ( newPoints ) + points = *newPoints; + } +} + + +/*! + \internal +*/ + +void QLCDNumber::drawDigit( const QPoint &pos, QPainter &p, int segLen, + char newCh, char oldCh ) +{ +// Draws and/or erases segments to change display of a single digit +// from oldCh to newCh + + char updates[18][2]; // can hold 2 times number of segments, only + // first 9 used if segment table is correct + int nErases; + int nUpdates; + const char *segs; + int i,j; + + const char erase = 0; + const char draw = 1; + const char leaveAlone = 2; + + segs = getSegments(oldCh); + for ( nErases=0; segs[nErases] != 99; nErases++ ) { + updates[nErases][0] = erase; // get segments to erase to + updates[nErases][1] = segs[nErases]; // remove old char + } + nUpdates = nErases; + segs = getSegments(newCh); + for(i = 0 ; segs[i] != 99 ; i++) { + for ( j=0; j<nErases; j++ ) + if ( segs[i] == updates[j][1] ) { // same segment ? + updates[j][0] = leaveAlone; // yes, already on screen + break; + } + if ( j == nErases ) { // if not already on screen + updates[nUpdates][0] = draw; + updates[nUpdates][1] = segs[i]; + nUpdates++; + } + } + for ( i=0; i<nUpdates; i++ ) { + if ( updates[i][0] == draw ) + drawSegment( pos, updates[i][1], p, segLen ); + if (updates[i][0] == erase) + drawSegment( pos, updates[i][1], p, segLen, TRUE ); + } +} + + +static void addPoint( QPointArray &a, const QPoint &p ) +{ + uint n = a.size(); + a.resize( n + 1 ); + a.setPoint( n, p ); +} + +/*! + \internal +*/ + +void QLCDNumber::drawSegment( const QPoint &pos, char segmentNo, QPainter &p, + int segLen, bool erase ) +{ + QPoint pt = pos; + int width = segLen/5; + + const QColorGroup & g = colorGroup(); + QColor lightColor,darkColor,fgColor; + if ( erase ){ + lightColor = backgroundColor(); + darkColor = lightColor; + fgColor = lightColor; + } else { + lightColor = g.light(); + darkColor = g.dark(); + fgColor = g.foreground(); + } + +#define LINETO(X,Y) addPoint( a, QPoint(pt.x() + (X),pt.y() + (Y))) +#define LIGHT +#define DARK + + if ( fill ) { + QPointArray a(0); + + //The following is an exact copy of the switch below. + //don't make any changes here + switch ( segmentNo ) { + case 0 : + p.moveTo(pt); + LIGHT; + LINETO(segLen - 1,0); + DARK; + LINETO(segLen - width - 1,width); + LINETO(width,width); + LINETO(0,0); + break; + case 1 : + pt += QPoint(0 , 1); + p.moveTo(pt); + LIGHT; + LINETO(width,width); + DARK; + LINETO(width,segLen - width/2 - 2); + LINETO(0,segLen - 2); + LIGHT; + LINETO(0,0); + break; + case 2 : + pt += QPoint(segLen - 1 , 1); + p.moveTo(pt); + DARK; + LINETO(0,segLen - 2); + LINETO(-width,segLen - width/2 - 2); + LIGHT; + LINETO(-width,width); + LINETO(0,0); + break; + case 3 : + pt += QPoint(0 , segLen); + p.moveTo(pt); + LIGHT; + LINETO(width,-width/2); + LINETO(segLen - width - 1,-width/2); + LINETO(segLen - 1,0); + DARK; + if (width & 1) { // adjust for integer division error + LINETO(segLen - width - 3,width/2 + 1); + LINETO(width + 2,width/2 + 1); + } else { + LINETO(segLen - width - 1,width/2); + LINETO(width,width/2); + } + LINETO(0,0); + break; + case 4 : + pt += QPoint(0 , segLen + 1); + p.moveTo(pt); + LIGHT; + LINETO(width,width/2); + DARK; + LINETO(width,segLen - width - 2); + LINETO(0,segLen - 2); + LIGHT; + LINETO(0,0); + break; + case 5 : + pt += QPoint(segLen - 1 , segLen + 1); + p.moveTo(pt); + DARK; + LINETO(0,segLen - 2); + LINETO(-width,segLen - width - 2); + LIGHT; + LINETO(-width,width/2); + LINETO(0,0); + break; + case 6 : + pt += QPoint(0 , segLen*2); + p.moveTo(pt); + LIGHT; + LINETO(width,-width); + LINETO(segLen - width - 1,-width); + LINETO(segLen - 1,0); + DARK; + LINETO(0,0); + break; + case 7 : + if ( smallPoint ) // if smallpoint place'.' between other digits + pt += QPoint(segLen + width/2 , segLen*2); + else + pt += QPoint(segLen/2 , segLen*2); + p.moveTo(pt); + DARK; + LINETO(width,0); + LINETO(width,-width); + LIGHT; + LINETO(0,-width); + LINETO(0,0); + break; + case 8 : + pt += QPoint(segLen/2 - width/2 + 1 , segLen/2 + width); + p.moveTo(pt); + DARK; + LINETO(width,0); + LINETO(width,-width); + LIGHT; + LINETO(0,-width); + LINETO(0,0); + break; + case 9 : + pt += QPoint(segLen/2 - width/2 + 1 , 3*segLen/2 + width); + p.moveTo(pt); + DARK; + LINETO(width,0); + LINETO(width,-width); + LIGHT; + LINETO(0,-width); + LINETO(0,0); + break; +#if defined(QT_CHECK_RANGE) + default : + qWarning( "QLCDNumber::drawSegment: (%s) Internal error." + " Illegal segment id: %d\n", + name( "unnamed" ), segmentNo ); +#endif + } + // End exact copy + p.setPen( fgColor ); + p.setBrush( fgColor ); + p.drawPolygon( a ); + p.setBrush( NoBrush ); + + pt = pos; + } +#undef LINETO +#undef LIGHT +#undef DARK + +#define LINETO(X,Y) p.lineTo(QPoint(pt.x() + (X),pt.y() + (Y))) +#define LIGHT p.setPen(lightColor) +#define DARK p.setPen(darkColor) + if ( shadow ) + switch ( segmentNo ) { + case 0 : + p.moveTo(pt); + LIGHT; + LINETO(segLen - 1,0); + DARK; + LINETO(segLen - width - 1,width); + LINETO(width,width); + LINETO(0,0); + break; + case 1 : + pt += QPoint(0,1); + p.moveTo(pt); + LIGHT; + LINETO(width,width); + DARK; + LINETO(width,segLen - width/2 - 2); + LINETO(0,segLen - 2); + LIGHT; + LINETO(0,0); + break; + case 2 : + pt += QPoint(segLen - 1 , 1); + p.moveTo(pt); + DARK; + LINETO(0,segLen - 2); + LINETO(-width,segLen - width/2 - 2); + LIGHT; + LINETO(-width,width); + LINETO(0,0); + break; + case 3 : + pt += QPoint(0 , segLen); + p.moveTo(pt); + LIGHT; + LINETO(width,-width/2); + LINETO(segLen - width - 1,-width/2); + LINETO(segLen - 1,0); + DARK; + if (width & 1) { // adjust for integer division error + LINETO(segLen - width - 3,width/2 + 1); + LINETO(width + 2,width/2 + 1); + } else { + LINETO(segLen - width - 1,width/2); + LINETO(width,width/2); + } + LINETO(0,0); + break; + case 4 : + pt += QPoint(0 , segLen + 1); + p.moveTo(pt); + LIGHT; + LINETO(width,width/2); + DARK; + LINETO(width,segLen - width - 2); + LINETO(0,segLen - 2); + LIGHT; + LINETO(0,0); + break; + case 5 : + pt += QPoint(segLen - 1 , segLen + 1); + p.moveTo(pt); + DARK; + LINETO(0,segLen - 2); + LINETO(-width,segLen - width - 2); + LIGHT; + LINETO(-width,width/2); + LINETO(0,0); + break; + case 6 : + pt += QPoint(0 , segLen*2); + p.moveTo(pt); + LIGHT; + LINETO(width,-width); + LINETO(segLen - width - 1,-width); + LINETO(segLen - 1,0); + DARK; + LINETO(0,0); + break; + case 7 : + if ( smallPoint ) // if smallpoint place'.' between other digits + pt += QPoint(segLen + width/2 , segLen*2); + else + pt += QPoint(segLen/2 , segLen*2); + p.moveTo(pt); + DARK; + LINETO(width,0); + LINETO(width,-width); + LIGHT; + LINETO(0,-width); + LINETO(0,0); + break; + case 8 : + pt += QPoint(segLen/2 - width/2 + 1 , segLen/2 + width); + p.moveTo(pt); + DARK; + LINETO(width,0); + LINETO(width,-width); + LIGHT; + LINETO(0,-width); + LINETO(0,0); + break; + case 9 : + pt += QPoint(segLen/2 - width/2 + 1 , 3*segLen/2 + width); + p.moveTo(pt); + DARK; + LINETO(width,0); + LINETO(width,-width); + LIGHT; + LINETO(0,-width); + LINETO(0,0); + break; +#if defined(QT_CHECK_RANGE) + default : + qWarning( "QLCDNumber::drawSegment: (%s) Internal error." + " Illegal segment id: %d\n", + name( "unnamed" ), segmentNo ); +#endif + } + +#undef LINETO +#undef LIGHT +#undef DARK +} + + + +/*! + \property QLCDNumber::segmentStyle + \brief the style of the LCDNumber + + \table + \header \i Style \i Result + \row \i \c Outline + \i Produces raised segments filled with the background color + (this is the default). + \row \i \c Filled + \i Produces raised segments filled with the foreground color. + \row \i \c Flat + \i Produces flat segments filled with the foreground color. + \endtable + + \c Outline and \c Filled will additionally use + QColorGroup::light() and QColorGroup::dark() for shadow effects. +*/ +void QLCDNumber::setSegmentStyle( SegmentStyle s ) +{ + fill = ( s == Flat || s == Filled ); + shadow = ( s == Outline || s == Filled ); + update(); +} + +QLCDNumber::SegmentStyle QLCDNumber::segmentStyle() const +{ + Q_ASSERT( fill || shadow ); + if ( !fill && shadow ) + return Outline; + if ( fill && shadow ) + return Filled; + return Flat; +} + + +/*!\reimp +*/ +QSize QLCDNumber::sizeHint() const +{ + return QSize( 10 + 9 * (numDigits() + (smallDecimalPoint() ? 0 : 1)), 23 ); +} + +#endif // QT_NO_LCDNUMBER diff --git a/src/widgets/qlcdnumber.h b/src/widgets/qlcdnumber.h new file mode 100644 index 0000000..fb4780a --- /dev/null +++ b/src/widgets/qlcdnumber.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Definition of QLCDNumber class +** +** Created : 940518 +** +** 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. +** +**********************************************************************/ + +#ifndef QLCDNUMBER_H +#define QLCDNUMBER_H + +#ifndef QT_H +#include "qframe.h" +#include "qbitarray.h" +#endif // QT_H + +#ifndef QT_NO_LCDNUMBER + + +class QLCDNumberPrivate; + +class Q_EXPORT QLCDNumber : public QFrame // LCD number widget +{ + Q_OBJECT + Q_ENUMS( Mode SegmentStyle ) + Q_PROPERTY( bool smallDecimalPoint READ smallDecimalPoint WRITE setSmallDecimalPoint ) + Q_PROPERTY( int numDigits READ numDigits WRITE setNumDigits ) + Q_PROPERTY( Mode mode READ mode WRITE setMode ) + Q_PROPERTY( SegmentStyle segmentStyle READ segmentStyle WRITE setSegmentStyle ) + Q_PROPERTY( double value READ value WRITE display ) + Q_PROPERTY( int intValue READ intValue WRITE display ) + +public: + QLCDNumber( QWidget* parent=0, const char* name=0 ); + QLCDNumber( uint numDigits, QWidget* parent=0, const char* name=0 ); + ~QLCDNumber(); + + enum Mode { Hex, Dec, Oct, Bin, HEX = Hex, DEC = Dec, OCT = Oct, + BIN = Bin }; + enum SegmentStyle { Outline, Filled, Flat }; + + bool smallDecimalPoint() const; + + int numDigits() const; + virtual void setNumDigits( int nDigits ); + + bool checkOverflow( double num ) const; + bool checkOverflow( int num ) const; + + Mode mode() const; + virtual void setMode( Mode ); + + SegmentStyle segmentStyle() const; + virtual void setSegmentStyle( SegmentStyle ); + + double value() const; + int intValue() const; + + QSize sizeHint() const; + +public slots: + void display( const QString &str ); + void display( int num ); + void display( double num ); + virtual void setHexMode(); + virtual void setDecMode(); + virtual void setOctMode(); + virtual void setBinMode(); + virtual void setSmallDecimalPoint( bool ); + +signals: + void overflow(); + +protected: + void drawContents( QPainter * ); + +private: + void init(); + void internalDisplay( const QString &); + void internalSetString( const QString& s ); + void drawString( const QString& s, QPainter &, QBitArray * = 0, + bool = TRUE ); + //void drawString( const QString &, QPainter &, QBitArray * = 0 ) const; + void drawDigit( const QPoint &, QPainter &, int, char, + char = ' ' ); + void drawSegment( const QPoint &, char, QPainter &, int, bool = FALSE ); + + int ndigits; + double val; + uint base : 2; + uint smallPoint : 1; + uint fill : 1; + uint shadow : 1; + QString digitStr; + QBitArray points; + QLCDNumberPrivate * d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QLCDNumber( const QLCDNumber & ); + QLCDNumber &operator=( const QLCDNumber & ); +#endif +}; + +inline bool QLCDNumber::smallDecimalPoint() const +{ return (bool)smallPoint; } + +inline int QLCDNumber::numDigits() const +{ return ndigits; } + + +#endif // QT_NO_LCDNUMBER + +#endif // QLCDNUMBER_H diff --git a/src/widgets/qlineedit.cpp b/src/widgets/qlineedit.cpp new file mode 100644 index 0000000..d786829 --- /dev/null +++ b/src/widgets/qlineedit.cpp @@ -0,0 +1,2925 @@ +/********************************************************************** +** +** Implementation of QLineEdit widget class +** +** Created : 941011 +** +** 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 "qlineedit.h" +#ifndef QT_NO_LINEEDIT + +// Keep this position to avoid patch rejection +#ifndef QT_NO_IM +#include "qinputcontext.h" +#endif + +#include "qpainter.h" +#include "qdrawutil.h" +#include "qfontmetrics.h" +#include "qpixmap.h" +#include "qclipboard.h" +#include "qapplication.h" +#include "qvalidator.h" +#include "qdragobject.h" +#include "qtimer.h" +#include "qpopupmenu.h" +#include "qstringlist.h" +#include "qguardedptr.h" +#include "qstyle.h" +#include "qwhatsthis.h" +#include "../kernel/qinternal_p.h" +#include "private/qtextlayout_p.h" +#include "qvaluevector.h" +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif + +#ifndef QT_NO_ACCEL +#include "qkeysequence.h" +#define ACCEL_KEY(k) "\t" + QString(QKeySequence( Qt::CTRL | Qt::Key_ ## k )) +#else +#define ACCEL_KEY(k) "\t" + QString("Ctrl+" #k) +#endif + +#define innerMargin 1 + +struct QLineEditPrivate : public Qt +{ + QLineEditPrivate( QLineEdit *q ) + : q(q), cursor(0), cursorTimer(0), tripleClickTimer(0), frame(1), + cursorVisible(0), separator(0), readOnly(0), modified(0), + direction(QChar::DirON), dragEnabled(1), alignment(0), + echoMode(0), textDirty(0), selDirty(0), validInput(1), + ascent(0), maxLength(32767), menuId(0), + hscroll(0), validator(0), maskData(0), + undoState(0), selstart(0), selend(0), + imstart(0), imend(0), imselstart(0), imselend(0) +#ifndef QT_NO_DRAGANDDROP + ,dndTimer(0) +#endif + {} + void init( const QString&); + + QLineEdit *q; + QString text; + int cursor; + int cursorTimer; + QPoint tripleClick; + int tripleClickTimer; + uint frame : 1; + uint cursorVisible : 1; + uint separator : 1; + uint readOnly : 1; + uint modified : 1; + uint direction : 5; + uint dragEnabled : 1; + uint alignment : 3; + uint echoMode : 2; + uint textDirty : 1; + uint selDirty : 1; + uint validInput : 1; + int ascent; + int maxLength; + int menuId; + int hscroll; + QChar passwordChar; // obsolete + + void finishChange( int validateFromState = -1, bool setModified = TRUE ); + + const QValidator* validator; + struct MaskInputData { + enum Casemode { NoCaseMode, Upper, Lower }; + QChar maskChar; // either the separator char or the inputmask + bool separator; + Casemode caseMode; + }; + QString inputMask; + QChar blank; + MaskInputData *maskData; + inline int nextMaskBlank( int pos ) { + int c = findInMask( pos, TRUE, FALSE ); + separator |= ( c != pos ); + return ( c != -1 ? c : maxLength ); + } + inline int prevMaskBlank( int pos ) { + int c = findInMask( pos, FALSE, FALSE ); + separator |= ( c != pos ); + return ( c != -1 ? c : 0 ); + } + + void setCursorVisible( bool visible ); + + + // undo/redo handling + enum CommandType { Separator, Insert, Remove, Delete, RemoveSelection, DeleteSelection }; + struct Command { + inline Command(){} + inline Command( CommandType type, int pos, QChar c ) + :type(type),c(c),pos(pos){} + uint type : 4; + QChar c; + int pos; + }; + int undoState; + QValueVector<Command> history; + void addCommand( const Command& cmd ); + void insert( const QString& s ); + void del( bool wasBackspace = FALSE ); + void remove( int pos ); + + inline void separate() { separator = TRUE; } + inline void undo( int until = -1 ) { + if ( !isUndoAvailable() ) + return; + deselect(); + while ( undoState && undoState > until ) { + Command& cmd = history[--undoState]; + switch ( cmd.type ) { + case Insert: + text.remove( cmd.pos, 1); + cursor = cmd.pos; + break; + case Remove: + case RemoveSelection: + text.insert( cmd.pos, cmd.c ); + cursor = cmd.pos + 1; + break; + case Delete: + case DeleteSelection: + text.insert( cmd.pos, cmd.c ); + cursor = cmd.pos; + break; + case Separator: + continue; + } + if ( until < 0 && undoState ) { + Command& next = history[undoState-1]; + if ( next.type != cmd.type && next.type < RemoveSelection + && !( cmd.type >= RemoveSelection && next.type != Separator ) ) + break; + } + } + modified = ( undoState != 0 ); + textDirty = TRUE; + } + inline void redo() { + if ( !isRedoAvailable() ) + return; + deselect(); + while ( undoState < (int)history.size() ) { + Command& cmd = history[undoState++]; + switch ( cmd.type ) { + case Insert: + text.insert( cmd.pos, cmd.c ); + cursor = cmd.pos + 1; + break; + case Remove: + case Delete: + case RemoveSelection: + case DeleteSelection: + text.remove( cmd.pos, 1 ); + cursor = cmd.pos; + break; + case Separator: + continue; + } + if ( undoState < (int)history.size() ) { + Command& next = history[undoState]; + if ( next.type != cmd.type && cmd.type < RemoveSelection + && !( next.type >= RemoveSelection && cmd.type != Separator ) ) + break; + } + } + textDirty = TRUE; + } + inline bool isUndoAvailable() const { return !readOnly && undoState; } + inline bool isRedoAvailable() const { return !readOnly && undoState < (int)history.size(); } + + // bidi + inline bool isRightToLeft() const { return direction==QChar::DirON?text.isRightToLeft():(direction==QChar::DirR); } + + // selection + int selstart, selend; + inline bool allSelected() const { return !text.isEmpty() && selstart == 0 && selend == (int)text.length(); } + inline bool hasSelectedText() const { return !text.isEmpty() && selend > selstart; } + inline void deselect() { selDirty |= (selend > selstart); selstart = selend = 0; } + void removeSelectedText(); +#ifndef QT_NO_CLIPBOARD + void copy( bool clipboard = TRUE ) const; +#endif + inline bool inSelection( int x ) const + { if ( selstart >= selend ) return FALSE; + int pos = xToPos( x, QTextItem::OnCharacters ); return pos >= selstart && pos < selend; } + + // masking + void parseInputMask( const QString &maskFields ); + bool isValidInput( QChar key, QChar mask ) const; + QString maskString( uint pos, const QString &str, bool clear = FALSE ) const; + QString clearString( uint pos, uint len ) const; + QString stripString( const QString &str ) const; + int findInMask( int pos, bool forward, bool findSeparator, QChar searchChar = QChar() ) const; + + // input methods + int imstart, imend, imselstart, imselend; + bool composeMode() const { return preeditLength(); } + bool hasIMSelection() const { return imSelectionLength(); } + int preeditLength() const { return ( imend - imstart ); } + int imSelectionLength() const { return ( imselend - imselstart ); } + + // complex text layout + QTextLayout textLayout; + void updateTextLayout(); + void moveCursor( int pos, bool mark = FALSE ); + void setText( const QString& txt ); + int xToPosInternal( int x, QTextItem::CursorPosition ) const; + int xToPos( int x, QTextItem::CursorPosition = QTextItem::BetweenCharacters ) const; + inline int visualAlignment() const { return alignment ? alignment : int( isRightToLeft() ? AlignRight : AlignLeft ); } + QRect cursorRect() const; + void updateMicroFocusHint(); + +#ifndef QT_NO_DRAGANDDROP + // drag and drop + QPoint dndPos; + int dndTimer; + bool drag(); +#endif +}; + + +/*! + \class QLineEdit + \brief The QLineEdit widget is a one-line text editor. + + \ingroup basic + \mainclass + + A line edit allows the user to enter and edit a single line of + plain text with a useful collection of editing functions, + including undo and redo, cut and paste, and drag and drop. + + By changing the echoMode() of a line edit, it can also be used as + a "write-only" field, for inputs such as passwords. + + The length of the text can be constrained to maxLength(). The text + can be arbitrarily constrained using a validator() or an + inputMask(), or both. + + A related class is QTextEdit which allows multi-line, rich-text + editing. + + You can change the text with setText() or insert(). The text is + retrieved with text(); the displayed text (which may be different, + see \l{EchoMode}) is retrieved with displayText(). Text can be + selected with setSelection() or selectAll(), and the selection can + be cut(), copy()ied and paste()d. The text can be aligned with + setAlignment(). + + When the text changes the textChanged() signal is emitted; when + the Return or Enter key is pressed the returnPressed() signal is + emitted. Note that if there is a validator set on the line edit, + the returnPressed() signal will only be emitted if the validator + returns \c Acceptable. + + By default, QLineEdits have a frame as specified by the Windows + and Motif style guides; you can turn it off by calling + setFrame(FALSE). + + The default key bindings are described below. The line edit also + provides a context menu (usually invoked by a right mouse click) + that presents some of these editing options. + \target desc + \table + \header \i Keypress \i Action + \row \i Left Arrow \i Moves the cursor one character to the left. + \row \i Shift+Left Arrow \i Moves and selects text one character to the left. + \row \i Right Arrow \i Moves the cursor one character to the right. + \row \i Shift+Right Arrow \i Moves and selects text one character to the right. + \row \i Home \i Moves the cursor to the beginning of the line. + \row \i End \i Moves the cursor to the end of the line. + \row \i Backspace \i Deletes the character to the left of the cursor. + \row \i Ctrl+Backspace \i Deletes the word to the left of the cursor. + \row \i Delete \i Deletes the character to the right of the cursor. + \row \i Ctrl+Delete \i Deletes the word to the right of the cursor. + \row \i Ctrl+A \i Moves the cursor to the beginning of the line. + \row \i Ctrl+B \i Moves the cursor one character to the left. + \row \i Ctrl+C \i Copies the selected text to the clipboard. + (Windows also supports Ctrl+Insert for this operation.) + \row \i Ctrl+D \i Deletes the character to the right of the cursor. + \row \i Ctrl+E \i Moves the cursor to the end of the line. + \row \i Ctrl+F \i Moves the cursor one character to the right. + \row \i Ctrl+H \i Deletes the character to the left of the cursor. + \row \i Ctrl+K \i Deletes to the end of the line. + \row \i Ctrl+V \i Pastes the clipboard text into line edit. + (Windows also supports Shift+Insert for this operation.) + \row \i Ctrl+X \i Deletes the selected text and copies it to the clipboard. + (Windows also supports Shift+Delete for this operation.) + \row \i Ctrl+Z \i Undoes the last operation. + \row \i Ctrl+Y \i Redoes the last undone operation. + \endtable + + Any other key sequence that represents a valid character, will + cause the character to be inserted into the line edit. + + <img src=qlined-m.png> <img src=qlined-w.png> + + \sa QTextEdit QLabel QComboBox + \link guibooks.html#fowler GUI Design Handbook: Field, Entry\endlink +*/ + + +/*! + \fn void QLineEdit::textChanged( const QString& ) + + This signal is emitted whenever the text changes. The argument is + the new text. +*/ + +/*! + \fn void QLineEdit::selectionChanged() + + This signal is emitted whenever the selection changes. + + \sa hasSelectedText(), selectedText() +*/ + +/*! + \fn void QLineEdit::lostFocus() + + This signal is emitted when the line edit has lost focus. + + \sa hasFocus(), QWidget::focusInEvent(), QWidget::focusOutEvent() +*/ + + + +/*! + Constructs a line edit with no text. + + The maximum text length is set to 32767 characters. + + The \a parent and \a name arguments are sent to the QWidget constructor. + + \sa setText(), setMaxLength() +*/ + +QLineEdit::QLineEdit( QWidget* parent, const char* name ) + : QFrame( parent, name, WNoAutoErase ), d(new QLineEditPrivate( this )) +{ + d->init( QString::null ); +} + +/*! + Constructs a line edit containing the text \a contents. + + The cursor position is set to the end of the line and the maximum + text length to 32767 characters. + + The \a parent and \a name arguments are sent to the QWidget + constructor. + + \sa text(), setMaxLength() +*/ + +QLineEdit::QLineEdit( const QString& contents, QWidget* parent, const char* name ) + : QFrame( parent, name, WNoAutoErase ), d(new QLineEditPrivate( this )) +{ + d->init( contents ); +} + +/*! + Constructs a line edit with an input \a inputMask and the text \a + contents. + + The cursor position is set to the end of the line and the maximum + text length is set to the length of the mask (the number of mask + characters and separators). + + The \a parent and \a name arguments are sent to the QWidget + constructor. + + \sa setMask() text() +*/ +QLineEdit::QLineEdit( const QString& contents, const QString &inputMask, QWidget* parent, const char* name ) + : QFrame( parent, name, WNoAutoErase ), d(new QLineEditPrivate( this )) +{ + d->parseInputMask( inputMask ); + if ( d->maskData ) { + QString ms = d->maskString( 0, contents ); + d->init( ms + d->clearString( ms.length(), d->maxLength - ms.length() ) ); + d->cursor = d->nextMaskBlank( ms.length() ); + } else { + d->init( contents ); + } +} + +/*! + Destroys the line edit. +*/ + +QLineEdit::~QLineEdit() +{ + delete [] d->maskData; + delete d; +} + + +/*! + \property QLineEdit::text + \brief the line edit's text + + Note that setting this property clears the selection, clears the + undo/redo history, moves the cursor to the end of the line and + resets the \c modified property to FALSE. The text is not + validated when inserted with setText(). + + The text is truncated to maxLength() length. + + \sa insert() +*/ +QString QLineEdit::text() const +{ + QString res = d->text; + if ( d->maskData ) + res = d->stripString( d->text ); + return ( res.isNull() ? QString::fromLatin1("") : res ); +} + +void QLineEdit::setText( const QString& text) +{ + resetInputContext(); + d->setText( text ); + d->modified = FALSE; + d->finishChange( -1, FALSE ); +} + + +/*! + \property QLineEdit::displayText + \brief the displayed text + + If \c EchoMode is \c Normal this returns the same as text(); if + \c EchoMode is \c Password it returns a string of asterisks + text().length() characters long, e.g. "******"; if \c EchoMode is + \c NoEcho returns an empty string, "". + + \sa setEchoMode() text() EchoMode +*/ + +QString QLineEdit::displayText() const +{ + if ( d->echoMode == NoEcho ) + return QString::fromLatin1(""); + QString res = d->text; + if ( d->echoMode == Password ) + res.fill( passwordChar() ); + return ( res.isNull() ? QString::fromLatin1("") : res ); +} + + +/*! + \property QLineEdit::maxLength + \brief the maximum permitted length of the text + + If the text is too long, it is truncated at the limit. + + If truncation occurs any selected text will be unselected, the + cursor position is set to 0 and the first part of the string is + shown. + + If the line edit has an input mask, the mask defines the maximum + string length. + + \sa inputMask +*/ + +int QLineEdit::maxLength() const +{ + return d->maxLength; +} + +void QLineEdit::setMaxLength( int maxLength ) +{ + if ( d->maskData ) + return; + d->maxLength = maxLength; + setText( d->text ); +} + + + +/*! + \property QLineEdit::frame + \brief whether the line edit draws itself with a frame + + If enabled (the default) the line edit draws itself inside a + two-pixel frame, otherwise the line edit draws itself without any + frame. +*/ +bool QLineEdit::frame() const +{ + return frameShape() != NoFrame; +} + + +void QLineEdit::setFrame( bool enable ) +{ + setFrameStyle( enable ? ( LineEditPanel | Sunken ) : NoFrame ); +} + + +/*! + \enum QLineEdit::EchoMode + + This enum type describes how a line edit should display its + contents. + + \value Normal Display characters as they are entered. This is the + default. + \value NoEcho Do not display anything. This may be appropriate + for passwords where even the length of the + password should be kept secret. + \value Password Display asterisks instead of the characters + actually entered. + + \sa setEchoMode() echoMode() +*/ + + +/*! + \property QLineEdit::echoMode + \brief the line edit's echo mode + + The initial setting is \c Normal, but QLineEdit also supports \c + NoEcho and \c Password modes. + + The widget's display and the ability to copy or drag the text is + affected by this setting. + + \sa EchoMode displayText() +*/ + +QLineEdit::EchoMode QLineEdit::echoMode() const +{ + return (EchoMode) d->echoMode; +} + +void QLineEdit::setEchoMode( EchoMode mode ) +{ + if (mode == (EchoMode)d->echoMode) + return; + d->echoMode = mode; + d->updateTextLayout(); + setInputMethodEnabled( mode == Normal ); + update(); +} + + + +/*! + Returns a pointer to the current input validator, or 0 if no + validator has been set. + + \sa setValidator() +*/ + +const QValidator * QLineEdit::validator() const +{ + return d->validator; +} + +/*! + Sets this line edit to only accept input that the validator, \a v, + will accept. This allows you to place any arbitrary constraints on + the text which may be entered. + + If \a v == 0, setValidator() removes the current input validator. + The initial setting is to have no input validator (i.e. any input + is accepted up to maxLength()). + + \sa validator() QIntValidator QDoubleValidator QRegExpValidator +*/ + +void QLineEdit::setValidator( const QValidator *v ) +{ + if ( d->validator ) + disconnect( (QObject*)d->validator, SIGNAL( destroyed() ), + this, SLOT( clearValidator() ) ); + d->validator = v; + if ( d->validator ) + connect( (QObject*)d->validator, SIGNAL( destroyed() ), + this, SLOT( clearValidator() ) ); +} + + + +/*! + Returns a recommended size for the widget. + + The width returned, in pixels, is usually enough for about 15 to + 20 characters. +*/ + +QSize QLineEdit::sizeHint() const +{ + constPolish(); + QFontMetrics fm( font() ); + int h = QMAX(fm.lineSpacing(), 14) + 2*innerMargin; + int w = fm.width( 'x' ) * 17; // "some" + int m = frameWidth() * 2; + return (style().sizeFromContents(QStyle::CT_LineEdit, this, + QSize( w + m, h + m ). + expandedTo(QApplication::globalStrut()))); +} + + +/*! + Returns a minimum size for the line edit. + + The width returned is enough for at least one character. +*/ + +QSize QLineEdit::minimumSizeHint() const +{ + constPolish(); + QFontMetrics fm = fontMetrics(); + int h = fm.height() + QMAX( 2*innerMargin, fm.leading() ); + int w = fm.maxWidth(); + int m = frameWidth() * 2; + return QSize( w + m, h + m ); +} + + +/*! + \property QLineEdit::cursorPosition + \brief the current cursor position for this line edit + + Setting the cursor position causes a repaint when appropriate. +*/ + +int QLineEdit::cursorPosition() const +{ + return d->cursor; +} + + +void QLineEdit::setCursorPosition( int pos ) +{ + if (pos < 0) + pos = 0; + + if ( pos <= (int) d->text.length() ) + d->moveCursor( pos ); +} + + +/*! \obsolete Use setText(), setCursorPosition() and setSelection() instead. +*/ + +bool QLineEdit::validateAndSet( const QString &newText, int newPos, + int newMarkAnchor, int newMarkDrag ) +{ + int priorState = d->undoState; + d->selstart = 0; + d->selend = d->text.length(); + d->removeSelectedText(); + d->insert( newText ); + d->finishChange( priorState ); + if ( d->undoState > priorState ) { + d->cursor = newPos; + d->selstart = QMIN( newMarkAnchor, newMarkDrag ); + d->selend = QMAX( newMarkAnchor, newMarkDrag ); + d->updateMicroFocusHint(); + update(); + return TRUE; + } + return FALSE; +} + + +/*! + \property QLineEdit::alignment + \brief the alignment of the line edit + + Possible Values are \c Qt::AlignAuto, \c Qt::AlignLeft, \c + Qt::AlignRight and \c Qt::AlignHCenter. + + Attempting to set the alignment to an illegal flag combination + does nothing. + + \sa Qt::AlignmentFlags +*/ + +int QLineEdit::alignment() const +{ + return d->alignment; +} + +void QLineEdit::setAlignment( int flag ) +{ + d->alignment = flag & 0x7; + update(); +} + + +/*! + \obsolete + \fn void QLineEdit::cursorRight( bool, int ) + + Use cursorForward() instead. + + \sa cursorForward() +*/ + +/*! + \obsolete + \fn void QLineEdit::cursorLeft( bool, int ) + For compatibilty with older applications only. Use cursorBackward() + instead. + \sa cursorBackward() +*/ + +/*! + Moves the cursor forward \a steps characters. If \a mark is TRUE + each character moved over is added to the selection; if \a mark is + FALSE the selection is cleared. + + \sa cursorBackward() +*/ + +void QLineEdit::cursorForward( bool mark, int steps ) +{ + int cursor = d->cursor; + if ( steps > 0 ) { + while( steps-- ) + cursor = d->textLayout.nextCursorPosition( cursor ); + } else if ( steps < 0 ) { + while ( steps++ ) + cursor = d->textLayout.previousCursorPosition( cursor ); + } + d->moveCursor( cursor, mark ); +} + + +/*! + Moves the cursor back \a steps characters. If \a mark is TRUE each + character moved over is added to the selection; if \a mark is + FALSE the selection is cleared. + + \sa cursorForward() +*/ +void QLineEdit::cursorBackward( bool mark, int steps ) +{ + cursorForward( mark, -steps ); +} + +/*! + Moves the cursor one word forward. If \a mark is TRUE, the word is + also selected. + + \sa cursorWordBackward() +*/ +void QLineEdit::cursorWordForward( bool mark ) +{ + d->moveCursor( d->textLayout.nextCursorPosition(d->cursor, QTextLayout::SkipWords), mark ); +} + +/*! + Moves the cursor one word backward. If \a mark is TRUE, the word + is also selected. + + \sa cursorWordForward() +*/ + +void QLineEdit::cursorWordBackward( bool mark ) +{ + d->moveCursor( d->textLayout.previousCursorPosition(d->cursor, QTextLayout::SkipWords), mark ); +} + + +/*! + If no text is selected, deletes the character to the left of the + text cursor and moves the cursor one position to the left. If any + text is selected, the cursor is moved to the beginning of the + selected text and the selected text is deleted. + + \sa del() +*/ +void QLineEdit::backspace() +{ + int priorState = d->undoState; + if ( d->hasSelectedText() ) { + d->removeSelectedText(); + } else if ( d->cursor ) { + --d->cursor; + if ( d->maskData ) + d->cursor = d->prevMaskBlank( d->cursor ); + d->del( TRUE ); + } + d->finishChange( priorState ); +} + +/*! + If no text is selected, deletes the character to the right of the + text cursor. If any text is selected, the cursor is moved to the + beginning of the selected text and the selected text is deleted. + + \sa backspace() +*/ + +void QLineEdit::del() +{ + int priorState = d->undoState; + if ( d->hasSelectedText() ) { + d->removeSelectedText(); + } else { + int n = d->textLayout.nextCursorPosition( d->cursor ) - d->cursor; + while ( n-- ) + d->del(); + } + d->finishChange( priorState ); +} + +/*! + Moves the text cursor to the beginning of the line unless it is + already there. If \a mark is TRUE, text is selected towards the + first position; otherwise, any selected text is unselected if the + cursor is moved. + + \sa end() +*/ + +void QLineEdit::home( bool mark ) +{ + d->moveCursor( 0, mark ); +} + +/*! + Moves the text cursor to the end of the line unless it is already + there. If \a mark is TRUE, text is selected towards the last + position; otherwise, any selected text is unselected if the cursor + is moved. + + \sa home() +*/ + +void QLineEdit::end( bool mark ) +{ + d->moveCursor( d->text.length(), mark ); +} + + +/*! + \property QLineEdit::modified + \brief whether the line edit's contents has been modified by the user + + The modified flag is never read by QLineEdit; it has a default value + of FALSE and is changed to TRUE whenever the user changes the line + edit's contents. + + This is useful for things that need to provide a default value but + do not start out knowing what the default should be (perhaps it + depends on other fields on the form). Start the line edit without + the best default, and when the default is known, if modified() + returns FALSE (the user hasn't entered any text), insert the + default value. + + Calling clearModified() or setText() resets the modified flag to + FALSE. +*/ + +bool QLineEdit::isModified() const +{ + return d->modified; +} + +/*! + Resets the modified flag to FALSE. + + \sa isModified() +*/ +void QLineEdit::clearModified() +{ + d->modified = FALSE; + d->history.clear(); + d->undoState = 0; +} + +/*! + \obsolete + \property QLineEdit::edited + \brief whether the line edit has been edited. Use modified instead. +*/ +bool QLineEdit::edited() const { return d->modified; } +void QLineEdit::setEdited( bool on ) { d->modified = on; } + +/*! + \obsolete + \property QLineEdit::hasMarkedText + \brief whether part of the text has been selected by the user. Use hasSelectedText instead. +*/ + +/*! + \property QLineEdit::hasSelectedText + \brief whether there is any text selected + + hasSelectedText() returns TRUE if some or all of the text has been + selected by the user; otherwise returns FALSE. + + \sa selectedText() +*/ + + +bool QLineEdit::hasSelectedText() const +{ + return d->hasSelectedText(); +} + +/*! + \obsolete + \property QLineEdit::markedText + \brief the text selected by the user. Use selectedText instead. +*/ + +/*! + \property QLineEdit::selectedText + \brief the selected text + + If there is no selected text this property's value is + QString::null. + + \sa hasSelectedText() +*/ + +QString QLineEdit::selectedText() const +{ + if ( d->hasSelectedText() ) + return d->text.mid( d->selstart, d->selend - d->selstart ); + return QString::null; +} + +/*! + selectionStart() returns the index of the first selected character in the + line edit or -1 if no text is selected. + + \sa selectedText() +*/ + +int QLineEdit::selectionStart() const +{ + return d->hasSelectedText() ? d->selstart : -1; +} + +/*! \obsolete use selectedText(), selectionStart() */ +bool QLineEdit::getSelection( int *start, int *end ) +{ + if ( d->hasSelectedText() && start && end ) { + *start = d->selstart; + *end = d->selend; + return TRUE; + } + return FALSE; +} + + +/*! + Selects text from position \a start and for \a length characters. + + Note that this function sets the cursor's position to the end of + the selection regardless of its current position. + + \sa deselect() selectAll() getSelection() cursorForward() cursorBackward() +*/ + +void QLineEdit::setSelection( int start, int length ) +{ + if ( start < 0 || start > (int)d->text.length() || length < 0 ) { + d->selstart = d->selend = 0; + } else { + d->selstart = start; + d->selend = QMIN( start + length, (int)d->text.length() ); + d->cursor = d->selend; + } + update(); +} + + +/*! + \property QLineEdit::undoAvailable + \brief whether undo is available +*/ + +bool QLineEdit::isUndoAvailable() const +{ + return d->isUndoAvailable(); +} + +/*! + \property QLineEdit::redoAvailable + \brief whether redo is available +*/ + +bool QLineEdit::isRedoAvailable() const +{ + return d->isRedoAvailable(); +} + +/*! + \property QLineEdit::dragEnabled + \brief whether the lineedit starts a drag if the user presses and + moves the mouse on some selected text +*/ + +bool QLineEdit::dragEnabled() const +{ + return d->dragEnabled; +} + +void QLineEdit::setDragEnabled( bool b ) +{ + d->dragEnabled = b; +} + +/*! + \property QLineEdit::acceptableInput + \brief whether the input satisfies the inputMask and the + validator. + + \sa setInputMask(), setValidator() +*/ +bool QLineEdit::hasAcceptableInput() const +{ +#ifndef QT_NO_VALIDATOR + QString text = d->text; + int cursor = d->cursor; + if ( d->validator && d->validator->validate( text, cursor ) != QValidator::Acceptable ) + return FALSE; +#endif + + if ( !d->maskData ) + return TRUE; + + if ( d->text.length() != (uint)d->maxLength ) + return FALSE; + + for ( uint i=0; i < (uint)d->maxLength; i++) { + if ( d->maskData[i].separator ) { + if ( d->text[(int)i] != d->maskData[i].maskChar ) + return FALSE; + } else { + if ( !d->isValidInput( d->text[(int)i], d->maskData[i].maskChar ) ) + return FALSE; + } + } + return TRUE; +} + + +/*! + \property QLineEdit::inputMask + \brief The validation input mask + + If no mask is set, inputMask() returns QString::null. + + Sets the QLineEdit's validation mask. Validators can be used + instead of, or in conjunction with masks; see setValidator(). + + Unset the mask and return to normal QLineEdit operation by passing + an empty string ("") or just calling setInputMask() with no + arguments. + + The mask format understands these mask characters: + \table + \header \i Character \i Meaning + \row \i \c A \i ASCII alphabetic character required. A-Z, a-z. + \row \i \c a \i ASCII alphabetic character permitted but not required. + \row \i \c N \i ASCII alphanumeric character required. A-Z, a-z, 0-9. + \row \i \c n \i ASCII alphanumeric character permitted but not required. + \row \i \c X \i Any character required. + \row \i \c x \i Any character permitted but not required. + \row \i \c 9 \i ASCII digit required. 0-9. + \row \i \c 0 \i ASCII digit permitted but not required. + \row \i \c D \i ASCII digit required. 1-9. + \row \i \c d \i ASCII digit permitted but not required (1-9). + \row \i \c # \i ASCII digit or plus/minus sign permitted but not required. + \row \i \c > \i All following alphabetic characters are uppercased. + \row \i \c < \i All following alphabetic characters are lowercased. + \row \i \c ! \i Switch off case conversion. + \row \i <tt>\\</tt> \i Use <tt>\\</tt> to escape the special + characters listed above to use them as + separators. + \endtable + + The mask consists of a string of mask characters and separators, + optionally followed by a semi-colon and the character used for + blanks: the blank characters are always removed from the text + after editing. The default blank character is space. + + Examples: + \table + \header \i Mask \i Notes + \row \i \c 000.000.000.000;_ \i IP address; blanks are \c{_}. + \row \i \c 0000-00-00 \i ISO Date; blanks are \c space + \row \i \c >AAAAA-AAAAA-AAAAA-AAAAA-AAAAA;# \i License number; + blanks are \c - and all (alphabetic) characters are converted to + uppercase. + \endtable + + To get range control (e.g. for an IP address) use masks together + with \link setValidator() validators\endlink. + + \sa maxLength +*/ +QString QLineEdit::inputMask() const +{ + return ( d->maskData ? d->inputMask + ';' + d->blank : QString::null ); +} + +void QLineEdit::setInputMask( const QString &inputMask ) +{ + d->parseInputMask( inputMask ); + if ( d->maskData ) + d->moveCursor( d->nextMaskBlank( 0 ) ); +} + +/*! + Selects all the text (i.e. highlights it) and moves the cursor to + the end. This is useful when a default value has been inserted + because if the user types before clicking on the widget, the + selected text will be deleted. + + \sa setSelection() deselect() +*/ + +void QLineEdit::selectAll() +{ + d->selstart = d->selend = d->cursor = 0; + d->moveCursor( d->text.length(), TRUE ); +} + +/*! + Deselects any selected text. + + \sa setSelection() selectAll() +*/ + +void QLineEdit::deselect() +{ + d->deselect(); + d->finishChange(); +} + + +/*! + This slot is equivalent to setValidator(0). +*/ + +void QLineEdit::clearValidator() +{ + setValidator( 0 ); +} + +/*! + Deletes any selected text, inserts \a newText, and validates the + result. If it is valid, it sets it as the new contents of the line + edit. +*/ +void QLineEdit::insert( const QString &newText ) +{ +// q->resetInputContext(); //#### FIX ME IN QT + int priorState = d->undoState; + d->removeSelectedText(); + d->insert( newText ); + d->finishChange( priorState ); +} + +/*! + Clears the contents of the line edit. +*/ +void QLineEdit::clear() +{ + int priorState = d->undoState; + resetInputContext(); + d->selstart = 0; + d->selend = d->text.length(); + d->removeSelectedText(); + d->separate(); + d->finishChange( priorState ); +} + +/*! + Undoes the last operation if undo is \link + QLineEdit::undoAvailable available\endlink. Deselects any current + selection, and updates the selection start to the current cursor + position. +*/ +void QLineEdit::undo() +{ + resetInputContext(); + d->undo(); + d->finishChange( -1, FALSE ); +} + +/*! + Redoes the last operation if redo is \link + QLineEdit::redoAvailable available\endlink. +*/ +void QLineEdit::redo() +{ + resetInputContext(); + d->redo(); + d->finishChange(); +} + + +/*! + \property QLineEdit::readOnly + \brief whether the line edit is read only. + + In read-only mode, the user can still copy the text to the + clipboard or drag-and-drop the text (if echoMode() is \c Normal), + but cannot edit it. + + QLineEdit does not show a cursor in read-only mode. + + \sa setEnabled() +*/ + +bool QLineEdit::isReadOnly() const +{ + return d->readOnly; +} + +void QLineEdit::setReadOnly( bool enable ) +{ + d->readOnly = enable; +#ifndef QT_NO_CURSOR + setCursor( enable ? arrowCursor : ibeamCursor ); +#endif + update(); +} + + +#ifndef QT_NO_CLIPBOARD +/*! + Copies the selected text to the clipboard and deletes it, if there + is any, and if echoMode() is \c Normal. + + If the current validator disallows deleting the selected text, + cut() will copy without deleting. + + \sa copy() paste() setValidator() +*/ + +void QLineEdit::cut() +{ + if ( hasSelectedText() ) { + copy(); + del(); + } +} + + +/*! + Copies the selected text to the clipboard, if there is any, and if + echoMode() is \c Normal. + + \sa cut() paste() +*/ + +void QLineEdit::copy() const +{ + d->copy(); +} + +/*! + Inserts the clipboard's text at the cursor position, deleting any + selected text, providing the line edit is not \link + QLineEdit::readOnly read-only\endlink. + + If the end result would not be acceptable to the current + \link setValidator() validator\endlink, nothing happens. + + \sa copy() cut() +*/ + +void QLineEdit::paste() +{ + insert( QApplication::clipboard()->text( QClipboard::Clipboard ) ); +} + +void QLineEditPrivate::copy( bool clipboard ) const +{ + QString t = q->selectedText(); + if ( !t.isEmpty() && echoMode == QLineEdit::Normal ) { + q->disconnect( QApplication::clipboard(), SIGNAL(selectionChanged()), q, 0); + QApplication::clipboard()->setText( t, clipboard ? QClipboard::Clipboard : QClipboard::Selection ); + q->connect( QApplication::clipboard(), SIGNAL(selectionChanged()), + q, SLOT(clipboardChanged()) ); + } +} + +#endif // !QT_NO_CLIPBOARD + +/*!\reimp +*/ + +void QLineEdit::resizeEvent( QResizeEvent *e ) +{ + QFrame::resizeEvent( e ); +} + +/*! \reimp +*/ +bool QLineEdit::event( QEvent * e ) +{ + if ( e->type() == QEvent::AccelOverride && !d->readOnly ) { + QKeyEvent* ke = (QKeyEvent*) e; + if ( ke->state() == NoButton || ke->state() == ShiftButton + || ke->state() == Keypad ) { + if ( ke->key() < Key_Escape ) { + ke->accept(); + } else { + switch ( ke->key() ) { + case Key_Delete: + case Key_Home: + case Key_End: + case Key_Backspace: + case Key_Left: + case Key_Right: + ke->accept(); + default: + break; + } + } + } else if ( ke->state() & ControlButton ) { + switch ( ke->key() ) { +// Those are too frequently used for application functionality +/* case Key_A: + case Key_B: + case Key_D: + case Key_E: + case Key_F: + case Key_H: + case Key_K: +*/ + case Key_C: + case Key_V: + case Key_X: + case Key_Y: + case Key_Z: + case Key_Left: + case Key_Right: +#if defined (Q_WS_WIN) + case Key_Insert: + case Key_Delete: +#endif + ke->accept(); + default: + break; + } + } + } else if ( e->type() == QEvent::Timer ) { + // should be timerEvent, is here for binary compatibility + int timerId = ((QTimerEvent*)e)->timerId(); + if ( timerId == d->cursorTimer ) { + if(!hasSelectedText() || style().styleHint( QStyle::SH_BlinkCursorWhenTextSelected )) + d->setCursorVisible( !d->cursorVisible ); +#ifndef QT_NO_DRAGANDDROP + } else if ( timerId == d->dndTimer ) { + if( !d->drag() ) + return TRUE; +#endif + } else if ( timerId == d->tripleClickTimer ) { + killTimer( d->tripleClickTimer ); + d->tripleClickTimer = 0; + } + } + return QWidget::event( e ); +} + +/*! \reimp +*/ +void QLineEdit::mousePressEvent( QMouseEvent* e ) +{ + if ( sendMouseEventToInputContext( e ) ) + return; + if ( e->button() == RightButton ) + return; + if ( d->tripleClickTimer && ( e->pos() - d->tripleClick ).manhattanLength() < + QApplication::startDragDistance() ) { + selectAll(); + return; + } + bool mark = e->state() & ShiftButton; + int cursor = d->xToPos( e->pos().x() ); +#ifndef QT_NO_DRAGANDDROP + if ( !mark && d->dragEnabled && d->echoMode == Normal && + e->button() == LeftButton && d->inSelection( e->pos().x() ) ) { + d->cursor = cursor; + d->updateMicroFocusHint(); + update(); + d->dndPos = e->pos(); + if ( !d->dndTimer ) + d->dndTimer = startTimer( QApplication::startDragTime() ); + } else +#endif + { + d->moveCursor( cursor, mark ); + } +} + +/*! \reimp +*/ +void QLineEdit::mouseMoveEvent( QMouseEvent * e ) +{ + if ( sendMouseEventToInputContext( e ) ) + return; +#ifndef QT_NO_CURSOR + if ( ( e->state() & MouseButtonMask ) == 0 ) { + if ( !d->readOnly && d->dragEnabled +#ifndef QT_NO_WHATSTHIS + && !QWhatsThis::inWhatsThisMode() +#endif + ) + setCursor( ( d->inSelection( e->pos().x() ) ? arrowCursor : ibeamCursor ) ); + } +#endif + + if ( e->state() & LeftButton ) { +#ifndef QT_NO_DRAGANDDROP + if ( d->dndTimer ) { + if ( ( d->dndPos - e->pos() ).manhattanLength() > QApplication::startDragDistance() ) + d->drag(); + } else +#endif + { + d->moveCursor( d->xToPos( e->pos().x() ), TRUE ); + } + } +} + +/*! \reimp +*/ +void QLineEdit::mouseReleaseEvent( QMouseEvent* e ) +{ + if ( sendMouseEventToInputContext( e ) ) + return; +#ifndef QT_NO_DRAGANDDROP + if ( e->button() == LeftButton ) { + if ( d->dndTimer ) { + killTimer( d->dndTimer ); + d->dndTimer = 0; + deselect(); + return; + } + } +#endif +#ifndef QT_NO_CLIPBOARD + if (QApplication::clipboard()->supportsSelection() ) { + if ( e->button() == LeftButton ) { + d->copy( FALSE ); + } else if ( !d->readOnly && e->button() == MidButton ) { + d->deselect(); + insert( QApplication::clipboard()->text( QClipboard::Selection ) ); + } + } +#endif +} + +/*! \reimp +*/ +void QLineEdit::mouseDoubleClickEvent( QMouseEvent* e ) +{ + if ( sendMouseEventToInputContext( e ) ) + return; + if ( e->button() == Qt::LeftButton ) { + deselect(); + d->cursor = d->xToPos( e->pos().x() ); + d->cursor = d->textLayout.previousCursorPosition( d->cursor, QTextLayout::SkipWords ); + // ## text layout should support end of words. + int end = d->textLayout.nextCursorPosition( d->cursor, QTextLayout::SkipWords ); + while ( end > d->cursor && d->text[end-1].isSpace() ) + --end; + d->moveCursor( end, TRUE ); + d->tripleClickTimer = startTimer( QApplication::doubleClickInterval() ); + d->tripleClick = e->pos(); + } +} + +/*! + \fn void QLineEdit::returnPressed() + + This signal is emitted when the Return or Enter key is pressed. + Note that if there is a validator() or inputMask() set on the line + edit, the returnPressed() signal will only be emitted if the input + follows the inputMask() and the validator() returns \c Acceptable. +*/ + +/*! + Converts key press event \a e into a line edit action. + + If Return or Enter is pressed and the current text is valid (or + can be \link QValidator::fixup() made valid\endlink by the + validator), the signal returnPressed() is emitted. + + The default key bindings are listed in the \link #desc detailed + description.\endlink +*/ + +void QLineEdit::keyPressEvent( QKeyEvent * e ) +{ + d->setCursorVisible( TRUE ); + if ( e->key() == Key_Enter || e->key() == Key_Return ) { + const QValidator * v = d->validator; + if ( hasAcceptableInput() ) { + emit returnPressed(); + } +#ifndef QT_NO_VALIDATOR + else if ( v && v->validate( d->text, d->cursor ) != QValidator::Acceptable ) { + QString vstr = d->text; + v->fixup( vstr ); + if ( vstr != d->text ) { + setText( vstr ); + if ( hasAcceptableInput() ) + emit returnPressed(); + } + } +#endif + e->ignore(); + return; + } + if ( !d->readOnly ) { + QString t = e->text(); + if ( !t.isEmpty() && (!e->ascii() || e->ascii()>=32) && + e->key() != Key_Delete && + e->key() != Key_Backspace ) { +#ifdef Q_WS_X11 + extern bool qt_hebrew_keyboard_hack; + if ( qt_hebrew_keyboard_hack ) { + // the X11 keyboard layout is broken and does not reverse + // braces correctly. This is a hack to get halfway correct + // behaviour + if ( d->isRightToLeft() ) { + QChar *c = (QChar *)t.unicode(); + int l = t.length(); + while( l-- ) { + if ( c->mirrored() ) + *c = c->mirroredChar(); + c++; + } + } + } +#endif + insert( t ); + return; + } + } + bool unknown = FALSE; + if ( e->state() & ControlButton ) { + switch ( e->key() ) { + case Key_A: +#if defined(Q_WS_X11) + home( e->state() & ShiftButton ); +#else + selectAll(); +#endif + break; + case Key_B: + cursorForward( e->state() & ShiftButton, -1 ); + break; +#ifndef QT_NO_CLIPBOARD + case Key_C: + copy(); + break; +#endif + case Key_D: + if ( !d->readOnly ) { + del(); + } + break; + case Key_E: + end( e->state() & ShiftButton ); + break; + case Key_F: + cursorForward( e->state() & ShiftButton, 1 ); + break; + case Key_H: + if ( !d->readOnly ) { + backspace(); + } + break; + case Key_K: + if ( !d->readOnly ) { + int priorState = d->undoState; + d->deselect(); + while ( d->cursor < (int) d->text.length() ) + d->del(); + d->finishChange( priorState ); + } + break; +#if defined(Q_WS_X11) + case Key_U: + if ( !d->readOnly ) + clear(); + break; +#endif +#ifndef QT_NO_CLIPBOARD + case Key_V: + if ( !d->readOnly ) + paste(); + break; + case Key_X: + if ( !d->readOnly && d->hasSelectedText() && echoMode() == Normal ) { + copy(); + del(); + } + break; +#if defined (Q_WS_WIN) + case Key_Insert: + copy(); + break; +#endif +#endif + case Key_Delete: + if ( !d->readOnly ) { + cursorWordForward( TRUE ); + del(); + } + break; + case Key_Backspace: + if ( !d->readOnly ) { + cursorWordBackward( TRUE ); + del(); + } + break; + case Key_Right: + case Key_Left: + if ( d->isRightToLeft() == (e->key() == Key_Right) ) { + if ( echoMode() == Normal ) + cursorWordBackward( e->state() & ShiftButton ); + else + home( e->state() & ShiftButton ); + } else { + if ( echoMode() == Normal ) + cursorWordForward( e->state() & ShiftButton ); + else + end( e->state() & ShiftButton ); + } + break; + case Key_Z: + if ( !d->readOnly ) { + if(e->state() & ShiftButton) + redo(); + else + undo(); + } + break; + case Key_Y: + if ( !d->readOnly ) + redo(); + break; + default: + unknown = TRUE; + } + } else { // ### check for *no* modifier + switch ( e->key() ) { + case Key_Shift: + // ### TODO + break; + case Key_Left: + case Key_Right: { + int step = (d->isRightToLeft() == (e->key() == Key_Right)) ? -1 : 1; + cursorForward( e->state() & ShiftButton, step ); + } + break; + case Key_Backspace: + if ( !d->readOnly ) { + backspace(); + } + break; + case Key_Home: +#ifdef Q_WS_MACX + case Key_Up: +#endif + home( e->state() & ShiftButton ); + break; + case Key_End: +#ifdef Q_WS_MACX + case Key_Down: +#endif + end( e->state() & ShiftButton ); + break; + case Key_Delete: + if ( !d->readOnly ) { +#if defined (Q_WS_WIN) + if ( e->state() & ShiftButton ) { + cut(); + break; + } +#endif + del(); + } + break; +#if defined (Q_WS_WIN) + case Key_Insert: + if ( !d->readOnly && e->state() & ShiftButton ) + paste(); + else + unknown = TRUE; + break; +#endif + case Key_F14: // Undo key on Sun keyboards + if ( !d->readOnly ) + undo(); + break; +#ifndef QT_NO_CLIPBOARD + case Key_F16: // Copy key on Sun keyboards + copy(); + break; + case Key_F18: // Paste key on Sun keyboards + if ( !d->readOnly ) + paste(); + break; + case Key_F20: // Cut key on Sun keyboards + if ( !d->readOnly && hasSelectedText() && echoMode() == Normal ) { + copy(); + del(); + } + break; +#endif + default: + unknown = TRUE; + } + } + if ( e->key() == Key_Direction_L || e->key() == Key_Direction_R ) { + d->direction = (e->key() == Key_Direction_L) ? QChar::DirL : QChar::DirR; + d->updateTextLayout(); + update(); + } + + if ( unknown ) + e->ignore(); +} + + +/*! + This function is not intended as polymorphic usage. Just a shared code + fragment that calls QWidget::sendMouseEventToInputContext() easily for this + class. + */ +bool QLineEdit::sendMouseEventToInputContext( QMouseEvent *e ) +{ +#ifndef QT_NO_IM + if ( d->composeMode() ) { + int cursor = d->xToPosInternal( e->pos().x(), QTextItem::OnCharacters ); + int mousePos = cursor - d->imstart; + if ( mousePos >= 0 && mousePos < d->preeditLength() ) { + QWidget::sendMouseEventToInputContext( mousePos, e->type(), + e->button(), e->state() ); + } else if ( e->type() != QEvent::MouseMove ) { + // send button events on out of preedit + QWidget::sendMouseEventToInputContext( -1, e->type(), + e->button(), e->state() ); + } + return TRUE; + } +#endif + return FALSE; +} + + +/*! \reimp + */ +void QLineEdit::imStartEvent( QIMEvent *e ) +{ + if ( d->readOnly ) { + e->ignore(); + return; + } + d->removeSelectedText(); + d->updateMicroFocusHint(); + d->imstart = d->imend = d->imselstart = d->imselend = d->cursor; +} + +/*! \reimp + */ +void QLineEdit::imComposeEvent( QIMEvent *e ) +{ + if ( d->readOnly ) { + e->ignore(); + return; + } + d->text.replace( d->imstart, d->imend - d->imstart, e->text() ); + d->imend = d->imstart + e->text().length(); + d->imselstart = d->imstart + e->cursorPos(); + d->imselend = d->imselstart + e->selectionLength(); + d->cursor = d->imstart + e->cursorPos(); + d->updateTextLayout(); + d->updateMicroFocusHint(); + update(); +} + +/*! \reimp + */ +void QLineEdit::imEndEvent( QIMEvent *e ) +{ + if ( d->readOnly) { + e->ignore(); + return; + } + d->text.remove( d->imstart, d->imend - d->imstart ); + d->cursor = d->imselstart = d->imselend = d->imend = d->imstart; + d->textDirty = TRUE; + insert( e->text() ); +} + +/*!\reimp +*/ + +void QLineEdit::focusInEvent( QFocusEvent* ) +{ + if ( QFocusEvent::reason() == QFocusEvent::Tab || + QFocusEvent::reason() == QFocusEvent::Backtab || + QFocusEvent::reason() == QFocusEvent::Shortcut ) + d->maskData ? d->moveCursor( d->nextMaskBlank( 0 ) ) : selectAll(); + if ( !d->cursorTimer ) { + int cft = QApplication::cursorFlashTime(); + d->cursorTimer = cft ? startTimer( cft/2 ) : -1; + } + if( !hasSelectedText() || style().styleHint( QStyle::SH_BlinkCursorWhenTextSelected ) ) + d->setCursorVisible( TRUE ); + if ( d->hasIMSelection() ) + d->cursor = d->imselstart; + d->updateMicroFocusHint(); +} + +/*!\reimp +*/ + +void QLineEdit::focusOutEvent( QFocusEvent* ) +{ + if ( QFocusEvent::reason() != QFocusEvent::ActiveWindow && + QFocusEvent::reason() != QFocusEvent::Popup ) + deselect(); + d->setCursorVisible( FALSE ); + if ( d->cursorTimer > 0 ) + killTimer( d->cursorTimer ); + d->cursorTimer = 0; + if (QFocusEvent::reason() != QFocusEvent::Popup) + emit lostFocus(); +} + +/*!\reimp +*/ +void QLineEdit::drawContents( QPainter *p ) +{ + const QColorGroup& cg = colorGroup(); + QRect cr = contentsRect(); + QFontMetrics fm = fontMetrics(); + QRect lineRect( cr.x() + innerMargin, cr.y() + (cr.height() - fm.height() + 1) / 2, + cr.width() - 2*innerMargin, fm.height() ); + QBrush bg = QBrush( paletteBackgroundColor() ); + if ( paletteBackgroundPixmap() ) + bg = QBrush( cg.background(), *paletteBackgroundPixmap() ); + else if ( !isEnabled() ) + bg = cg.brush( QColorGroup::Background ); + QPoint brushOrigin = p->brushOrigin(); + p->save(); + p->setClipRegion( QRegion(cr) - lineRect ); + p->setBrushOrigin(brushOrigin - backgroundOffset()); + p->fillRect( cr, bg ); + p->restore(); + QSharedDoubleBuffer buffer( p, lineRect.x(), lineRect.y(), + lineRect.width(), lineRect.height(), + hasFocus() ? QSharedDoubleBuffer::Force : 0 ); + p = buffer.painter(); + brushOrigin = p->brushOrigin(); + p->setBrushOrigin(brushOrigin - backgroundOffset()); + p->fillRect( lineRect, bg ); + p->setBrushOrigin(brushOrigin); + + // locate cursor position + int cix = 0; + QTextItem ci = d->textLayout.findItem( d->cursor ); + if ( ci.isValid() ) { + if ( d->cursor != (int)d->text.length() && d->cursor == ci.from() + ci.length() + && ci.isRightToLeft() != d->isRightToLeft() ) + ci = d->textLayout.findItem( d->cursor + 1 ); + cix = ci.x() + ci.cursorToX( d->cursor - ci.from() ); + } + + // horizontal scrolling + int minLB = QMAX( 0, -fm.minLeftBearing() ); + int minRB = QMAX( 0, -fm.minRightBearing() ); + int widthUsed = d->textLayout.widthUsed() + 1 + minRB; + if ( (minLB + widthUsed) <= lineRect.width() ) { + switch ( d->visualAlignment() ) { + case AlignRight: + d->hscroll = widthUsed - lineRect.width() + 1; + break; + case AlignHCenter: + d->hscroll = ( widthUsed - lineRect.width() ) / 2; + break; + default: + d->hscroll = 0; + break; + } + d->hscroll -= minLB; + } else if ( cix - d->hscroll >= lineRect.width() ) { + d->hscroll = cix - lineRect.width() + 1; + } else if ( cix - d->hscroll < 0 ) { + d->hscroll = cix; + } else if ( widthUsed - d->hscroll < lineRect.width() ) { + d->hscroll = widthUsed - lineRect.width() + 1; + } else if (d->hscroll < 0) { + d->hscroll = 0; + } + // This updateMicroFocusHint() is corresponding to update() at + // IMCompose event. Although the function is invoked from various + // other points, some situations such as "candidate selection on + // AlignHCenter'ed text" need this invocation because + // updateMicroFocusHint() requires updated contentsRect(), and + // there are no other chances in such situation that invoke the + // function. + d->updateMicroFocusHint(); + // the y offset is there to keep the baseline constant in case we have script changes in the text. + QPoint topLeft = lineRect.topLeft() - QPoint(d->hscroll, d->ascent-fm.ascent()); + + // draw text, selections and cursors + p->setPen( cg.text() ); + bool supressCursor = d->readOnly, hasRightToLeft = d->isRightToLeft(); + int textflags = 0; + if ( font().underline() ) + textflags |= Qt::Underline; + if ( font().strikeOut() ) + textflags |= Qt::StrikeOut; + if ( font().overline() ) + textflags |= Qt::Overline; + + for ( int i = 0; i < d->textLayout.numItems(); i++ ) { + QTextItem ti = d->textLayout.itemAt( i ); + hasRightToLeft |= ti.isRightToLeft(); + int tix = topLeft.x() + ti.x(); + int first = ti.from(); + int last = ti.from() + ti.length() - 1; + + // text and selection + if ( d->selstart < d->selend && (last >= d->selstart && first < d->selend ) ) { + QRect highlight = QRect( QPoint( tix + ti.cursorToX( QMAX( d->selstart - first, 0 ) ), + lineRect.top() ), + QPoint( tix + ti.cursorToX( QMIN( d->selend - first, last - first + 1 ) ) - 1, + lineRect.bottom() ) ).normalize(); + p->save(); + p->setClipRegion( QRegion( lineRect ) - highlight, QPainter::CoordPainter ); + p->drawTextItem( topLeft, ti, textflags ); + p->setClipRect( lineRect & highlight, QPainter::CoordPainter ); + p->fillRect( highlight, cg.highlight() ); + p->setPen( cg.highlightedText() ); + p->drawTextItem( topLeft, ti, textflags ); + p->restore(); + } else { + p->drawTextItem( topLeft, ti, textflags ); + } + + // input method edit area + if ( d->composeMode() && (last >= d->imstart && first < d->imend ) ) { + QRect highlight = QRect( QPoint( tix + ti.cursorToX( QMAX( d->imstart - first, 0 ) ), lineRect.top() ), + QPoint( tix + ti.cursorToX( QMIN( d->imend - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize(); + p->save(); + p->setClipRect( lineRect & highlight, QPainter::CoordPainter ); + + int h1, s1, v1, h2, s2, v2; + cg.color( QColorGroup::Base ).hsv( &h1, &s1, &v1 ); + cg.color( QColorGroup::Background ).hsv( &h2, &s2, &v2 ); + QColor imCol; + imCol.setHsv( h1, s1, ( v1 + v2 ) / 2 ); + p->fillRect( highlight, imCol ); + p->drawTextItem( topLeft, ti, textflags ); + // draw preedit's underline + if (d->imend - d->imstart > 0) { + p->setPen( cg.text() ); + p->drawLine( highlight.bottomLeft(), highlight.bottomRight() ); + } + p->restore(); + } + + // input method selection + if ( d->hasIMSelection() && (last >= d->imselstart && first < d->imselend ) ) { + QRect highlight = QRect( QPoint( tix + ti.cursorToX( QMAX( d->imselstart - first, 0 ) ), lineRect.top() ), + QPoint( tix + ti.cursorToX( QMIN( d->imselend - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize(); + p->save(); + p->setClipRect( lineRect & highlight, QPainter::CoordPainter ); + p->fillRect( highlight, cg.text() ); + p->setPen( paletteBackgroundColor() ); + p->drawTextItem( topLeft, ti, textflags ); + p->restore(); + supressCursor = TRUE; + } + + // overwrite cursor + if ( d->cursorVisible && d->maskData && + d->selend <= d->selstart && (last >= d->cursor && first <= d->cursor ) ) { + QRect highlight = QRect( QPoint( tix + ti.cursorToX( QMAX( d->cursor - first, 0 ) ), lineRect.top() ), + QPoint( tix + ti.cursorToX( QMIN( d->cursor + 1 - first, last - first + 1 ) )-1, lineRect.bottom() ) ).normalize(); + p->save(); + p->setClipRect( lineRect & highlight, QPainter::CoordPainter ); + p->fillRect( highlight, cg.text() ); + p->setPen( paletteBackgroundColor() ); + p->drawTextItem( topLeft, ti, textflags ); + p->restore(); + supressCursor = TRUE; + } + } + + // draw cursor + // + // Asian users regard IM selection text as cursor on candidate + // selection phase of input method, so ordinary cursor should be + // invisible if IM selection text exists. + if ( d->cursorVisible && !supressCursor && !d->hasIMSelection() ) { + QPoint from( topLeft.x() + cix, lineRect.top() ); + QPoint to = from + QPoint( 0, lineRect.height() ); + p->drawLine( from, to ); + if ( hasRightToLeft ) { + bool rtl = ci.isValid() ? ci.isRightToLeft() : TRUE; + to = from + QPoint( (rtl ? -2 : 2), 2 ); + p->drawLine( from, to ); + from.ry() += 4; + p->drawLine( from, to ); + } + } + buffer.end(); +} + + +#ifndef QT_NO_DRAGANDDROP +/*!\reimp +*/ +void QLineEdit::dragMoveEvent( QDragMoveEvent *e ) +{ + if ( !d->readOnly && QTextDrag::canDecode(e) ) { + e->acceptAction(); + d->cursor = d->xToPos( e->pos().x() ); + d->cursorVisible = TRUE; + update(); + } +} + +/*!\reimp */ +void QLineEdit::dragEnterEvent( QDragEnterEvent * e ) +{ + QLineEdit::dragMoveEvent( e ); +} + +/*!\reimp */ +void QLineEdit::dragLeaveEvent( QDragLeaveEvent *) +{ + if ( d->cursorVisible ) { + d->cursorVisible = FALSE; + update(); + } +} + +/*!\reimp */ +void QLineEdit::dropEvent( QDropEvent* e ) +{ + QString str; + // try text/plain + QCString plain = "plain"; + bool decoded = QTextDrag::decode(e, str, plain); + // otherwise we'll accept any kind of text (like text/uri-list) + if (! decoded) + decoded = QTextDrag::decode(e, str); + + if ( decoded && !d->readOnly ) { + if ( e->source() == this && e->action() == QDropEvent::Copy ) + deselect(); + d->cursor =d->xToPos( e->pos().x() ); + int selStart = d->cursor; + int oldSelStart = d->selstart; + int oldSelEnd = d->selend; + d->cursorVisible = FALSE; + e->acceptAction(); + insert( str ); + if ( e->source() == this ) { + if ( e->action() == QDropEvent::Move ) { + if ( selStart > oldSelStart && selStart <= oldSelEnd ) + setSelection( oldSelStart, str.length() ); + else if ( selStart > oldSelEnd ) + setSelection( selStart - str.length(), str.length() ); + else + setSelection( selStart, str.length() ); + } else { + setSelection( selStart, str.length() ); + } + } + } else { + e->ignore(); + update(); + } +} + +bool QLineEditPrivate::drag() +{ + q->killTimer( dndTimer ); + dndTimer = 0; + QTextDrag *tdo = new QTextDrag( q->selectedText(), q ); + + QGuardedPtr<QLineEdit> gptr = q; + bool r = tdo->drag(); + if ( !gptr ) + return FALSE; + + // ### fix the check QDragObject::target() != q in Qt4 (should not be needed) + if ( r && !readOnly && QDragObject::target() != q ) { + int priorState = undoState; + removeSelectedText(); + finishChange( priorState ); + } +#ifndef QT_NO_CURSOR + q->setCursor( readOnly ? arrowCursor : ibeamCursor ); +#endif + return TRUE; +} + +#endif // QT_NO_DRAGANDDROP + +enum { IdUndo, IdRedo, IdSep1, IdCut, IdCopy, IdPaste, IdClear, IdSep2, IdSelectAll }; + +/*!\reimp +*/ +void QLineEdit::contextMenuEvent( QContextMenuEvent * e ) +{ +#ifndef QT_NO_POPUPMENU +#ifndef QT_NO_IM + if ( d->composeMode() ) + return; +#endif + d->separate(); + QPopupMenu *menu = createPopupMenu(); + if (!menu) + return; + QGuardedPtr<QPopupMenu> popup = menu; + QGuardedPtr<QLineEdit> that = this; + QPoint pos = e->reason() == QContextMenuEvent::Mouse ? e->globalPos() : + mapToGlobal( QPoint(e->pos().x(), 0) ) + QPoint( width() / 2, height() / 2 ); + int r = popup->exec( pos ); + delete (QPopupMenu*)popup; + if ( that && d->menuId ) { + switch ( d->menuId - r ) { + case IdClear: clear(); break; + case IdSelectAll: selectAll(); break; + case IdUndo: undo(); break; + case IdRedo: redo(); break; +#ifndef QT_NO_CLIPBOARD + case IdCut: cut(); break; + case IdCopy: copy(); break; + case IdPaste: paste(); break; +#endif + default: + ; // nothing selected or lineedit destroyed. Be careful. + } + } +#endif //QT_NO_POPUPMENU +} + +/*! + This function is called to create the popup menu which is shown + when the user clicks on the line edit with the right mouse button. + If you want to create a custom popup menu, reimplement this + function and return the popup menu you create. The popup menu's + ownership is transferred to the caller. +*/ + +QPopupMenu *QLineEdit::createPopupMenu() +{ +#ifndef QT_NO_POPUPMENU + QPopupMenu *popup = new QPopupMenu( this, "qt_edit_menu" ); + int id = d->menuId = popup->insertItem( tr( "&Undo" ) + ACCEL_KEY( Z ) ); + popup->insertItem( tr( "&Redo" ) + ACCEL_KEY( Y ) ); + popup->insertSeparator(); + popup->insertItem( tr( "Cu&t" ) + ACCEL_KEY( X ) ); + popup->insertItem( tr( "&Copy" ) + ACCEL_KEY( C ) ); + popup->insertItem( tr( "&Paste" ) + ACCEL_KEY( V ) ); + popup->insertItem( tr( "Clear" ) ); + popup->insertSeparator(); + popup->insertItem( tr( "Select All" ) +#ifndef Q_WS_X11 + + ACCEL_KEY( A ) +#endif + ); + +#ifndef QT_NO_IM + QInputContext *qic = getInputContext(); + if ( qic ) + qic->addMenusTo( popup ); +#endif + + popup->setItemEnabled( id - IdUndo, d->isUndoAvailable() ); + popup->setItemEnabled( id - IdRedo, d->isRedoAvailable() ); +#ifndef QT_NO_CLIPBOARD + popup->setItemEnabled( id - IdCut, !d->readOnly && d->hasSelectedText() ); + popup->setItemEnabled( id - IdCopy, d->hasSelectedText() ); + popup->setItemEnabled( id - IdPaste, !d->readOnly && !QApplication::clipboard()->text().isEmpty() ); +#else + popup->setItemVisible( id - IdCut, FALSE ); + popup->setItemVisible( id - IdCopy, FALSE ); + popup->setItemVisible( id - IdPaste, FALSE ); +#endif + popup->setItemEnabled( id - IdClear, !d->readOnly && !d->text.isEmpty() ); + popup->setItemEnabled( id - IdSelectAll, !d->text.isEmpty() && !d->allSelected() ); + return popup; +#else + return 0; +#endif +} + +/*! \reimp */ +void QLineEdit::windowActivationChange( bool b ) +{ + //### remove me with WHighlightSelection attribute + if ( palette().active() != palette().inactive() ) + update(); + QWidget::windowActivationChange( b ); +} + +/*! \reimp */ + +void QLineEdit::setPalette( const QPalette & p ) +{ + //### remove me with WHighlightSelection attribute + QWidget::setPalette( p ); + update(); +} + +/*! + \obsolete + \fn void QLineEdit::repaintArea( int from, int to ) + Repaints all characters from \a from to \a to. If cursorPos is + between from and to, ensures that cursorPos is visible. +*/ + +/*! \reimp + */ +void QLineEdit::setFont( const QFont & f ) +{ + QWidget::setFont( f ); + d->updateTextLayout(); +} + +/*! \obsolete +*/ +int QLineEdit::characterAt( int xpos, QChar *chr ) const +{ + int pos = d->xToPos( xpos + contentsRect().x() - d->hscroll + innerMargin ); + if ( chr && pos < (int) d->text.length() ) + *chr = d->text.at( pos ); + return pos; +} + +/*! + \internal + + Sets the password character to \a c. + + \sa passwordChar() +*/ + +void QLineEdit::setPasswordChar( QChar c ) +{ + d->passwordChar = c; +} + +/*! + \internal + + Returns the password character. + + \sa setPasswordChar() +*/ +QChar QLineEdit::passwordChar() const +{ + return ( d->passwordChar.isNull() ? QChar( style().styleHint( QStyle::SH_LineEdit_PasswordCharacter, this ) ) : d->passwordChar ); +} + +void QLineEdit::clipboardChanged() +{ +} + +void QLineEditPrivate::init( const QString& txt ) +{ +#ifndef QT_NO_CURSOR + q->setCursor( readOnly ? arrowCursor : ibeamCursor ); +#endif + q->setFocusPolicy( QWidget::StrongFocus ); + q->setInputMethodEnabled( TRUE ); + // Specifies that this widget can use more, but is able to survive on + // less, horizontal space; and is fixed vertically. + q->setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + q->setBackgroundMode( PaletteBase ); + q->setKeyCompression( TRUE ); + q->setMouseTracking( TRUE ); + q->setAcceptDrops( TRUE ); + q->setFrame( TRUE ); + text = txt; + updateTextLayout(); + cursor = text.length(); +} + +void QLineEditPrivate::updateTextLayout() +{ + // replace all non-printable characters with spaces (to avoid + // drawing boxes when using fonts that don't have glyphs for such + // characters) + const QString &displayText = q->displayText(); + QString str(displayText.unicode(), displayText.length()); + QChar* uc = (QChar*)str.unicode(); + for (int i = 0; i < (int)str.length(); ++i) { + if (! uc[i].isPrint()) + uc[i] = QChar(0x0020); + } + textLayout.setText( str, q->font() ); + textLayout.setDirection((QChar::Direction)direction); + textLayout.beginLayout(QTextLayout::SingleLine); + textLayout.beginLine( INT_MAX ); + while ( !textLayout.atEnd() ) + textLayout.addCurrentItem(); + ascent = 0; + textLayout.endLine(0, 0, Qt::AlignLeft|Qt::SingleLine, &ascent); +} + +int QLineEditPrivate::xToPosInternal( int x, QTextItem::CursorPosition betweenOrOn ) const +{ + x-= q->contentsRect().x() - hscroll + innerMargin; + for ( int i = 0; i < textLayout.numItems(); ++i ) { + QTextItem ti = textLayout.itemAt( i ); + QRect tir = ti.rect(); + if ( x >= tir.left() && x <= tir.right() ) + return ti.xToCursor( x - tir.x(), betweenOrOn ) + ti.from(); + } + return x < 0 ? -1 : text.length(); +} + +int QLineEditPrivate::xToPos( int x, QTextItem::CursorPosition betweenOrOn ) const +{ + int pos = xToPosInternal( x, betweenOrOn ); + return ( pos < 0 ) ? 0 : pos; +} + + +QRect QLineEditPrivate::cursorRect() const +{ + QRect cr = q->contentsRect(); + int cix = cr.x() - hscroll + innerMargin; + QTextItem ci = textLayout.findItem( cursor ); + if ( ci.isValid() ) { + if ( cursor != (int)text.length() && cursor == ci.from() + ci.length() + && ci.isRightToLeft() != isRightToLeft() ) + ci = textLayout.findItem( cursor + 1 ); + cix += ci.x() + ci.cursorToX( cursor - ci.from() ); + } + int ch = q->fontMetrics().height(); + return QRect( cix-4, cr.y() + ( cr.height() - ch + 1) / 2, 8, ch + 1 ); +} + +void QLineEditPrivate::updateMicroFocusHint() +{ + // To reduce redundant microfocus update notification, we remember + // the old rect and update the microfocus if actual update is + // required. The rect o is intentionally static because some + // notifyee requires the microfocus information as global update + // rather than per notifyee update to place shared widget around + // microfocus. + static QRect o; + if ( q->hasFocus() ) { + QRect r = cursorRect(); + if ( o != r ) { + o = r; + q->setMicroFocusHint( r.x(), r.y(), r.width(), r.height() ); + } + } +} + +void QLineEditPrivate::moveCursor( int pos, bool mark ) +{ + if ( pos != cursor ) + separate(); + if ( maskData && pos > cursor ) + pos = nextMaskBlank( pos ); + else if ( maskData && pos < cursor ) + pos = prevMaskBlank( pos ); + bool fullUpdate = mark || hasSelectedText(); + if ( mark ) { + int anchor; + if ( selend > selstart && cursor == selstart ) + anchor = selend; + else if ( selend > selstart && cursor == selend ) + anchor = selstart; + else + anchor = cursor; + selstart = QMIN( anchor, pos ); + selend = QMAX( anchor, pos ); + } else { + deselect(); + } + if ( fullUpdate ) { + cursor = pos; + q->update(); + } else { + setCursorVisible( FALSE ); + cursor = pos; + setCursorVisible( TRUE ); + } + updateMicroFocusHint(); + if ( mark && !q->style().styleHint( QStyle::SH_BlinkCursorWhenTextSelected ) ) + setCursorVisible( FALSE ); + if ( mark || selDirty ) { + selDirty = FALSE; + emit q->selectionChanged(); + } +} + +void QLineEditPrivate::finishChange( int validateFromState, bool setModified ) +{ + bool lineDirty = selDirty; + if ( textDirty ) { + // do validation + bool wasValidInput = validInput; + validInput = TRUE; +#ifndef QT_NO_VALIDATOR + if ( validator && validateFromState >= 0 ) { + QString textCopy = text; + int cursorCopy = cursor; + validInput = ( validator->validate( textCopy, cursorCopy ) != QValidator::Invalid ); + if ( validInput ) { + if ( text != textCopy ) { + q->setText( textCopy ); + cursor = cursorCopy; + return; + } + cursor = cursorCopy; + } + } +#endif + if ( validateFromState >= 0 && wasValidInput && !validInput ) { + undo( validateFromState ); + history.resize( undoState ); + validInput = TRUE; + textDirty = setModified = FALSE; + } + updateTextLayout(); + updateMicroFocusHint(); + lineDirty |= textDirty; + if ( setModified ) + modified = TRUE; + if ( textDirty ) { + textDirty = FALSE; + emit q->textChanged( maskData ? stripString(text) : text ); + } +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( q, 0, QAccessible::ValueChanged ); +#endif + } + if ( selDirty ) { + selDirty = FALSE; + emit q->selectionChanged(); + } + if ( lineDirty || !setModified ) + q->update(); +} + +void QLineEditPrivate::setText( const QString& txt ) +{ + deselect(); + QString oldText = text; + if ( maskData ) { + text = maskString( 0, txt, TRUE ); + text += clearString( text.length(), maxLength - text.length() ); + } else { + text = txt.isEmpty() ? txt : txt.left( maxLength ); + } + history.clear(); + undoState = 0; + cursor = text.length(); + textDirty = ( oldText != text ); +} + + +void QLineEditPrivate::setCursorVisible( bool visible ) +{ + if ( (bool)cursorVisible == visible ) + return; + if ( cursorTimer ) + cursorVisible = visible; + QRect r = cursorRect(); + if ( maskData || !q->contentsRect().contains( r ) ) + q->update(); + else + q->update( r ); +} + +void QLineEditPrivate::addCommand( const Command& cmd ) +{ + if ( separator && undoState && history[undoState-1].type != Separator ) { + history.resize( undoState + 2 ); + history[undoState++] = Command( Separator, 0, 0 ); + } else { + history.resize( undoState + 1); + } + separator = FALSE; + history[ undoState++ ] = cmd; +} + +void QLineEditPrivate::insert( const QString& s ) +{ + if ( maskData ) { + QString ms = maskString( cursor, s ); + for ( int i = 0; i < (int) ms.length(); ++i ) { + addCommand ( Command( DeleteSelection, cursor+i, text.at(cursor+i) ) ); + addCommand( Command( Insert, cursor+i, ms.at(i) ) ); + } + text.replace( cursor, ms.length(), ms ); + cursor += ms.length(); + cursor = nextMaskBlank( cursor ); + } else { + int remaining = maxLength - text.length(); + text.insert( cursor, s.left(remaining) ); + for ( int i = 0; i < (int) s.left(remaining).length(); ++i ) + addCommand( Command( Insert, cursor++, s.at(i) ) ); + } + textDirty = TRUE; +} + +void QLineEditPrivate::del( bool wasBackspace ) +{ + if ( cursor < (int) text.length() ) { + addCommand ( Command( (CommandType)((maskData?2:0)+(wasBackspace?Remove:Delete)), cursor, text.at(cursor) ) ); + if ( maskData ) { + text.replace( cursor, 1, clearString( cursor, 1 ) ); + addCommand( Command( Insert, cursor, text.at( cursor ) ) ); + } else { + text.remove( cursor, 1 ); + } + textDirty = TRUE; + } +} + +void QLineEditPrivate::removeSelectedText() +{ + if ( selstart < selend && selend <= (int) text.length() ) { + separate(); + int i ; + if ( selstart <= cursor && cursor < selend ) { + // cursor is within the selection. Split up the commands + // to be able to restore the correct cursor position + for ( i = cursor; i >= selstart; --i ) + addCommand ( Command( DeleteSelection, i, text.at(i) ) ); + for ( i = selend - 1; i > cursor; --i ) + addCommand ( Command( DeleteSelection, i - cursor + selstart - 1, text.at(i) ) ); + } else { + for ( i = selend-1; i >= selstart; --i ) + addCommand ( Command( RemoveSelection, i, text.at(i) ) ); + } + if ( maskData ) { + text.replace( selstart, selend - selstart, clearString( selstart, selend - selstart ) ); + for ( int i = 0; i < selend - selstart; ++i ) + addCommand( Command( Insert, selstart + i, text.at( selstart + i ) ) ); + } else { + text.remove( selstart, selend - selstart ); + } + if ( cursor > selstart ) + cursor -= QMIN( cursor, selend ) - selstart; + deselect(); + textDirty = TRUE; + } +} + +void QLineEditPrivate::parseInputMask( const QString &maskFields ) +{ + if ( maskFields.isEmpty() || maskFields.section( ';', 0, 0 ).isEmpty() ) { + if ( maskData ) { + delete [] maskData; + maskData = 0; + maxLength = 32767; + q->setText( QString::null ); + } + return; + } + + inputMask = maskFields.section( ';', 0, 0 ); + blank = maskFields.section( ';', 1, 1 ).at(0); + if ( blank.isNull() ) + blank = ' '; + + // calculate maxLength / maskData length + maxLength = 0; + QChar c = 0; + uint i; + for ( i=0; i<inputMask.length(); i++ ) { + c = inputMask.at(i); + if ( i > 0 && inputMask.at( i-1 ) == '\\' ) { + maxLength++; + continue; + } + if ( c != '\\' && c != '!' && + c != '<' && c != '>' && + c != '{' && c != '}' && + c != '[' && c != ']' ) + maxLength++; + } + + delete [] maskData; + maskData = new MaskInputData[ maxLength ]; + + MaskInputData::Casemode m = MaskInputData::NoCaseMode; + c = 0; + bool s; + bool escape = FALSE; + int index = 0; + for ( i = 0; i < inputMask.length(); i++ ) { + c = inputMask.at(i); + if ( escape ) { + s = TRUE; + maskData[ index ].maskChar = c; + maskData[ index ].separator = s; + maskData[ index ].caseMode = m; + index++; + escape = FALSE; + } else if ( c == '<' || c == '>' || c == '!') { + switch ( c ) { + case '<': + m = MaskInputData::Lower; + break; + case '>': + m = MaskInputData::Upper; + break; + case '!': + m = MaskInputData::NoCaseMode; + break; + } + } else if ( c != '{' && c != '}' && c != '[' && c != ']' ) { + switch ( c ) { + case 'A': + case 'a': + case 'N': + case 'n': + case 'X': + case 'x': + case '9': + case '0': + case 'D': + case 'd': + case '#': + s = FALSE; + break; + case '\\': + escape = TRUE; + default: + s = TRUE; + break; + } + + if ( !escape ) { + maskData[ index ].maskChar = c; + maskData[ index ].separator = s; + maskData[ index ].caseMode = m; + index++; + } + } + } + q->setText( QString::null ); +} + + +/* checks if the key is valid compared to the inputMask */ +bool QLineEditPrivate::isValidInput( QChar key, QChar mask ) const +{ + switch ( mask ) { + case 'A': + if ( key.isLetter() && key != blank ) + return TRUE; + break; + case 'a': + if ( key.isLetter() || key == blank ) + return TRUE; + break; + case 'N': + if ( key.isLetterOrNumber() && key != blank ) + return TRUE; + break; + case 'n': + if ( key.isLetterOrNumber() || key == blank ) + return TRUE; + break; + case 'X': + if ( key.isPrint() && key != blank ) + return TRUE; + break; + case 'x': + if ( key.isPrint() || key == blank ) + return TRUE; + break; + case '9': + if ( key.isNumber() && key != blank ) + return TRUE; + break; + case '0': + if ( key.isNumber() || key == blank ) + return TRUE; + break; + case 'D': + if ( key.isNumber() && key.digitValue() > 0 && key != blank ) + return TRUE; + break; + case 'd': + if ( (key.isNumber() && key.digitValue() > 0) || key == blank ) + return TRUE; + break; + case '#': + if ( key.isNumber() || key == '+' || key == '-' || key == blank ) + return TRUE; + break; + default: + break; + } + return FALSE; +} + +/* + Applies the inputMask on \a str starting from position \a pos in the mask. \a clear + specifies from where characters should be gotten when a separator is met in \a str - TRUE means + that blanks will be used, FALSE that previous input is used. + Calling this when no inputMask is set is undefined. +*/ +QString QLineEditPrivate::maskString( uint pos, const QString &str, bool clear) const +{ + if ( pos >= (uint)maxLength ) + return QString::fromLatin1(""); + + QString fill; + fill = clear ? clearString( 0, maxLength ) : text; + + uint strIndex = 0; + QString s = QString::fromLatin1(""); + int i = pos; + while ( i < maxLength ) { + if ( strIndex < str.length() ) { + if ( maskData[ i ].separator ) { + s += maskData[ i ].maskChar; + if ( str[(int)strIndex] == maskData[ i ].maskChar ) + strIndex++; + ++i; + } else { + if ( isValidInput( str[(int)strIndex], maskData[ i ].maskChar ) ) { + switch ( maskData[ i ].caseMode ) { + case MaskInputData::Upper: + s += str[(int)strIndex].upper(); + break; + case MaskInputData::Lower: + s += str[(int)strIndex].lower(); + break; + default: + s += str[(int)strIndex]; + } + ++i; + } else { + // search for separator first + int n = findInMask( i, TRUE, TRUE, str[(int)strIndex] ); + if ( n != -1 ) { + if ( str.length() != 1 || i == 0 || (i > 0 && (!maskData[i-1].separator || maskData[i-1].maskChar != str[(int)strIndex])) ) { + s += fill.mid( i, n-i+1 ); + i = n + 1; // update i to find + 1 + } + } else { + // search for valid blank if not + n = findInMask( i, TRUE, FALSE, str[(int)strIndex] ); + if ( n != -1 ) { + s += fill.mid( i, n-i ); + switch ( maskData[ n ].caseMode ) { + case MaskInputData::Upper: + s += str[(int)strIndex].upper(); + break; + case MaskInputData::Lower: + s += str[(int)strIndex].lower(); + break; + default: + s += str[(int)strIndex]; + } + i = n + 1; // updates i to find + 1 + } + } + } + strIndex++; + } + } else + break; + } + + return s; +} + + + +/* + Returns a "cleared" string with only separators and blank chars. + Calling this when no inputMask is set is undefined. +*/ +QString QLineEditPrivate::clearString( uint pos, uint len ) const +{ + if ( pos >= (uint)maxLength ) + return QString::null; + + QString s; + int end = QMIN( (uint)maxLength, pos + len ); + for ( int i=pos; i<end; i++ ) + if ( maskData[ i ].separator ) + s += maskData[ i ].maskChar; + else + s += blank; + + return s; +} + +/* + Strips blank parts of the input in a QLineEdit when an inputMask is set, + separators are still included. Typically "127.0__.0__.1__" becomes "127.0.0.1". +*/ +QString QLineEditPrivate::stripString( const QString &str ) const +{ + if ( !maskData ) + return str; + + QString s; + int end = QMIN( maxLength, (int)str.length() ); + for (int i=0; i < end; i++ ) + if ( maskData[ i ].separator ) + s += maskData[ i ].maskChar; + else + if ( str[i] != blank ) + s += str[i]; + + return s; +} + +/* searches forward/backward in maskData for either a separator or a blank */ +int QLineEditPrivate::findInMask( int pos, bool forward, bool findSeparator, QChar searchChar ) const +{ + if ( pos >= maxLength || pos < 0 ) + return -1; + + int end = forward ? maxLength : -1; + int step = forward ? 1 : -1; + int i = pos; + + while ( i != end ) { + if ( findSeparator ) { + if ( maskData[ i ].separator && maskData[ i ].maskChar == searchChar ) + return i; + } else { + if ( !maskData[ i ].separator ) { + if ( searchChar.isNull() ) + return i; + else if ( isValidInput( searchChar, maskData[ i ].maskChar ) ) + return i; + } + } + i += step; + } + return -1; +} + + +#endif // QT_NO_LINEEDIT diff --git a/src/widgets/qlineedit.h b/src/widgets/qlineedit.h new file mode 100644 index 0000000..3778e2a --- /dev/null +++ b/src/widgets/qlineedit.h @@ -0,0 +1,232 @@ +/********************************************************************** +** +** Definition of QLineEdit widget class +** +** Created : 941011 +** +** 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. +** +**********************************************************************/ + +#ifndef QLINEEDIT_H +#define QLINEEDIT_H + +struct QLineEditPrivate; + +class QValidator; +class QPopupMenu; + +#ifndef QT_H +#include "qframe.h" +#include "qstring.h" +#endif // QT_H + +#ifndef QT_NO_LINEEDIT + +class QTextParagraph; +class QTextCursor; + +class Q_EXPORT QLineEdit : public QFrame +{ + Q_OBJECT + Q_ENUMS( EchoMode ) + Q_PROPERTY( QString text READ text WRITE setText ) + Q_PROPERTY( int maxLength READ maxLength WRITE setMaxLength ) + Q_PROPERTY( bool frame READ frame WRITE setFrame ) + Q_PROPERTY( EchoMode echoMode READ echoMode WRITE setEchoMode ) + Q_PROPERTY( QString displayText READ displayText ) + Q_PROPERTY( int cursorPosition READ cursorPosition WRITE setCursorPosition ) + Q_PROPERTY( Alignment alignment READ alignment WRITE setAlignment ) + Q_PROPERTY( bool edited READ edited WRITE setEdited DESIGNABLE false ) + Q_PROPERTY( bool modified READ isModified ) + Q_PROPERTY( bool hasMarkedText READ hasMarkedText DESIGNABLE false ) + Q_PROPERTY( bool hasSelectedText READ hasSelectedText ) + Q_PROPERTY( QString markedText READ markedText DESIGNABLE false ) + Q_PROPERTY( QString selectedText READ selectedText ) + Q_PROPERTY( bool dragEnabled READ dragEnabled WRITE setDragEnabled ) + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly ) + Q_PROPERTY( bool undoAvailable READ isUndoAvailable ) + Q_PROPERTY( bool redoAvailable READ isRedoAvailable ) + Q_PROPERTY( QString inputMask READ inputMask WRITE setInputMask ) + Q_PROPERTY( bool acceptableInput READ hasAcceptableInput ) + +public: + QLineEdit( QWidget* parent, const char* name=0 ); + QLineEdit( const QString &, QWidget* parent, const char* name=0 ); + QLineEdit( const QString &, const QString &, QWidget* parent, const char* name=0 ); + ~QLineEdit(); + + QString text() const; + + QString displayText() const; + + int maxLength() const; + + bool frame() const; + + enum EchoMode { Normal, NoEcho, Password }; + EchoMode echoMode() const; + + bool isReadOnly() const; + + const QValidator * validator() const; + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + int cursorPosition() const; + bool validateAndSet( const QString &, int, int, int ); // obsolete + + int alignment() const; + +#ifndef QT_NO_COMPAT + void cursorLeft( bool mark, int steps = 1 ) { cursorForward( mark, -steps ); } + void cursorRight( bool mark, int steps = 1 ) { cursorForward( mark, steps ); } +#endif + void cursorForward( bool mark, int steps = 1 ); + void cursorBackward( bool mark, int steps = 1 ); + void cursorWordForward( bool mark ); + void cursorWordBackward( bool mark ); + void backspace(); + void del(); + void home( bool mark ); + void end( bool mark ); + + bool isModified() const; + void clearModified(); + + bool edited() const; // obsolete, use isModified() + void setEdited( bool ); // obsolete, use clearModified() + + bool hasSelectedText() const; + QString selectedText() const; + int selectionStart() const; + + bool isUndoAvailable() const; + bool isRedoAvailable() const; + +#ifndef QT_NO_COMPAT + bool hasMarkedText() const { return hasSelectedText(); } + QString markedText() const { return selectedText(); } +#endif + + bool dragEnabled() const; + + QString inputMask() const; + void setInputMask( const QString &inputMask ); + bool hasAcceptableInput() const; + +public slots: + virtual void setText( const QString &); + virtual void selectAll(); + virtual void deselect(); + virtual void clearValidator(); + virtual void insert( const QString &); + virtual void clear(); + virtual void undo(); + virtual void redo(); + virtual void setMaxLength( int ); + virtual void setFrame( bool ); + virtual void setEchoMode( EchoMode ); + virtual void setReadOnly( bool ); + virtual void setValidator( const QValidator * ); + virtual void setFont( const QFont & ); + virtual void setPalette( const QPalette & ); + virtual void setSelection( int, int ); + virtual void setCursorPosition( int ); + virtual void setAlignment( int flag ); +#ifndef QT_NO_CLIPBOARD + virtual void cut(); + virtual void copy() const; + virtual void paste(); +#endif + virtual void setDragEnabled( bool b ); + +signals: + void textChanged( const QString &); + void returnPressed(); + void lostFocus(); + void selectionChanged(); + +protected: + bool event( QEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseDoubleClickEvent( QMouseEvent * ); + void keyPressEvent( QKeyEvent * ); + void imStartEvent( QIMEvent * ); + void imComposeEvent( QIMEvent * ); + void imEndEvent( QIMEvent * ); + void focusInEvent( QFocusEvent * ); + void focusOutEvent( QFocusEvent * ); + void resizeEvent( QResizeEvent * ); + void drawContents( QPainter * ); +#ifndef QT_NO_DRAGANDDROP + void dragEnterEvent( QDragEnterEvent * ); + void dragMoveEvent( QDragMoveEvent *e ); + void dragLeaveEvent( QDragLeaveEvent *e ); + void dropEvent( QDropEvent * ); +#endif + void contextMenuEvent( QContextMenuEvent * ); + bool sendMouseEventToInputContext( QMouseEvent *e ); + virtual QPopupMenu *createPopupMenu(); + void windowActivationChange( bool ); +#ifndef QT_NO_COMPAT + void repaintArea( int, int ) { update(); } +#endif + +private slots: + void clipboardChanged(); + +public: + void setPasswordChar( QChar c ); // internal obsolete + QChar passwordChar() const; // obsolete internal + int characterAt( int, QChar* ) const; // obsolete + bool getSelection( int *, int * ); // obsolete + +private: + friend struct QLineEditPrivate; + QLineEditPrivate * d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QLineEdit( const QLineEdit & ); + QLineEdit &operator=( const QLineEdit & ); +#endif +}; + + +#endif // QT_NO_LINEEDIT + +#endif // QLINEEDIT_H diff --git a/src/widgets/qlistbox.cpp b/src/widgets/qlistbox.cpp new file mode 100644 index 0000000..f5e0057 --- /dev/null +++ b/src/widgets/qlistbox.cpp @@ -0,0 +1,4693 @@ +/********************************************************************** +** +** Implementation of QListBox widget class +** +** Created : 941121 +** +** 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 "qglobal.h" +#if defined(Q_CC_BOR) +// needed for qsort() because of a std namespace problem on Borland +#include "qplatformdefs.h" +#endif + +#include "qlistbox.h" +#ifndef QT_NO_LISTBOX +#include "qmemarray.h" +#include "qfontmetrics.h" +#include "qpainter.h" +#include "qstrlist.h" +#include "qpixmap.h" +#include "qapplication.h" +#include "qptrdict.h" +#include "qtimer.h" +#include "qstringlist.h" +#include "qstyle.h" +#include "qpopupmenu.h" +#include "qguardedptr.h" +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif + +#include <stdlib.h> + +class QListBoxPrivate +{ +public: + QListBoxPrivate( QListBox *lb ): + head( 0 ), last( 0 ), cache( 0 ), cacheIndex( -1 ), current( 0 ), + highlighted( 0 ), tmpCurrent( 0 ), columnPos( 1 ), rowPos( 1 ), rowPosCache( 0 ), + columnPosOne( 0 ), rowMode( QListBox::FixedNumber ), + columnMode( QListBox::FixedNumber ), numRows( 1 ), numColumns( 1 ), + currentRow( 0 ), currentColumn( 0 ), + mousePressRow( -1 ), mousePressColumn( -1 ), + mouseMoveRow( -1 ), mouseMoveColumn( -1 ), mouseInternalPress( FALSE ), + scrollTimer( 0 ), updateTimer( 0 ), visibleTimer( 0 ), + selectionMode( QListBox::Single ), + count( 0 ), + listBox( lb ), currInputString( QString::null ), + rowModeWins( FALSE ), + ignoreMoves( FALSE ), + layoutDirty( TRUE ), + mustPaintAll( TRUE ), + dragging( FALSE ), + dirtyDrag ( FALSE ), + variableHeight( TRUE /* !!! ### FALSE */ ), + variableWidth( FALSE ), + inMenuMode( FALSE ) + {} + int findItemByName( int item, const QString &text ); + ~QListBoxPrivate(); + + QListBoxItem * head, *last, *cache; + int cacheIndex; + QListBoxItem * current, *highlighted, *tmpCurrent; + + QMemArray<int> columnPos; + QMemArray<int> rowPos; + int rowPosCache; + int columnPosOne; + + QListBox::LayoutMode rowMode; + QListBox::LayoutMode columnMode; + int numRows; + int numColumns; + + int currentRow; + int currentColumn; + int mousePressRow; + int mousePressColumn; + int mouseMoveRow; + int mouseMoveColumn; + bool mouseInternalPress; + + QTimer * scrollTimer; + QTimer * updateTimer; + QTimer * visibleTimer; + QTimer * resizeTimer; + + QPoint scrollPos; + + QListBox::SelectionMode selectionMode; + + int count; + + + QListBox *listBox; + QString currInputString; + QTimer *inputTimer; + + QListBoxItem *pressedItem, *selectAnchor; + + uint select :1; + uint pressedSelected :1; + uint rowModeWins :1; + uint ignoreMoves :1; + uint clearing :1; + uint layoutDirty :1; + uint mustPaintAll :1; + uint dragging :1; + uint dirtyDrag :1; + uint variableHeight :1; + uint variableWidth :1; + uint inMenuMode :1; + + QRect *rubber; + QPtrDict<bool> selectable; + + struct SortableItem { + QListBoxItem *item; + }; +}; + + +QListBoxPrivate::~QListBoxPrivate() +{ + Q_ASSERT( !head ); +} + + +/*! + \class QListBoxItem qlistbox.h + \brief The QListBoxItem class is the base class of all list box items. + + This class is an abstract base class used for all list box items. + If you need to insert customized items into a QListBox you must + inherit this class and reimplement paint(), height() and width(). + + \sa QListBox +*/ + +/*! + \fn bool QListBox::dragSelect() const + \internal +*/ + +/*! + \fn void QListBox::setDragSelect( bool ) + \internal +*/ + +/*! + \fn bool QListBox::autoScroll() const + \internal +*/ + +/*! + \fn void QListBox::setAutoScroll( bool ) + \internal +*/ + +/*! + \fn bool QListBox::autoScrollBar() const + + \obsolete + + Returns TRUE if vScrollBarMode() is \c Auto; otherwise returns + FALSE. +*/ + +/*! + \fn void QListBox::setAutoScrollBar( bool enable ) + + \obsolete + + If \a enable is TRUE sets setVScrollBarMode() to \c AlwaysOn; + otherwise sets setVScrollBarMode() to \c AlwaysOff. +*/ + +/*! + \fn bool QListBox::scrollBar() const + + \obsolete + + Returns FALSE if vScrollBarMode() is \c AlwaysOff; otherwise + returns TRUE. +*/ + +/*! + \fn void QListBox::setScrollBar( bool enable ) + + \obsolete + + If \a enable is TRUE sets setVScrollBarMode() to \c AlwaysOn; + otherwise sets setVScrollBarMode() to \c AlwaysOff. +*/ + +/*! + \fn bool QListBox::autoBottomScrollBar() const + + \obsolete + + Returns TRUE if hScrollBarMode() is \c Auto; otherwise returns + FALSE. +*/ + +/*! + \fn void QListBox::setAutoBottomScrollBar( bool enable ) + + \obsolete + + If \a enable is TRUE sets setHScrollBarMode() to \c AlwaysOn; + otherwise sets setHScrollBarMode() to \c AlwaysOff. +*/ + +/*! + \fn bool QListBox::bottomScrollBar() const + + \obsolete + + Returns FALSE if vScrollBarMode() is \c AlwaysOff; otherwise + returns TRUE. +*/ + +/*! + \fn void QListBox::setBottomScrollBar( bool enable ) + + \obsolete + + If \a enable is TRUE sets setHScrollBarMode() to \c AlwaysOn; + otherwise sets setHScrollBarMode() to \c AlwaysOff. +*/ + +/*! + \fn bool QListBox::smoothScrolling() const + \internal +*/ + +/*! + \fn void QListBox::setSmoothScrolling( bool ) + \internal +*/ + +/*! + \fn bool QListBox::autoUpdate() const + \internal +*/ + +/*! + \fn void QListBox::setAutoUpdate( bool ) + \internal +*/ + +/*! + \fn void QListBox::setFixedVisibleLines( int lines ) + \internal +*/ + +/*! + \obsolete + \fn int QListBox::cellHeight( int i ) const + Returns the item height of item \a i. + \sa itemHeight() +*/ + +/*! + \obsolete + \overload int QListBox::cellHeight() const + Returns the item height of the first item, item 0. + \sa itemHeight() +*/ + +/*! + \obsolete + \fn int QListBox::cellWidth() const + Returns the maximum item width. + \sa maxItemWidth() +*/ + +/*! + \overload int QListBox::cellWidth(int i) const + \internal +*/ + +/*! + \obsolete + \fn int QListBox::numCols() const + Returns the number of columns. + \sa numColumns() +*/ + +/*! + \fn void QListBox::updateCellWidth() + \internal +*/ + +/*! + \obsolete + \fn int QListBox::totalWidth() const + Returns contentsWidth(). +*/ + +/*! + \obsolete + \fn int QListBox::totalHeight() const + Returns contentsHeight(). +*/ + +/*! + \obsolete + \fn int QListBox::findItem( int yPos ) const + Returns the index of the item a point (0, \a yPos). + \sa index() itemAt() +*/ + + +/*! + Constructs an empty list box item in the list box \a listbox. +*/ + +QListBoxItem::QListBoxItem( QListBox* listbox ) +{ + lbox = listbox; + s = FALSE; + dirty = TRUE; + custom_highlight = FALSE; + p = n = 0; + + // just something that'll look noticeable in the debugger + x = y = 42; + + if (listbox) + listbox->insertItem( this ); +} + +/*! + Constructs an empty list box item in the list box \a listbox and + inserts it after the item \a after or at the beginning if \a after + is 0. +*/ + +QListBoxItem::QListBoxItem( QListBox* listbox, QListBoxItem *after ) +{ + lbox = listbox; + s = FALSE; + dirty = TRUE; + custom_highlight = FALSE; + p = n = 0; + + // just something that'll be noticeable in the debugger + x = y = 42; + + if (listbox) + listbox->insertItem( this, after ); +} + + +/*! + Destroys the list box item. +*/ + +QListBoxItem::~QListBoxItem() +{ + if ( lbox ) + lbox->takeItem( this ); +} + + +/*! + Defines whether the list box item is responsible for drawing + itself in a highlighted state when being selected. + + If \a b is FALSE (the default), the list box will draw some + default highlight indicator before calling paint(). + + \sa selected(), paint() +*/ +void QListBoxItem::setCustomHighlighting( bool b ) +{ + custom_highlight = b; +} + +/*! + \fn void QListBoxItem::paint( QPainter *p ) + + Implement this function to draw your item. The painter, \a p, is + already open for painting. + + \sa height(), width() +*/ + +/*! + \fn int QListBoxItem::width( const QListBox* lb ) const + + Reimplement this function to return the width of your item. The \a + lb parameter is the same as listBox() and is provided for + convenience and compatibility. + + The default implementation returns + \l{QApplication::globalStrut()}'s width. + + \sa paint(), height() +*/ +int QListBoxItem::width(const QListBox*) const +{ + return QApplication::globalStrut().width(); +} + +/*! + \fn int QListBoxItem::height( const QListBox* lb ) const + + Implement this function to return the height of your item. The \a + lb parameter is the same as listBox() and is provided for + convenience and compatibility. + + The default implementation returns + \l{QApplication::globalStrut()}'s height. + + \sa paint(), width() +*/ +int QListBoxItem::height(const QListBox*) const +{ + return QApplication::globalStrut().height(); +} + + +/*! + Returns the text of the item. This text is also used for sorting. + + \sa setText() +*/ +QString QListBoxItem::text() const +{ + return txt; +} + +/*! + Returns the pixmap associated with the item, or 0 if there isn't + one. + + The default implementation returns 0. +*/ +const QPixmap *QListBoxItem::pixmap() const +{ + return 0; +} + +/*! + If \a b is TRUE (the default) then this item can be selected by + the user; otherwise this item cannot be selected by the user. + + \sa isSelectable() +*/ + +void QListBoxItem::setSelectable( bool b ) +{ + if ( !listBox() ) + return; + bool *sel = listBox()->d->selectable.find( this ); + if ( !sel ) + listBox()->d->selectable.insert( this, new bool( b ) ); + else + listBox()->d->selectable.replace( this, new bool( b ) ); +} + +/*! + Returns TRUE if this item is selectable (the default); otherwise + returns FALSE. + + \sa setSelectable() +*/ + +bool QListBoxItem::isSelectable() const +{ + if ( !listBox() ) + return TRUE; + bool *sel = listBox()->d->selectable.find( ( (QListBoxItem*)this ) ); + if ( !sel ) + return TRUE; + else + return *sel; +} + +/*! + \fn void QListBoxItem::setText( const QString &text ) + + Sets the text of the QListBoxItem to \a text. This \a text is also + used for sorting. The text is not shown unless explicitly drawn in + paint(). + + \sa text() +*/ + + +/*! + \class QListBoxText qlistbox.h + \brief The QListBoxText class provides list box items that display text. + + \ingroup advanced + + The text is drawn in the widget's current font. If you need + several different fonts, you must implement your own subclass of + QListBoxItem. + + \sa QListBox, QListBoxItem +*/ + + +/*! + Constructs a list box item in list box \a listbox showing the text + \a text. +*/ +QListBoxText::QListBoxText( QListBox *listbox, const QString &text ) + :QListBoxItem( listbox ) +{ + setText( text ); +} + +/*! + Constructs a list box item showing the text \a text. +*/ + +QListBoxText::QListBoxText( const QString &text ) + :QListBoxItem() +{ + setText( text ); +} + +/*! + Constructs a list box item in list box \a listbox showing the text + \a text. The item is inserted after the item \a after, or at the + beginning if \a after is 0. +*/ + +QListBoxText::QListBoxText( QListBox* listbox, const QString &text, QListBoxItem *after ) + : QListBoxItem( listbox, after ) +{ + setText( text ); +} + +/*! + Destroys the item. +*/ + +QListBoxText::~QListBoxText() +{ +} + +/*! + Draws the text using \a painter. +*/ + +void QListBoxText::paint( QPainter *painter ) +{ + int itemHeight = height( listBox() ); + QFontMetrics fm = painter->fontMetrics(); + int yPos = ( ( itemHeight - fm.height() ) / 2 ) + fm.ascent(); + painter->drawText( 3, yPos, text() ); +} + +/*! + Returns the height of a line of text in list box \a lb. + + \sa paint(), width() +*/ + +int QListBoxText::height( const QListBox* lb ) const +{ + int h = lb ? lb->fontMetrics().lineSpacing() + 2 : 0; + return QMAX( h, QApplication::globalStrut().height() ); +} + +/*! + Returns the width of this line in list box \a lb. + + \sa paint(), height() +*/ + +int QListBoxText::width( const QListBox* lb ) const +{ + int w = lb ? lb->fontMetrics().width( text() ) + 6 : 0; + return QMAX( w, QApplication::globalStrut().width() ); +} + +int QListBoxText::RTTI = 1; + +/*! + \fn int QListBoxText::rtti() const + + \reimp + + Returns 1. + + Make your derived classes return their own values for rtti(), and + you can distinguish between listbox items. You should use values + greater than 1000 preferably a large random number, to allow for + extensions to this class. +*/ + +int QListBoxText::rtti() const +{ + return RTTI; +} + +/*! + \class QListBoxPixmap qlistbox.h + \brief The QListBoxPixmap class provides list box items with a + pixmap and optional text. + + \ingroup advanced + + Items of this class are drawn with the pixmap on the left with the + optional text to the right of the pixmap. + + \sa QListBox, QListBoxItem +*/ + + +/*! + Constructs a new list box item in list box \a listbox showing the + pixmap \a pixmap. +*/ + +QListBoxPixmap::QListBoxPixmap( QListBox* listbox, const QPixmap &pixmap ) + : QListBoxItem( listbox ) +{ + pm = pixmap; +} + +/*! + Constructs a new list box item showing the pixmap \a pixmap. +*/ + +QListBoxPixmap::QListBoxPixmap( const QPixmap &pixmap ) + : QListBoxItem() +{ + pm = pixmap; +} + +/*! + Constructs a new list box item in list box \a listbox showing the + pixmap \a pixmap. The item gets inserted after the item \a after, + or at the beginning if \a after is 0. +*/ + +QListBoxPixmap::QListBoxPixmap( QListBox* listbox, const QPixmap &pixmap, QListBoxItem *after ) + : QListBoxItem( listbox, after ) +{ + pm = pixmap; +} + + +/*! + Destroys the item. +*/ + +QListBoxPixmap::~QListBoxPixmap() +{ +} + + +/*! + Constructs a new list box item in list box \a listbox showing the + pixmap \a pix and the text \a text. +*/ +QListBoxPixmap::QListBoxPixmap( QListBox* listbox, const QPixmap &pix, const QString& text) + : QListBoxItem( listbox ) +{ + pm = pix; + setText( text ); +} + +/*! + Constructs a new list box item showing the pixmap \a pix and the + text to \a text. +*/ +QListBoxPixmap::QListBoxPixmap( const QPixmap & pix, const QString& text) + : QListBoxItem() +{ + pm = pix; + setText( text ); +} + +/*! + Constructs a new list box item in list box \a listbox showing the + pixmap \a pix and the string \a text. The item gets inserted after + the item \a after, or at the beginning if \a after is 0. +*/ +QListBoxPixmap::QListBoxPixmap( QListBox* listbox, const QPixmap & pix, const QString& text, + QListBoxItem *after ) + : QListBoxItem( listbox, after ) +{ + pm = pix; + setText( text ); +} + +/*! + \fn const QPixmap *QListBoxPixmap::pixmap() const + + Returns the pixmap associated with the item. +*/ + + +/*! + Draws the pixmap using \a painter. +*/ + +void QListBoxPixmap::paint( QPainter *painter ) +{ + int itemHeight = height( listBox() ); + int yPos; + + const QPixmap *pm = pixmap(); + if ( pm && ! pm->isNull() ) { + yPos = ( itemHeight - pm->height() ) / 2; + painter->drawPixmap( 3, yPos, *pm); + } + + if ( !text().isEmpty() ) { + QFontMetrics fm = painter->fontMetrics(); + yPos = ( ( itemHeight - fm.height() ) / 2 ) + fm.ascent(); + painter->drawText( pm->width() + 5, yPos, text() ); + } +} + +/*! + Returns the height of the pixmap in list box \a lb. + + \sa paint(), width() +*/ + +int QListBoxPixmap::height( const QListBox* lb ) const +{ + int h; + if ( text().isEmpty() ) + h = pm.height(); + else + h = QMAX( pm.height(), lb->fontMetrics().lineSpacing() + 2 ); + return QMAX( h, QApplication::globalStrut().height() ); +} + +/*! + Returns the width of the pixmap plus some margin in list box \a lb. + + \sa paint(), height() +*/ + +int QListBoxPixmap::width( const QListBox* lb ) const +{ + if ( text().isEmpty() ) + return QMAX( pm.width() + 6, QApplication::globalStrut().width() ); + return QMAX( pm.width() + lb->fontMetrics().width( text() ) + 6, + QApplication::globalStrut().width() ); +} + +int QListBoxPixmap::RTTI = 2; + +/*! + \fn int QListBoxPixmap::rtti() const + + \reimp + + Returns 2. + + Make your derived classes return their own values for rtti(), and + you can distinguish between listbox items. You should use values + greater than 1000 preferably a large random number, to allow for + extensions to this class. +*/ + +int QListBoxPixmap::rtti() const +{ + return RTTI; +} + +/*! + \class QListBox qlistbox.h + \brief The QListBox widget provides a list of selectable, read-only items. + + \ingroup advanced + \mainclass + + This is typically a single-column list in which either no item or + one item is selected, but it can also be used in many other ways. + + QListBox will add scroll bars as necessary, but it isn't intended + for \e really big lists. If you want more than a few thousand + items, it's probably better to use a different widget mainly + because the scroll bars won't provide very good navigation, but + also because QListBox may become slow with huge lists. (See + QListView and QTable for possible alternatives.) + + There are a variety of selection modes described in the + QListBox::SelectionMode documentation. The default is \c Single + selection mode, but you can change it using setSelectionMode(). + (setMultiSelection() is still provided for compatibility with Qt + 1.x. We recommend using setSelectionMode() in all code.) + + Because QListBox offers multiple selection it must display + keyboard focus and selection state separately. Therefore there are + functions both to set the selection state of an item, i.e. + setSelected(), and to set which item displays keyboard focus, i.e. + setCurrentItem(). + + The list box normally arranges its items in a single column and + adds a vertical scroll bar if required. It is possible to have a + different fixed number of columns (setColumnMode()), or as many + columns as will fit in the list box's assigned screen space + (setColumnMode(FitToWidth)), or to have a fixed number of rows + (setRowMode()) or as many rows as will fit in the list box's + assigned screen space (setRowMode(FitToHeight)). In all these + cases QListBox will add scroll bars, as appropriate, in at least + one direction. + + If multiple rows are used, each row can be as high as necessary + (the normal setting), or you can request that all items will have + the same height by calling setVariableHeight(FALSE). The same + applies to a column's width, see setVariableWidth(). + + The QListBox's items are QListBoxItem objects. QListBox provides + methods to insert new items as strings, as pixmaps, and as + QListBoxItem * (insertItem() with various arguments), and to + replace an existing item with a new string, pixmap or QListBoxItem + (changeItem() with various arguments). You can also remove items + singly with removeItem() or clear() the entire list box. Note that + if you create a QListBoxItem yourself and insert it, QListBox + takes ownership of the item. + + You can also create a QListBoxItem, such as QListBoxText or + QListBoxPixmap, with the list box as first parameter. The item + will then append itself. When you delete an item it is + automatically removed from the list box. + + The list of items can be arbitrarily large; QListBox will add + scroll bars if necessary. QListBox can display a single-column + (the common case) or multiple-columns, and offers both single and + multiple selection. QListBox does not support multiple-column + items (but QListView and QTable do), or tree hierarchies (but + QListView does). + + The list box items can be accessed both as QListBoxItem objects + (recommended) and using integer indexes (the original QListBox + implementation used an array of strings internally, and the API + still supports this mode of operation). Everything can be done + using the new objects, and most things can be done using indexes. + + Each item in a QListBox contains a QListBoxItem. One of the items + can be the current item. The currentChanged() signal and the + highlighted() signal are emitted when a new item becomes current, + e.g. because the user clicks on it or QListBox::setCurrentItem() + is called. The selected() signal is emitted when the user + double-clicks on an item or presses Enter on the current item. + + If the user does not select anything, no signals are emitted and + currentItem() returns -1. + + A list box has \c WheelFocus as a default focusPolicy(), i.e. it + can get keyboard focus by tabbing, clicking and through the use of + the mouse wheel. + + New items can be inserted using insertItem(), insertStrList() or + insertStringList(). inSort() is obsolete because this method is + quite inefficient. It's preferable to insert the items normally + and call sort() afterwards, or to insert a sorted QStringList(). + + By default, vertical and horizontal scroll bars are added and + removed as necessary. setHScrollBarMode() and setVScrollBarMode() + can be used to change this policy. + + If you need to insert types other than strings and pixmaps, you + must define new classes which inherit QListBoxItem. + + \warning The list box assumes ownership of all list box items and + will delete them when it does not need them any more. + + <img src=qlistbox-m.png> <img src=qlistbox-w.png> + + \sa QListView QComboBox QButtonGroup + \link guibooks.html#fowler GUI Design Handbook: List Box (two + sections)\endlink +*/ + +/*! + \enum QListBox::SelectionMode + + This enumerated type is used by QListBox to indicate how it reacts + to selection by the user. + + \value Single When the user selects an item, any already-selected + item becomes unselected and the user cannot unselect the selected + item. This means that the user can never clear the selection, even + though the selection may be cleared by the application programmer + using QListBox::clearSelection(). + + \value Multi When the user selects an item the selection status + of that item is toggled and the other items are left alone. Also, + multiple items can be selected by dragging the mouse while the + left mouse button is kept pressed. + + \value Extended When the user selects an item the selection is + cleared and the new item selected. However, if the user presses + the Ctrl key when clicking on an item, the clicked item gets + toggled and all other items are left untouched. And if the user + presses the Shift key while clicking on an item, all items between + the current item and the clicked item get selected or unselected, + depending on the state of the clicked item. Also, multiple items + can be selected by dragging the mouse while the left mouse button + is kept pressed. + + \value NoSelection Items cannot be selected. + + In other words, \c Single is a real single-selection list box, \c + Multi is a real multi-selection list box, \c Extended is a list + box in which users can select multiple items but usually want to + select either just one or a range of contiguous items, and \c + NoSelection is for a list box where the user can look but not + touch. +*/ + + +/*! + \enum QListBox::LayoutMode + + This enum type is used to specify how QListBox lays out its rows + and columns. + + \value FixedNumber There is a fixed number of rows (or columns). + + \value FitToWidth There are as many columns as will fit + on-screen. + + \value FitToHeight There are as many rows as will fit on-screen. + + \value Variable There are as many rows as are required by the + column mode. (Or as many columns as required by the row mode.) + + Example: When you call setRowMode( FitToHeight ), columnMode() + automatically becomes \c Variable to accommodate the row mode + you've set. +*/ + +/*! + \fn void QListBox::onItem( QListBoxItem *i ) + + This signal is emitted when the user moves the mouse cursor onto + an item, similar to the QWidget::enterEvent() function. \a i is + the QListBoxItem that the mouse has moved on. +*/ + +// ### bug here too? enter/leave event may noit considered. move the +// mouse out of the window and back in, to the same item - does it +// work? + +/*! + \fn void QListBox::onViewport() + + This signal is emitted when the user moves the mouse cursor from + an item to an empty part of the list box. +*/ + + +/*! + Constructs a new empty list box called \a name and with parent \a + parent. + + Performance is boosted by modifying the widget flags \a f so that + only part of the QListBoxItem children is redrawn. This may be + unsuitable for custom QListBoxItem classes, in which case \c + WStaticContents and \c WNoAutoErase should be cleared + immediately after construction. + + \sa QWidget::clearWFlags() Qt::WidgetFlags +*/ + +QListBox::QListBox( QWidget *parent, const char *name, WFlags f ) + : QScrollView( parent, name, f | WStaticContents | WNoAutoErase ) +{ + d = new QListBoxPrivate( this ); + d->updateTimer = new QTimer( this, "listbox update timer" ); + d->visibleTimer = new QTimer( this, "listbox visible timer" ); + d->inputTimer = new QTimer( this, "listbox input timer" ); + d->resizeTimer = new QTimer( this, "listbox resize timer" ); + d->clearing = FALSE; + d->pressedItem = 0; + d->selectAnchor = 0; + d->select = FALSE; + d->rubber = 0; + d->selectable.setAutoDelete( TRUE ); + + setMouseTracking( TRUE ); + viewport()->setMouseTracking( TRUE ); + + connect( d->updateTimer, SIGNAL(timeout()), + this, SLOT(refreshSlot()) ); + connect( d->visibleTimer, SIGNAL(timeout()), + this, SLOT(ensureCurrentVisible()) ); + connect( d->resizeTimer, SIGNAL( timeout() ), + this, SLOT( adjustItems() ) ); + viewport()->setBackgroundMode( PaletteBase ); + setBackgroundMode( PaletteBackground, PaletteBase ); + viewport()->setFocusProxy( this ); + viewport()->setFocusPolicy( WheelFocus ); +} + + +QListBox * QListBox::changedListBox = 0; + +/*! + Destroys the list box. Deletes all list box items. +*/ + +QListBox::~QListBox() +{ + if ( changedListBox == this ) + changedListBox = 0; + clear(); + delete d; + d = 0; +} + +/*! + \fn void QListBox::pressed( QListBoxItem *item ) + + This signal is emitted when the user presses any mouse button. If + \a item is not 0, the cursor is on \a item. If \a item is 0, the + mouse cursor isn't on any item. + + Note that you must not delete any QListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \overload void QListBox::pressed( QListBoxItem *item, const QPoint &pnt ) + + This signal is emitted when the user presses any mouse button. If + \a item is not 0, the cursor is on \a item. If \a item is 0, the + mouse cursor isn't on any item. + + \a pnt is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). + + Note that you must not delete any QListBoxItem objects in slots + connected to this signal. + + \sa mouseButtonPressed() rightButtonPressed() clicked() +*/ + +/*! + \fn void QListBox::clicked( QListBoxItem *item ) + + This signal is emitted when the user clicks any mouse button. If + \a item is not 0, the cursor is on \a item. If \a item is 0, the + mouse cursor isn't on any item. + + Note that you must not delete any QListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \overload void QListBox::clicked( QListBoxItem *item, const QPoint &pnt ) + + This signal is emitted when the user clicks any mouse button. If + \a item is not 0, the cursor is on \a item. If \a item is 0, the + mouse cursor isn't on any item. + + \a pnt is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). (If the click's + press and release differs by a pixel or two, \a pnt is the + position at release time.) + + Note that you must not delete any QListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \fn void QListBox::mouseButtonClicked (int button, QListBoxItem * item, const QPoint & pos) + + This signal is emitted when the user clicks mouse button \a + button. If \a item is not 0, the cursor is on \a item. If \a item + is 0, the mouse cursor isn't on any item. + + \a pos is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). (If the click's + press and release differs by a pixel or two, \a pos is the + position at release time.) + + Note that you must not delete any QListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \fn void QListBox::mouseButtonPressed (int button, QListBoxItem * item, const QPoint & pos) + + This signal is emitted when the user presses mouse button \a + button. If \a item is not 0, the cursor is on \a item. If \a item + is 0, the mouse cursor isn't on any item. + + \a pos is the position of the mouse cursor in the global + coordinate system (QMouseEvent::globalPos()). + + Note that you must not delete any QListBoxItem objects in slots + connected to this signal. +*/ + +/*! + \fn void QListBox::doubleClicked( QListBoxItem *item ) + + This signal is emitted whenever an item is double-clicked. It's + emitted on the second button press, not the second button release. + If \a item is not 0, the cursor is on \a item. If \a item is 0, + the mouse cursor isn't on any item. +*/ + + +/*! + \fn void QListBox::returnPressed( QListBoxItem * ) + + This signal is emitted when Enter or Return is pressed. The + argument is currentItem(). +*/ + +/*! + \fn void QListBox::rightButtonClicked( QListBoxItem *, const QPoint& ) + + This signal is emitted when the right button is clicked (i.e. when + it's released at the same point where it was pressed). The + arguments are the relevant QListBoxItem (may be 0) and the point + in global coordinates. +*/ + + +/*! + \fn void QListBox::rightButtonPressed (QListBoxItem *, const QPoint & ) + + This signal is emitted when the right button is pressed. The + arguments are the relevant QListBoxItem (may be 0) and the point + in global coordinates. +*/ + +/*! + \fn void QListBox::contextMenuRequested( QListBoxItem *item, const QPoint & pos ) + + This signal is emitted when the user invokes a context menu with + the right mouse button or with special system keys, with \a item + being the item under the mouse cursor or the current item, + respectively. + + \a pos is the position for the context menu in the global + coordinate system. +*/ + +/*! + \fn void QListBox::selectionChanged() + + This signal is emitted when the selection set of a list box + changes. This signal is emitted in each selection mode. If the + user selects five items by drag-selecting, QListBox tries to emit + just one selectionChanged() signal so the signal can be connected + to computationally expensive slots. + + \sa selected() currentItem() +*/ + +/*! + \overload void QListBox::selectionChanged( QListBoxItem *item ) + + This signal is emitted when the selection in a \c Single selection + list box changes. \a item is the newly selected list box item. + + \sa selected() currentItem() +*/ + +/*! + \fn void QListBox::currentChanged( QListBoxItem *item ) + + This signal is emitted when the user makes a new item the current + item. \a item is the new current list box item. + + \sa setCurrentItem() currentItem() +*/ + +/*! + \fn void QListBox::highlighted( int index ) + + This signal is emitted when the user makes a new item the current + item. \a index is the index of the new current item. + + \sa currentChanged() selected() currentItem() selectionChanged() +*/ + +/*! + \overload void QListBox::highlighted( QListBoxItem * ) + + This signal is emitted when the user makes a new item the current + item. The argument is a pointer to the new current item. + + \sa currentChanged() selected() currentItem() selectionChanged() +*/ + +/*! + \overload void QListBox::highlighted( const QString & ) + + This signal is emitted when the user makes a new item the current + item and the item is (or has) a string. The argument is the text + of the new current item. + + \sa currentChanged() selected() currentItem() selectionChanged() +*/ + +/*! + \fn void QListBox::selected( int index ) + + This signal is emitted when the user double-clicks on an item or + presses Enter on the current item. \a index is the index of the + selected item. + + \sa currentChanged() highlighted() selectionChanged() +*/ + +/*! + \overload void QListBox::selected( QListBoxItem * ) + + This signal is emitted when the user double-clicks on an item or + presses Enter on the current item. The argument is a pointer to + the new selected item. + + \sa currentChanged() highlighted() selectionChanged() +*/ + +/*! + \overload void QListBox::selected( const QString &) + + This signal is emitted when the user double-clicks on an item or + presses Enter on the current item, and the item is (or has) a + string. The argument is the text of the selected item. + + \sa currentChanged() highlighted() selectionChanged() +*/ + +/*! \reimp */ + +void QListBox::setFont( const QFont &font ) +{ + QScrollView::setFont( font ); + triggerUpdate( TRUE ); +} + + +/*! + \property QListBox::count + \brief the number of items in the list box +*/ + +uint QListBox::count() const +{ + return d->count; +} + + +/*! + Inserts the string list \a list into the list at position \a + index. + + If \a index is negative, \a list is inserted at the end of the + list. If \a index is too large, the operation is ignored. + + \warning This function uses \c{const char *} rather than QString, + so we recommend against using it. It is provided so that legacy + code will continue to work, and so that programs that certainly + will not need to handle code outside a single 8-bit locale can use + it. See insertStringList() which uses real QStrings. + + \warning This function is never significantly faster than a loop + around insertItem(). + + \sa insertItem(), insertStringList() +*/ + +void QListBox::insertStrList( const QStrList *list, int index ) +{ + if ( !list ) { +#if defined(QT_CHECK_NULL) + Q_ASSERT( list != 0 ); +#endif + return; + } + insertStrList( *list, index ); +} + + + +/*! + Inserts the string list \a list into the list at position \a + index. + + If \a index is negative, \a list is inserted at the end of the + list. If \a index is too large, the operation is ignored. + + \warning This function is never significantly faster than a loop + around insertItem(). + + \sa insertItem(), insertStrList() +*/ + +void QListBox::insertStringList( const QStringList & list, int index ) +{ + if ( index < 0 ) + index = count(); + for ( QStringList::ConstIterator it = list.begin(); it != list.end(); ++it ) + insertItem( new QListBoxText(*it), index++ ); +} + + + +/*! + \overload + + Inserts the string list \a list into the list at position \a + index. + + If \a index is negative, \a list is inserted at the end of the + list. If \a index is too large, the operation is ignored. + + \warning This function uses \c{const char *} rather than QString, + so we recommend against using it. It is provided so that legacy + code will continue to work, and so that programs that certainly + will not need to handle code outside a single 8-bit locale can use + it. See insertStringList() which uses real QStrings. + + \warning This function is never significantly faster than a loop + around insertItem(). + + \sa insertItem(), insertStringList() +*/ + +void QListBox::insertStrList( const QStrList & list, int index ) +{ + QStrListIterator it( list ); + const char* txt; + if ( index < 0 ) + index = count(); + while ( (txt=it.current()) ) { + ++it; + insertItem( new QListBoxText(QString::fromLatin1(txt)), + index++ ); + } + if ( hasFocus() && !d->current ) + setCurrentItem( d->head ); +} + + +/*! + \overload + + Inserts the \a numStrings strings of the array \a strings into the + list at position \a index. + + If \a index is negative, insertStrList() inserts \a strings at the + end of the list. If \a index is too large, the operation is + ignored. + + \warning This function uses \c{const char *} rather than QString, + so we recommend against using it. It is provided so that legacy + code will continue to work, and so that programs that certainly + will not need to handle code outside a single 8-bit locale can use + it. See insertStringList() which uses real QStrings. + + \warning This function is never significantly faster than a loop + around insertItem(). + + \sa insertItem(), insertStringList() +*/ + +void QListBox::insertStrList( const char **strings, int numStrings, int index ) +{ + if ( !strings ) { +#if defined(QT_CHECK_NULL) + Q_ASSERT( strings != 0 ); +#endif + return; + } + if ( index < 0 ) + index = count(); + int i = 0; + while ( (numStrings<0 && strings[i]!=0) || i<numStrings ) { + insertItem( new QListBoxText( QString::fromLatin1(strings[i])), + index + i ); + i++; + } + if ( hasFocus() && !d->current ) + setCurrentItem( d->head ); +} + +/*! + Inserts the item \a lbi into the list at position \a index. + + If \a index is negative or larger than the number of items in the + list box, \a lbi is inserted at the end of the list. + + \sa insertStrList() +*/ + +void QListBox::insertItem( const QListBoxItem *lbi, int index ) +{ +#if defined ( QT_CHECK_NULL ) + Q_ASSERT( lbi != 0 ); +#else + if ( !lbi ) + return; +#endif + + if ( index < 0 ) + index = d->count; + + if ( index >= d->count ) { + insertItem( lbi, d->last ); + return; + } + + QListBoxItem * item = (QListBoxItem *)lbi; + d->count++; + d->cache = 0; + + item->lbox = this; + if ( !d->head || index == 0 ) { + item->n = d->head; + item->p = 0; + d->head = item; + item->dirty = TRUE; + if ( item->n ) + item->n->p = item; + } else { + QListBoxItem * i = d->head; + while ( i->n && index > 1 ) { + i = i->n; + index--; + } + if ( i->n ) { + item->n = i->n; + item->p = i; + item->n->p = item; + item->p->n = item; + } else { + i->n = item; + item->p = i; + item->n = 0; + } + } + + if ( hasFocus() && !d->current ) { + d->current = d->head; + updateItem( d->current ); + emit highlighted( d->current ); + emit highlighted( d->current->text() ); + emit highlighted( index ); + } + + triggerUpdate( TRUE ); +} + +/*! + \overload + + Inserts the item \a lbi into the list after the item \a after, or + at the beginning if \a after is 0. + + \sa insertStrList() +*/ + +void QListBox::insertItem( const QListBoxItem *lbi, const QListBoxItem *after ) +{ +#if defined ( QT_CHECK_NULL ) + Q_ASSERT( lbi != 0 ); +#else + if ( !lbi ) + return; +#endif + + QListBoxItem * item = (QListBoxItem*)lbi; + d->count++; + d->cache = 0; + + item->lbox = this; + if ( !d->head || !after ) { + item->n = d->head; + item->p = 0; + d->head = item; + item->dirty = TRUE; + if ( item->n ) + item->n->p = item; + } else { + QListBoxItem * i = (QListBoxItem*) after; + if ( i ) { + item->n = i->n; + item->p = i; + if ( item->n ) + item->n->p = item; + if ( item->p ) + item->p->n = item; + } + } + + if ( after == d->last ) + d->last = (QListBoxItem*) lbi; + + if ( hasFocus() && !d->current ) { + d->current = d->head; + updateItem( d->current ); + emit highlighted( d->current ); + emit highlighted( d->current->text() ); + emit highlighted( index( d->current ) ); + } + + triggerUpdate( TRUE ); +} + +/*! + \overload + + Inserts a new list box text item with the text \a text into the + list at position \a index. + + If \a index is negative, \a text is inserted at the end of the + list. + + \sa insertStrList() +*/ + +void QListBox::insertItem( const QString &text, int index ) +{ + insertItem( new QListBoxText(text), index ); +} + +/*! + \overload + + Inserts a new list box pixmap item with the pixmap \a pixmap into + the list at position \a index. + + If \a index is negative, \a pixmap is inserted at the end of the + list. + + \sa insertStrList() +*/ + +void QListBox::insertItem( const QPixmap &pixmap, int index ) +{ + insertItem( new QListBoxPixmap(pixmap), index ); +} + +/*! + \overload + + Inserts a new list box pixmap item with the pixmap \a pixmap and + the text \a text into the list at position \a index. + + If \a index is negative, \a pixmap is inserted at the end of the + list. + + \sa insertStrList() +*/ + +void QListBox::insertItem( const QPixmap &pixmap, const QString &text, int index ) +{ + insertItem( new QListBoxPixmap(pixmap, text), index ); +} + +/*! + Removes and deletes the item at position \a index. If \a index is + equal to currentItem(), a new item becomes current and the + currentChanged() and highlighted() signals are emitted. + + \sa insertItem(), clear() +*/ + +void QListBox::removeItem( int index ) +{ + bool wasVisible = itemVisible( currentItem() ); + delete item( index ); + triggerUpdate( TRUE ); + if ( wasVisible ) + ensureCurrentVisible(); +} + + +/*! + Deletes all the items in the list. + + \sa removeItem() +*/ + +void QListBox::clear() +{ + setContentsPos( 0, 0 ); + bool blocked = signalsBlocked(); + blockSignals( TRUE ); + d->clearing = TRUE; + d->current = 0; + d->tmpCurrent = 0; + QListBoxItem * i = d->head; + d->head = 0; + while ( i ) { + QListBoxItem * n = i->n; + i->n = i->p = 0; + delete i; + i = n; + } + d->count = 0; + d->numRows = 1; + d->numColumns = 1; + d->currentRow = 0; + d->currentColumn = 0; + d->mousePressRow = -1; + d->mousePressColumn = -1; + d->mouseMoveRow = -1; + d->mouseMoveColumn = -1; + d->selectable.clear(); + clearSelection(); + blockSignals( blocked ); + triggerUpdate( TRUE ); + d->last = 0; + d->clearing = FALSE; +} + + +/*! + Returns the text at position \a index, or QString::null if there + is no text at that position. + + \sa pixmap() +*/ + +QString QListBox::text( int index ) const +{ + QListBoxItem * i = item( index ); + if ( i ) + return i->text(); + return QString::null; +} + + +/*! + Returns a pointer to the pixmap at position \a index, or 0 if + there is no pixmap there. + + \sa text() +*/ + +const QPixmap *QListBox::pixmap( int index ) const +{ + QListBoxItem * i = item( index ); + if ( i ) + return i->pixmap(); + return 0; +} + +/*! + \overload + + Replaces the item at position \a index with a new list box text + item with text \a text. + + The operation is ignored if \a index is out of range. + + \sa insertItem(), removeItem() +*/ + +void QListBox::changeItem( const QString &text, int index ) +{ + if( index >= 0 && index < (int)count() ) + changeItem( new QListBoxText(text), index ); +} + +/*! + \overload + + Replaces the item at position \a index with a new list box pixmap + item with pixmap \a pixmap. + + The operation is ignored if \a index is out of range. + + \sa insertItem(), removeItem() +*/ + +void QListBox::changeItem( const QPixmap &pixmap, int index ) +{ + if( index >= 0 && index < (int)count() ) + changeItem( new QListBoxPixmap(pixmap), index ); +} + +/*! + \overload + + Replaces the item at position \a index with a new list box pixmap + item with pixmap \a pixmap and text \a text. + + The operation is ignored if \a index is out of range. + + \sa insertItem(), removeItem() +*/ + +void QListBox::changeItem( const QPixmap &pixmap, const QString &text, int index ) +{ + if( index >= 0 && index < (int)count() ) + changeItem( new QListBoxPixmap(pixmap, text), index ); +} + + + +/*! + Replaces the item at position \a index with \a lbi. If \a index is + negative or too large, changeItem() does nothing. + + The item that has been changed will become selected. + + \sa insertItem(), removeItem() +*/ + +void QListBox::changeItem( const QListBoxItem *lbi, int index ) +{ + if ( !lbi || index < 0 || index >= (int)count() ) + return; + + removeItem( index ); + insertItem( lbi, index ); + setCurrentItem( index ); +} + + +/*! + \property QListBox::numItemsVisible + \brief the number of visible items. + + Both partially and entirely visible items are counted. +*/ + +int QListBox::numItemsVisible() const +{ + doLayout(); + + int columns = 0; + + int x = contentsX(); + int i=0; + while ( i < (int)d->columnPos.size()-1 && + d->columnPos[i] < x ) + i++; + if ( i < (int)d->columnPos.size()-1 && + d->columnPos[i] > x ) + columns++; + x += visibleWidth(); + while ( i < (int)d->columnPos.size()-1 && + d->columnPos[i] < x ) { + i++; + columns++; + } + + int y = contentsY(); + int rows = 0; + while ( i < (int)d->rowPos.size()-1 && + d->rowPos[i] < y ) + i++; + if ( i < (int)d->rowPos.size()-1 && + d->rowPos[i] > y ) + rows++; + y += visibleHeight(); + while ( i < (int)d->rowPos.size()-1 && + d->rowPos[i] < y ) { + i++; + rows++; + } + + return rows*columns; +} + +int QListBox::currentItem() const +{ + if ( !d->current || !d->head ) + return -1; + + return index( d->current ); +} + + +/*! + \property QListBox::currentText + \brief the text of the current item. + + This is equivalent to text(currentItem()). +*/ + + +/*! + \property QListBox::currentItem + \brief the current highlighted item + + When setting this property, the highlighting is moved to the item + and the list box scrolled as necessary. + + If no item is current, currentItem() returns -1. +*/ + +void QListBox::setCurrentItem( int index ) +{ + setCurrentItem( item( index ) ); +} + + +/*! + \overload + + Sets the current item to the QListBoxItem \a i. +*/ +void QListBox::setCurrentItem( QListBoxItem * i ) +{ + if ( !i || d->current == i || numRows() == 0 ) + return; + + QRect mfrect = itemRect( i ); + if ( mfrect.isValid() ) + setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE ); + + QListBoxItem * o = d->current; + d->current = i; + int ind = index( i ); + + if ( i && selectionMode() == Single ) { + bool changed = FALSE; + if ( o && o->s ) { + changed = TRUE; + o->s = FALSE; + } + if ( i && !i->s && d->selectionMode != NoSelection && i->isSelectable() ) { + i->s = TRUE; + changed = TRUE; + emit selectionChanged( i ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::StateChanged ); +#endif + } + if ( changed ) { + emit selectionChanged(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection ); +#endif + } + } + + d->currentColumn = ind / numRows(); + d->currentRow = ind % numRows(); + if ( o ) + updateItem( o ); + if ( i ) + updateItem( i ); + // scroll after the items are redrawn + d->visibleTimer->start( 1, TRUE ); + + QString tmp; + if ( i ) + tmp = i->text(); + emit highlighted( i ); + if ( !tmp.isNull() ) + emit highlighted( tmp ); + emit highlighted( ind ); + emit currentChanged( i ); + +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::Focus ); +#endif +} + + +/*! + Returns a pointer to the item at position \a index, or 0 if \a + index is out of bounds. + + \sa index() +*/ + +QListBoxItem *QListBox::item( int index ) const +{ + if ( index < 0 || index > d->count -1 ) + return 0; + + QListBoxItem * i = d->head; + + if ( d->cache && index > 0 ) { + i = d->cache; + int idx = d->cacheIndex; + while ( i && idx < index ) { + idx++; + i = i->n; + } + while ( i && idx > index ) { + idx--; + i = i->p; + } + } else { + int idx = index; + while ( i && idx > 0 ) { + idx--; + i = i->n; + } + } + + if ( index > 0 ) { + d->cache = i; + d->cacheIndex = index; + } + + return i; +} + + +/*! + Returns the index of \a lbi, or -1 if the item is not in this list + box or \a lbi is 0. + + \sa item() +*/ + +int QListBox::index( const QListBoxItem * lbi ) const +{ + if ( !lbi ) + return -1; + QListBoxItem * i_n = d->head; + int c_n = 0; + if ( d->cache ) { + i_n = d->cache; + c_n = d->cacheIndex; + } + QListBoxItem* i_p = i_n; + int c_p = c_n; + while ( ( i_n != 0 || i_p != 0 ) && i_n != lbi && i_p != lbi ) { + if ( i_n ) { + c_n++; + i_n = i_n->n; + } + if ( i_p ) { + c_p--; + i_p = i_p->p; + } + } + if ( i_p == lbi ) + return c_p; + if ( i_n == lbi ) + return c_n; + return -1; +} + + + +/*! + Returns TRUE if the item at position \a index is at least partly + visible; otherwise returns FALSE. +*/ + +bool QListBox::itemVisible( int index ) +{ + QListBoxItem * i = item( index ); + return i ? itemVisible( i ) : FALSE; +} + + +/*! + \overload + + Returns TRUE if \a item is at least partly visible; otherwise + returns FALSE. +*/ + +bool QListBox::itemVisible( const QListBoxItem * item ) +{ + if ( d->layoutDirty ) + doLayout(); + + int i = index( item ); + int col = i / numRows(); + int row = i % numRows(); + return ( d->columnPos[col] < contentsX()+visibleWidth() && + d->rowPos[row] < contentsY()+visibleHeight() && + d->columnPos[col+1] > contentsX() && + d->rowPos[row+1] > contentsY() ); +} + + +/*! \reimp */ + +void QListBox::mousePressEvent( QMouseEvent *e ) +{ + mousePressEventEx( e ); +} + +void QListBox::mousePressEventEx( QMouseEvent *e ) +{ + d->mouseInternalPress = TRUE; + QListBoxItem * i = itemAt( e->pos() ); + + if ( !i && !d->current && d->head ) { + d->current = d->head; + updateItem( d->head ); + } + + if ( !i && ( d->selectionMode != Single || e->button() == RightButton ) + && !( e->state() & ControlButton ) ) + clearSelection(); + + d->select = d->selectionMode == Multi ? ( i ? !i->isSelected() : FALSE ) : TRUE; + d->pressedSelected = i && i->s; + + if ( i ) + d->selectAnchor = i; + if ( i ) { + switch( selectionMode() ) { + default: + case Single: + if ( !i->s || i != d->current ) { + if ( i->isSelectable() ) + setSelected( i, TRUE ); + else + setCurrentItem( i ); + } + break; + case Extended: + if ( i ) { + if ( !(e->state() & QMouseEvent::ShiftButton) && + !(e->state() & QMouseEvent::ControlButton) ) { + if ( !i->isSelected() ) { + bool b = signalsBlocked(); + blockSignals( TRUE ); + clearSelection(); + blockSignals( b ); + } + setSelected( i, TRUE ); + d->dragging = TRUE; // always assume dragging + } else if ( e->state() & ShiftButton ) { + d->pressedSelected = FALSE; + QListBoxItem *oldCurrent = item( currentItem() ); + bool down = index( oldCurrent ) < index( i ); + + QListBoxItem *lit = down ? oldCurrent : i; + bool select = d->select; + bool blocked = signalsBlocked(); + blockSignals( TRUE ); + for ( ;; lit = lit->n ) { + if ( !lit ) { + triggerUpdate( FALSE ); + break; + } + if ( down && lit == i ) { + setSelected( i, select ); + triggerUpdate( FALSE ); + break; + } + if ( !down && lit == oldCurrent ) { + setSelected( oldCurrent, select ); + triggerUpdate( FALSE ); + break; + } + setSelected( lit, select ); + } + blockSignals( blocked ); + emit selectionChanged(); + } else if ( e->state() & ControlButton ) { + setSelected( i, !i->isSelected() ); + d->pressedSelected = FALSE; + } + setCurrentItem( i ); + } + break; + case Multi: + //d->current = i; + setSelected( i, !i->s ); + setCurrentItem( i ); + break; + case NoSelection: + setCurrentItem( i ); + break; + } + } else { + bool unselect = TRUE; + if ( e->button() == LeftButton ) { + if ( d->selectionMode == Multi || + d->selectionMode == Extended ) { + d->tmpCurrent = d->current; + d->current = 0; + updateItem( d->tmpCurrent ); + if ( d->rubber ) + delete d->rubber; + d->rubber = 0; + d->rubber = new QRect( e->x(), e->y(), 0, 0 ); + + if ( d->selectionMode == Extended && !( e->state() & ControlButton ) ) + selectAll( FALSE ); + unselect = FALSE; + } + if ( unselect && ( e->button() == RightButton || + ( selectionMode() == Multi || selectionMode() == Extended ) ) ) + clearSelection(); + } + } + + // for sanity, in case people are event-filtering or whatnot + delete d->scrollTimer; + d->scrollTimer = 0; + if ( i ) { + d->mousePressColumn = d->currentColumn; + d->mousePressRow = d->currentRow; + } else { + d->mousePressColumn = -1; + d->mousePressRow = -1; + } + d->ignoreMoves = FALSE; + + d->pressedItem = i; + + emit pressed( i ); + emit pressed( i, e->globalPos() ); + emit mouseButtonPressed( e->button(), i, e->globalPos() ); + if ( e->button() == RightButton ) + emit rightButtonPressed( i, e->globalPos() ); +} + + +/*! \reimp */ + +void QListBox::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( d->selectionMode == Extended && + d->dragging ) { + d->dragging = FALSE; + if (d->current != d->pressedItem) { + updateSelection(); // when we drag, we get an update after we release + } + } + + if ( d->rubber ) { + drawRubber(); + delete d->rubber; + d->rubber = 0; + d->current = d->tmpCurrent; + updateItem( d->current ); + } + if ( d->scrollTimer ) + mouseMoveEvent( e ); + delete d->scrollTimer; + d->scrollTimer = 0; + d->ignoreMoves = FALSE; + + if ( d->selectionMode == Extended && + d->current == d->pressedItem && + d->pressedSelected && d->current ) { + bool block = signalsBlocked(); + blockSignals( TRUE ); + clearSelection(); + blockSignals( block ); + d->current->s = TRUE; + emit selectionChanged(); + } + + QListBoxItem * i = itemAt( e->pos() ); + bool emitClicked = d->mousePressColumn != -1 && d->mousePressRow != -1 || !d->pressedItem; + emitClicked = emitClicked && d->pressedItem == i; + d->pressedItem = 0; + d->mousePressRow = -1; + d->mousePressColumn = -1; + d->mouseInternalPress = FALSE; + if ( emitClicked ) { + emit clicked( i ); + emit clicked( i, e->globalPos() ); + emit mouseButtonClicked( e->button(), i, e->globalPos() ); + if ( e->button() == RightButton ) + emit rightButtonClicked( i, e->globalPos() ); + } +} + + +/*! \reimp */ + +void QListBox::mouseDoubleClickEvent( QMouseEvent *e ) +{ + bool ok = TRUE; + QListBoxItem *i = itemAt( e->pos() ); + if ( !i || selectionMode() == NoSelection ) + ok = FALSE; + + d->ignoreMoves = TRUE; + + if ( d->current && ok ) { + QListBoxItem * i = d->current; + QString tmp = d->current->text(); + emit selected( currentItem() ); + emit selected( i ); + if ( !tmp.isNull() ) + emit selected( tmp ); + emit doubleClicked( i ); + } +} + + +/*! \reimp */ + +void QListBox::mouseMoveEvent( QMouseEvent *e ) +{ + QListBoxItem * i = itemAt( e->pos() ); + if ( i != d->highlighted ) { + if ( i ) { + emit onItem( i ); + } else { + emit onViewport(); + } + d->highlighted = i; + } + + if ( d->rubber ) { + QRect r = d->rubber->normalize(); + drawRubber(); + d->rubber->setCoords( d->rubber->x(), d->rubber->y(), e->x(), e->y() ); + doRubberSelection( r, d->rubber->normalize() ); + drawRubber(); + return; + } + + if ( ( (e->state() & ( RightButton | LeftButton | MidButton ) ) == 0 ) || + d->ignoreMoves ) + return; + + // hack to keep the combo (and what else?) working: if we get a + // move outside the listbox without having seen a press, discard + // it. + if ( !QRect( 0, 0, visibleWidth(), visibleHeight() ).contains( e->pos() ) && + ( d->mousePressColumn < 0 && d->mousePressRow < 0 || + (e->state() == NoButton && !d->pressedItem) ) ) + return; + + // figure out in what direction to drag-select and perhaps scroll + int dx = 0; + int x = e->x(); + if ( x >= visibleWidth() ) { + x = visibleWidth()-1; + dx = 1; + } else if ( x < 0 ) { + x = 0; + dx = -1; + } + d->mouseMoveColumn = columnAt( x + contentsX() ); + + // sanitize mousePressColumn, if we got here without a mouse press event + if ( d->mousePressColumn < 0 && d->mouseMoveColumn >= 0 ) + d->mousePressColumn = d->mouseMoveColumn; + if ( d->mousePressColumn < 0 && d->currentColumn >= 0 ) + d->mousePressColumn = d->currentColumn; + + // if it's beyond the last column, use the last one + if ( d->mouseMoveColumn < 0 ) + d->mouseMoveColumn = dx >= 0 ? numColumns()-1 : 0; + + // repeat for y + int dy = 0; + int y = e->y(); + if ( y >= visibleHeight() ) { + y = visibleHeight()-1; + dy = 1; + } else if ( y < 0 ) { + y = 0; + dy = -1; + } + d->mouseMoveRow = rowAt( y + contentsY() ); + + if ( d->mousePressRow < 0 && d->mouseMoveRow >= 0 ) + d->mousePressRow = d->mouseMoveRow; + if ( d->mousePressRow < 0 && d->currentRow >= 0 ) + d->mousePressRow = d->currentRow; + + if ( d->mousePressRow < 0 ) + d->mousePressRow = rowAt( x + contentsX() ); + + d->scrollPos = QPoint( dx, dy ); + + if ( ( dx || dy ) && !d->scrollTimer && e->state() == LeftButton && e->button() != LeftButton ) { + // start autoscrolling if necessary + d->scrollTimer = new QTimer( this ); + connect( d->scrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll()) ); + d->scrollTimer->start( 100, FALSE ); + doAutoScroll(); + } else if ( !d->scrollTimer ) { + // or just select the required bits + updateSelection(); + } +} + + + +void QListBox::updateSelection() +{ + if ( d->mouseMoveColumn >= 0 && d->mouseMoveRow >= 0 && + d->mousePressColumn >= 0 && d->mousePressRow >= 0 ) { + QListBoxItem * i = item( d->mouseMoveColumn * numRows() + + d->mouseMoveRow ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + int ind = index(i); +#endif + if ( selectionMode() == Single || selectionMode() == NoSelection ) { + if ( i && ( d->mouseInternalPress || testWFlags(WType_Popup) ) ) + setCurrentItem( i ); + } else { + if ( d->selectionMode == Extended && ( + ( d->current == d->pressedItem && d->pressedSelected ) || + (d->dirtyDrag && !d->dragging) ) ) { + if (d->dirtyDrag && !d->dragging) // emit after dragging stops + d->dirtyDrag = FALSE; + else + clearSelection(); // dont reset drag-selected items + d->pressedItem = 0; + if ( i && i->isSelectable() ) { + bool block = signalsBlocked(); + blockSignals( TRUE ); + i->s = TRUE; + blockSignals( block ); + emit selectionChanged(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::StateChanged ); + QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection ); + QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::SelectionAdd ); +#endif + } + triggerUpdate( FALSE ); + } else { + int c = QMIN( d->mouseMoveColumn, d->mousePressColumn ); + int r = QMIN( d->mouseMoveRow, d->mousePressRow ); + int c2 = QMAX( d->mouseMoveColumn, d->mousePressColumn ); + int r2 = QMAX( d->mouseMoveRow, d->mousePressRow ); + bool changed = FALSE; + while( c <= c2 ) { + QListBoxItem * i = item( c*numRows()+r ); + int rtmp = r; + while( i && rtmp <= r2 ) { + if ( (bool)i->s != (bool)d->select && i->isSelectable() ) { + i->s = d->select; +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::StateChanged ); + QAccessible::updateAccessibility( viewport(), ind+1, d->select ? QAccessible::SelectionAdd : QAccessible::SelectionRemove ); +#endif + i->dirty = TRUE; + d->dirtyDrag = changed = TRUE; + } + i = i->n; + rtmp++; + } + c++; + } + if ( changed ) { + if (!d->dragging) // emit after dragging stops instead + emit selectionChanged(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection ); +#endif + triggerUpdate( FALSE ); + } + } + if ( i ) + setCurrentItem( i ); + } + } +} + +void QListBox::repaintSelection() +{ + if ( d->numColumns == 1 ) { + for ( uint i = topItem(); itemVisible( i ) && i < count(); ++i ) { + QListBoxItem *it = item(i); + if ( !it ) + break; + if ( it->isSelected() ) + updateItem( it ); + } + } else { + for ( uint i = 0; i < count(); ++i ) { + QListBoxItem *it = item(i); + if ( !it ) + break; + if ( it->isSelected() ) + updateItem( it ); + } + } +} + +/*! \reimp +*/ + +void QListBox::contentsContextMenuEvent( QContextMenuEvent *e ) +{ + if ( !receivers( SIGNAL(contextMenuRequested(QListBoxItem*,const QPoint&)) ) ) { + e->ignore(); + return; + } + if ( e->reason() == QContextMenuEvent::Keyboard ) { + QListBoxItem *i = item( currentItem() ); + if ( i ) { + QRect r = itemRect( i ); + emit contextMenuRequested( i, mapToGlobal( r.topLeft() + QPoint( width() / 2, r.height() / 2 ) ) ); + } + } else { + QListBoxItem * i = itemAt( contentsToViewport( e->pos() ) ); + emit contextMenuRequested( i, e->globalPos() ); + } +} + +/*!\reimp +*/ +void QListBox::keyPressEvent( QKeyEvent *e ) +{ + if ( ( e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab ) + && e->state() & Qt::ControlButton ) + e->ignore(); + + if ( count() == 0 ) { + e->ignore(); + return; + } + + QGuardedPtr<QListBox> selfCheck = this; + + QListBoxItem *old = d->current; + if ( !old ) { + setCurrentItem( d->head ); + if ( d->selectionMode == Single ) + setSelected( d->head, TRUE ); + e->ignore(); + return; + } + + bool selectCurrent = FALSE; + switch ( e->key() ) { + case Key_Up: + { + d->currInputString = QString::null; + if ( currentItem() > 0 ) { + setCurrentItem( currentItem() - 1 ); + handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton ); + } + if ( !( e->state() & ShiftButton ) || !d->selectAnchor ) + d->selectAnchor = d->current; + } + break; + case Key_Down: + { + d->currInputString = QString::null; + if ( currentItem() < (int)count() - 1 ) { + setCurrentItem( currentItem() + 1 ); + handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton ); + } + if ( !( e->state() & ShiftButton ) || !d->selectAnchor ) + d->selectAnchor = d->current; + } + break; + case Key_Left: + { + d->currInputString = QString::null; + if ( currentColumn() > 0 ) { + setCurrentItem( currentItem() - numRows() ); + handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton ); + } else if ( numColumns() > 1 && currentItem() > 0 ) { + int row = currentRow(); + setCurrentItem( currentRow() - 1 + ( numColumns() - 1 ) * numRows() ); + + if ( currentItem() == -1 ) + setCurrentItem( row - 1 + ( numColumns() - 2 ) * numRows() ); + + handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton ); + } else { + QApplication::sendEvent( horizontalScrollBar(), e ); + } + if ( !( e->state() & ShiftButton ) || !d->selectAnchor ) + d->selectAnchor = d->current; + } + break; + case Key_Right: + { + d->currInputString = QString::null; + if ( currentColumn() < numColumns()-1 ) { + int row = currentRow(); + int i = currentItem(); + QListBoxItem *it = item( i + numRows() ); + if ( !it ) + it = item( count()-1 ); + setCurrentItem( it ); + + if ( currentItem() == -1 ) { + if ( row < numRows() - 1 ) + setCurrentItem( row + 1 ); + else + setCurrentItem( i ); + } + + handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton ); + } else if ( numColumns() > 1 && currentRow() < numRows() ) { + if ( currentRow() + 1 < numRows() ) { + setCurrentItem( currentRow() + 1 ); + handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton ); + } + } else { + QApplication::sendEvent( horizontalScrollBar(), e ); + } + if ( !( e->state() & ShiftButton ) || !d->selectAnchor ) + d->selectAnchor = d->current; + } + break; + case Key_Next: + { + d->currInputString = QString::null; + int i = 0; + if ( numColumns() == 1 ) { + i = currentItem() + numItemsVisible(); + i = i > (int)count() - 1 ? (int)count() - 1 : i; + setCurrentItem( i ); + setBottomItem( i ); + } else { + // I'm not sure about this behavior... + if ( currentRow() == numRows() - 1 ) + i = currentItem() + numRows(); + else + i = currentItem() + numRows() - currentRow() - 1; + i = i > (int)count() - 1 ? (int)count() - 1 : i; + setCurrentItem( i ); + } + handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton ); + if ( !( e->state() & ShiftButton ) || !d->selectAnchor ) + d->selectAnchor = d->current; + } + break; + case Key_Prior: + { + selectCurrent = TRUE; + d->currInputString = QString::null; + int i; + if ( numColumns() == 1 ) { + i = currentItem() - numItemsVisible(); + i = i < 0 ? 0 : i; + setCurrentItem( i ); + setTopItem( i ); + } else { + // I'm not sure about this behavior... + if ( currentRow() == 0 ) + i = currentItem() - numRows(); + else + i = currentItem() - currentRow(); + i = i < 0 ? 0 : i; + setCurrentItem( i ); + } + handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton ); + if ( !( e->state() & ShiftButton ) || !d->selectAnchor ) + d->selectAnchor = d->current; + } + break; + case Key_Space: + { + selectCurrent = TRUE; + d->currInputString = QString::null; + toggleCurrentItem(); + if ( selectionMode() == Extended && d->current->isSelected() ) + emit highlighted( currentItem() ); + if (selfCheck && (!( e->state() & ShiftButton ) || !d->selectAnchor)) + d->selectAnchor = d->current; + } + break; + case Key_Return: + case Key_Enter: + { + selectCurrent = TRUE; + d->currInputString = QString::null; + if ( currentItem() >= 0 && selectionMode() != NoSelection ) { + QString tmp = item( currentItem() )->text(); + emit selected( currentItem()); + emit selected( item( currentItem() ) ); + if ( !tmp.isEmpty() ) + emit selected( tmp ); + emit returnPressed( item( currentItem() ) ); + } + if (selfCheck && (!( e->state() & ShiftButton ) || !d->selectAnchor)) + d->selectAnchor = d->current; + } + break; + case Key_Home: + { + selectCurrent = TRUE; + d->currInputString = QString::null; + setCurrentItem( 0 ); + handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton ); + if ( !( e->state() & ShiftButton ) || !d->selectAnchor ) + d->selectAnchor = d->current; + } + break; + case Key_End: + { + selectCurrent = TRUE; + d->currInputString = QString::null; + int i = (int)count() - 1; + setCurrentItem( i ); + handleItemChange( old, e->state() & ShiftButton, e->state() & ControlButton ); + if ( !( e->state() & ShiftButton ) || !d->selectAnchor ) + d->selectAnchor = d->current; + } + break; + default: + { + if ( !e->text().isEmpty() && e->text()[ 0 ].isPrint() && count() ) { + int curItem = currentItem(); + if ( curItem == -1 ) + curItem = 0; + if ( !d->inputTimer->isActive() ) { + d->currInputString = e->text(); + curItem = d->findItemByName( ++curItem, d->currInputString ); + } else { + d->inputTimer->stop(); + d->currInputString += e->text(); + int oldCurItem = curItem; + curItem = d->findItemByName( curItem, d->currInputString ); + if ( curItem < 0 ) { + curItem = d->findItemByName( ++oldCurItem, e->text() ); + d->currInputString = e->text(); + } + } + if ( curItem >= 0 ) + setCurrentItem( curItem ); + if ( curItem >= 0 && selectionMode() == QListBox::Extended ) { + bool changed = FALSE; + bool block = signalsBlocked(); + blockSignals( TRUE ); + selectAll( FALSE ); + blockSignals( block ); + QListBoxItem *i = item( curItem ); + if ( !i->s && i->isSelectable() ) { + changed = TRUE; + i->s = TRUE; + updateItem( i ); + } + if ( changed ) + emit selectionChanged(); + } + d->inputTimer->start( 400, TRUE ); + } else { + d->currInputString = QString::null; + if ( e->state() & ControlButton ) { + switch ( e->key() ) { + case Key_A: + selectAll( TRUE ); + break; + } + } else { + e->ignore(); + } + } + } + } + + if (selfCheck && selectCurrent && selectionMode() == Single && + d->current && !d->current->s ) { + updateItem( d->current ); + setSelected( d->current, TRUE ); + } +} + + +/*!\reimp +*/ +void QListBox::focusInEvent( QFocusEvent* ) +{ + d->mousePressRow = -1; + d->mousePressColumn = -1; + d->inMenuMode = FALSE; + if ( QFocusEvent::reason() != QFocusEvent::Mouse && !d->current && d->head ) { + d->current = d->head; + QListBoxItem *i = d->current; + QString tmp; + if ( i ) + tmp = i->text(); + int tmp2 = index( i ); + emit highlighted( i ); + if ( !tmp.isNull() ) + emit highlighted( tmp ); + emit highlighted( tmp2 ); + emit currentChanged( i ); + } + if ( style().styleHint( QStyle::SH_ItemView_ChangeHighlightOnFocus, this ) ) + repaintSelection(); + + if ( d->current ) { + updateItem( currentItem() ); + QRect mfrect = itemRect( d->current ); + if ( mfrect.isValid() ) + setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE ); + } +} + + +/*!\reimp +*/ +void QListBox::focusOutEvent( QFocusEvent* ) +{ + if (style().styleHint( QStyle::SH_ItemView_ChangeHighlightOnFocus, this )) { + d->inMenuMode = + QFocusEvent::reason() == QFocusEvent::Popup || + (qApp->focusWidget() && qApp->focusWidget()->inherits("QMenuBar")); + if ( !d->inMenuMode ) + repaintSelection(); + } + + if ( d->current ) + updateItem( currentItem() ); +} + +/*!\reimp +*/ +bool QListBox::eventFilter( QObject *o, QEvent *e ) +{ + //### remove in 4.0 + return QScrollView::eventFilter( o, e ); +} + +/*! + Repaints the item at position \a index in the list. +*/ + +void QListBox::updateItem( int index ) +{ + if ( index >= 0 ) + updateItem( item( index ) ); +} + + +/*! + \overload + + Repaints the QListBoxItem \a i. +*/ + +void QListBox::updateItem( QListBoxItem * i ) +{ + if ( !i ) + return; + i->dirty = TRUE; + d->updateTimer->start( 0, TRUE ); +} + + +/*! + \property QListBox::selectionMode + \brief the selection mode of the list box + + Sets the list box's selection mode, which may be one of \c Single + (the default), \c Extended, \c Multi or \c NoSelection. + + \sa SelectionMode +*/ + +void QListBox::setSelectionMode( SelectionMode mode ) +{ + if ( d->selectionMode == mode ) + return; + + if ( ( selectionMode() == Multi || selectionMode() == Extended ) + && ( mode == QListBox::Single || mode == QListBox::NoSelection ) ){ + clearSelection(); + if ( ( mode == QListBox::Single ) && currentItem() ) + setSelected( currentItem(), TRUE ); + } + + d->selectionMode = mode; + triggerUpdate( TRUE ); +} + + +QListBox::SelectionMode QListBox::selectionMode() const +{ + return d->selectionMode; +} + + +/*! + \obsolete + \property QListBox::multiSelection + \brief whether or not the list box is in Multi selection mode + + Consider using the \l QListBox::selectionMode property instead of + this property. + + When setting this property, Multi selection mode is used if set to TRUE and + to Single selection mode if set to FALSE. + + When getting this property, TRUE is returned if the list box is in + Multi selection mode or Extended selection mode, and FALSE if it is + in Single selection mode or NoSelection mode. + + \sa selectionMode +*/ + +bool QListBox::isMultiSelection() const +{ + return selectionMode() == Multi || selectionMode() == Extended; +} + +void QListBox::setMultiSelection( bool enable ) +{ + setSelectionMode( enable ? Multi : Single ); +} + + +/*! + Toggles the selection status of currentItem() and repaints if the + list box is a \c Multi selection list box. + + \sa setMultiSelection() +*/ + +void QListBox::toggleCurrentItem() +{ + if ( selectionMode() == Single || + selectionMode() == NoSelection || + !d->current ) + return; + + if ( d->current->s || d->current->isSelectable() ) { + d->current->s = !d->current->s; + emit selectionChanged(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + int ind = index( d->current ); + QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection ); + QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::StateChanged ); + QAccessible::updateAccessibility( viewport(), ind+1, d->current->s ? QAccessible::SelectionAdd : QAccessible::SelectionRemove ); +#endif + } + updateItem( d->current ); +} + + +/*! + \overload + + If \a select is TRUE the item at position \a index is selected; + otherwise the item is deselected. +*/ + +void QListBox::setSelected( int index, bool select ) +{ + setSelected( item( index ), select ); +} + + +/*! + Selects \a item if \a select is TRUE or unselects it if \a select + is FALSE, and repaints the item appropriately. + + If the list box is a \c Single selection list box and \a select is + TRUE, setSelected() calls setCurrentItem(). + + If the list box is a \c Single selection list box, \a select is + FALSE, setSelected() calls clearSelection(). + + \sa setMultiSelection(), setCurrentItem(), clearSelection(), currentItem() +*/ + +void QListBox::setSelected( QListBoxItem * item, bool select ) +{ + if ( !item || !item->isSelectable() || + (bool)item->s == select || d->selectionMode == NoSelection ) + return; + + int ind = index( item ); + bool emitHighlighted = (d->current != item) || + ( item->s != (uint) select && select ); + if ( selectionMode() == Single ) { + if ( d->current != item ) { + QListBoxItem *o = d->current; + if ( d->current && d->current->s ) + d->current->s = FALSE; + d->current = item; +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::Focus ); +#endif + d->currentColumn = ind / numRows(); + d->currentRow = ind % numRows(); + + if ( o ) + updateItem( o ); + } + } + + item->s = (uint)select; + updateItem( item ); + + if ( d->selectionMode == Single && select ) { + emit selectionChanged( item ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), ind+1, QAccessible::StateChanged ); +#endif + } + emit selectionChanged(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection ); + if ( d->selectionMode != Single ) + QAccessible::updateAccessibility( viewport(), ind+1, select ? QAccessible::SelectionAdd : QAccessible::SelectionRemove ); +#endif + + if ( emitHighlighted ) { + QString tmp; + if ( d->current ) + tmp = d->current->text(); + int tmp2 = index( d->current ); + emit highlighted( d->current ); + if ( !tmp.isNull() ) + emit highlighted( tmp ); + emit highlighted( tmp2 ); + emit currentChanged( d->current ); + } +} + + +/*! + Returns TRUE if item \a i is selected; otherwise returns FALSE. +*/ + +bool QListBox::isSelected( int i ) const +{ + if ( selectionMode() == Single && i != currentItem() ) + return FALSE; + + QListBoxItem * lbi = item( i ); + if ( !lbi ) + return FALSE; // should not happen + return lbi->s; +} + + +/*! + \overload + + Returns TRUE if item \a i is selected; otherwise returns FALSE. +*/ + +bool QListBox::isSelected( const QListBoxItem * i ) const +{ + if ( !i ) + return FALSE; + + return i->s; +} + +/*! Returns the selected item if the list box is in +single-selection mode and an item is selected. + +If no items are selected or the list box is in another selection mode +this function returns 0. + +\sa setSelected() setMultiSelection() +*/ + +QListBoxItem* QListBox::selectedItem() const +{ + if ( d->selectionMode != Single ) + return 0; + if ( isSelected( currentItem() ) ) + return d->current; + return 0; +} + + +/*! + Deselects all items, if possible. + + Note that a \c Single selection list box will automatically select + an item if it has keyboard focus. +*/ + +void QListBox::clearSelection() +{ + selectAll( FALSE ); +} + +/*! + In \c Multi and \c Extended modes, this function sets all items to + be selected if \a select is TRUE, and to be unselected if \a + select is FALSE. + + In \c Single and \c NoSelection modes, this function only changes + the selection status of currentItem(). +*/ + +void QListBox::selectAll( bool select ) +{ + if ( selectionMode() == Multi || selectionMode() == Extended ) { + bool b = signalsBlocked(); + blockSignals( TRUE ); + for ( int i = 0; i < (int)count(); i++ ) + setSelected( i, select ); + blockSignals( b ); + emit selectionChanged(); + } else if ( d->current ) { + QListBoxItem * i = d->current; + setSelected( i, select ); + } +} + +/*! + Inverts the selection. Only works in \c Multi and \c Extended + selection mode. +*/ + +void QListBox::invertSelection() +{ + if ( d->selectionMode == Single || + d->selectionMode == NoSelection ) + return; + + bool b = signalsBlocked(); + blockSignals( TRUE ); + for ( int i = 0; i < (int)count(); i++ ) + setSelected( i, !item( i )->isSelected() ); + blockSignals( b ); + emit selectionChanged(); +} + + +/*! + \obsolete + Not used anymore; provided for binary compatibility +*/ + +void QListBox::emitChangedSignal( bool ) +{ +} + + +/*! \reimp */ + +QSize QListBox::sizeHint() const +{ + if ( cachedSizeHint().isValid() ) + return cachedSizeHint(); + + constPolish(); + doLayout(); + + int i=0; + while( i < 10 && + i < (int)d->columnPos.size()-1 && + d->columnPos[i] < 200 ) + i++; + int x; + x = QMIN( 200, d->columnPos[i] + + 2 * style().pixelMetric( QStyle::PM_DefaultFrameWidth ) ); + x = QMAX( 40, x ); + + i = 0; + while( i < 10 && + i < (int)d->rowPos.size()-1 && + d->rowPos[i] < 200 ) + i++; + int y; + y = QMIN( 200, d->rowPos[i] + + 2 * style().pixelMetric( QStyle::PM_DefaultFrameWidth ) ); + y = QMAX( 40, y ); + + QSize s( x, y ); + setCachedSizeHint( s ); + return s; +} + +/*! + \reimp +*/ + +QSize QListBox::minimumSizeHint() const +{ + return QScrollView::minimumSizeHint(); +} + + +/*! + Ensures that a single paint event will occur at the end of the + current event loop iteration. If \a doLayout is TRUE, the layout + is also redone. +*/ + +void QListBox::triggerUpdate( bool doLayout ) +{ + if ( doLayout ) + d->layoutDirty = d->mustPaintAll = TRUE; + d->updateTimer->start( 0, TRUE ); +} + + +void QListBox::setColumnMode( LayoutMode mode ) +{ + if ( mode == Variable ) + return; + d->rowModeWins = FALSE; + d->columnMode = mode; + triggerUpdate( TRUE ); +} + + +void QListBox::setColumnMode( int columns ) +{ + if ( columns < 1 ) + columns = 1; + d->columnMode = FixedNumber; + d->numColumns = columns; + d->rowModeWins = FALSE; + triggerUpdate( TRUE ); +} + +void QListBox::setRowMode( LayoutMode mode ) +{ + if ( mode == Variable ) + return; + d->rowModeWins = TRUE; + d->rowMode = mode; + triggerUpdate( TRUE ); +} + + +void QListBox::setRowMode( int rows ) +{ + if ( rows < 1 ) + rows = 1; + d->rowMode = FixedNumber; + d->numRows = rows; + d->rowModeWins = TRUE; + triggerUpdate( TRUE ); +} + +/*! + \property QListBox::columnMode + \brief the column layout mode for this list box. + + setColumnMode() sets the layout mode and adjusts the number of + displayed columns. The row layout mode automatically becomes \c + Variable, unless the column mode is \c Variable. + + \sa setRowMode() columnMode() rowMode numColumns +*/ + + +QListBox::LayoutMode QListBox::columnMode() const +{ + if ( d->rowModeWins ) + return Variable; + else + return d->columnMode; +} + + +/*! + \property QListBox::rowMode + \brief the row layout mode for this list box + + This property is normally \c Variable. + + setRowMode() sets the layout mode and adjusts the number of + displayed rows. The column layout mode automatically becomes \c + Variable, unless the row mode is \c Variable. + + \sa columnMode rowMode +*/ + + +QListBox::LayoutMode QListBox::rowMode() const +{ + if ( d->rowModeWins ) + return d->rowMode; + else + return Variable; +} + + +/*! + \property QListBox::numColumns + \brief the number of columns in the list box + + This is normally 1, but can be different if \l + QListBox::columnMode or \l QListBox::rowMode has been set. + + \sa columnMode rowMode numRows +*/ + +int QListBox::numColumns() const +{ + if ( count() == 0 ) + return 0; + if ( !d->rowModeWins && d->columnMode == FixedNumber ) + return d->numColumns; + doLayout(); + return d->columnPos.size()-1; +} + + +/*! + \property QListBox::numRows + \brief the number of rows in the list box. + + This is equal to the number of items in the default single-column + layout, but can be different. + + \sa columnMode rowMode numColumns +*/ + +int QListBox::numRows() const +{ + if ( count() == 0 ) + return 0; + if ( d->rowModeWins && d->rowMode == FixedNumber ) + return d->numRows; + doLayout(); + return d->rowPos.size()-1; +} + + +/*! + This function does the hard layout work. You should never need to + call it. +*/ + +void QListBox::doLayout() const +{ + if ( !d->layoutDirty || d->resizeTimer->isActive() ) + return; + constPolish(); + int c = count(); + switch( rowMode() ) { + case FixedNumber: + // columnMode() is known to be Variable + tryGeometry( d->numRows, (c+d->numRows-1)/d->numRows ); + break; + case FitToHeight: + // columnMode() is known to be Variable + if ( d->head ) { + // this is basically the FitToWidth code, but edited to use rows. + int maxh = 0; + QListBoxItem * i = d->head; + while ( i ) { + int h = i->height(this); + if ( maxh < h ) + maxh = h; + i = i->n; + } + int vh = viewportSize( 1, 1 ).height(); + do { + int rows = vh / maxh; + if ( rows > c ) + rows = c; + if ( rows < 1 ) + rows = 1; + if ( variableHeight() && rows < c ) { + do { + ++rows; + tryGeometry( rows, (c+rows-1)/rows ); + } while ( rows <= c && + d->rowPos[(int)d->rowPos.size()-1] <= vh ); + --rows; + } + tryGeometry( rows, (c+rows-1)/rows ); + int nvh = viewportSize( d->columnPos[(int)d->columnPos.size()-1], + d->rowPos[(int)d->rowPos.size()-1] ).height(); + if ( nvh < vh ) + vh = nvh; + } while ( d->rowPos.size() > 2 && + vh < d->rowPos[(int)d->rowPos.size()-1] ); + } else { + tryGeometry( 1, 1 ); + } + break; + case Variable: + if ( columnMode() == FixedNumber ) { + tryGeometry( (count()+d->numColumns-1)/d->numColumns, + d->numColumns ); + } else if ( d->head ) { // FitToWidth, at least one item + int maxw = 0; + QListBoxItem * i = d->head; + while ( i ) { + int w = i->width(this); + if ( maxw < w ) + maxw = w; + i = i->n; + } + int vw = viewportSize( 1, 1 ).width(); + do { + int cols = vw / maxw; + if ( cols > c ) + cols = c; + if ( cols < 1 ) + cols = 1; + if ( variableWidth() && cols < c ) { + do { + ++cols; + tryGeometry( (c+cols-1)/cols, cols ); + } while ( cols <= c && + d->columnPos[(int)d->columnPos.size()-1] <= vw ); + --cols; + } + tryGeometry( (c+cols-1)/cols, cols ); + int nvw = viewportSize( d->columnPos[(int)d->columnPos.size()-1], + d->rowPos[(int)d->rowPos.size()-1] ).width(); + if ( nvw < vw ) + vw = nvw; + } while ( d->columnPos.size() > 2 && + vw < d->columnPos[(int)d->columnPos.size()-1] ); + } else { + tryGeometry( 1, 1 ); + } + break; + } + + d->layoutDirty = FALSE; + int w = d->columnPos[(int)d->columnPos.size()-1]; + int h = d->rowPos[(int)d->rowPos.size()-1]; + QSize s( viewportSize( w, h ) ); + w = QMAX( w, s.width() ); + + d->columnPosOne = d->columnPos[1]; + // extend the column for simple single-column listboxes + if ( columnMode() == FixedNumber && d->numColumns == 1 && + d->columnPos[1] < w ) + d->columnPos[1] = w; + ((QListBox *)this)->resizeContents( w, h ); +} + + +/*! + Lay the items out in a \a columns by \a rows array. The array may + be too big: doLayout() is expected to call this with the right + values. +*/ + +void QListBox::tryGeometry( int rows, int columns ) const +{ + if ( columns < 1 ) + columns = 1; + d->columnPos.resize( columns+1 ); + + if ( rows < 1 ) + rows = 1; + d->rowPos.resize( rows+1 ); + + // funky hack I: dump the height/width of each column/row in + // {column,row}Pos for later conversion to positions. + int c; + for( c=0; c<=columns; c++ ) + d->columnPos[c] = 0; + int r; + for( r=0; r<=rows; r++ ) + d->rowPos[r] = 0; + r = c = 0; + QListBoxItem * i = d->head; + while ( i && c < columns ) { + if ( i == d->current ) { + d->currentRow = r; + d->currentColumn = c; + } + + int w = i->width(this); + if ( d->columnPos[c] < w ) + d->columnPos[c] = w; + int h = i->height(this); + if ( d->rowPos[r] < h ) + d->rowPos[r] = h; + i = i->n; + r++; + if ( r == rows ) { + r = 0; + c++; + } + } + // funky hack II: if not variable {width,height}, unvariablify it. + if ( !variableWidth() ) { + int w = 0; + for( c=0; c<columns; c++ ) + if ( w < d->columnPos[c] ) + w = d->columnPos[c]; + for( c=0; c<columns; c++ ) + d->columnPos[c] = w; + } + if ( !variableHeight() ) { + int h = 0; + for( r=0; r<rows; r++ ) + if ( h < d->rowPos[r] ) + h = d->rowPos[r]; + for( r=0; r<rows; r++ ) + d->rowPos[r] = h; + } + // repair the hacking. + int x = 0; + for( c=0; c<=columns; c++ ) { + int w = d->columnPos[c]; + d->columnPos[c] = x; + x += w; + } + int y = 0; + for( r=0; r<=rows; r++ ) { + int h = d->rowPos[r]; + d->rowPos[r] = y; + y += h; + } +} + + +/*! + Returns the row index of the current item, or -1 if no item is the + current item. +*/ + +int QListBox::currentRow() const +{ + if ( !d->current ) + return -1; + if ( d->currentRow < 0 ) + d->layoutDirty = TRUE; + if ( d->layoutDirty ) + doLayout(); + return d->currentRow; +} + + +/*! + Returns the column index of the current item, or -1 if no item is + the current item. +*/ + +int QListBox::currentColumn() const +{ + if ( !d->current ) + return -1; + if ( d->currentColumn < 0 ) + d->layoutDirty = TRUE; + if ( d->layoutDirty ) + doLayout(); + return d->currentColumn; +} + + +void QListBox::setTopItem( int index ) +{ + if ( index >= (int)count() || count() == 0 ) + return; + int col = index / numRows(); + int y = d->rowPos[index-col*numRows()]; + if ( d->columnPos[col] >= contentsX() && + d->columnPos[col+1] <= contentsX() + visibleWidth() ) + setContentsPos( contentsX(), y ); + else + setContentsPos( d->columnPos[col], y ); +} + +/*! + Scrolls the list box so the item at position \a index in the list + is displayed in the bottom row of the list box. + + \sa setTopItem() +*/ + +void QListBox::setBottomItem( int index ) +{ + if ( index >= (int)count() || count() == 0 ) + return; + int col = index / numRows(); + int y = d->rowPos[1+index-col*numRows()] - visibleHeight(); + if ( y < 0 ) + y = 0; + if ( d->columnPos[col] >= contentsX() && + d->columnPos[col+1] <= contentsX() + visibleWidth() ) + setContentsPos( contentsX(), y ); + else + setContentsPos( d->columnPos[col], y ); +} + + +/*! + Returns the item at point \a p, specified in viewport coordinates, + or a 0 if there is no item at \a p. + + Use contentsToViewport() to convert between widget coordinates and + viewport coordinates. +*/ + +QListBoxItem * QListBox::itemAt( const QPoint& p ) const +{ + if ( d->layoutDirty ) + doLayout(); + QPoint np = p; + + // take into acount frame margin to get to viewport + np -= viewport()->pos(); + if (!viewport()->rect().contains(np)) + return 0; + + // take into account contents position + np = viewportToContents( np ); + + int x = np.x(); + int y = np.y(); + + // return 0 when y is below the last row + if ( y > d->rowPos[ numRows() ] ) + return 0; + + int col = columnAt( x ); + int row = rowAt( y ); + + QListBoxItem *i = item( col * numRows() + row ); + if ( i && numColumns() > 1 ) { + if ( d->columnPos[ col ] + i->width( this ) >= x ) + return i; + } else { + if ( d->columnPos[ col + 1 ] >= x ) + return i; + } + return 0; +} + + +/*! + Ensures that the current item is visible. +*/ + +void QListBox::ensureCurrentVisible() +{ + if ( !d->current ) + return; + + doLayout(); + + int row = currentRow(); + int column = currentColumn(); + int w = ( d->columnPos[column+1] - d->columnPos[column] ) / 2; + int h = ( d->rowPos[row+1] - d->rowPos[row] ) / 2; + // next four lines are Bad. they mean that for pure left-to-right + // languages, textual list box items are displayed better than + // before when there is little space. for non-textual items, or + // other languages, it means... that you really should have enough + // space in the first place :) + if ( numColumns() == 1 ) + w = 0; + if ( w*2 > viewport()->width() ) + w = viewport()->width()/2; + + ensureVisible( d->columnPos[column] + w, d->rowPos[row] + h, w, h); +} + + +/*! \internal */ + +void QListBox::doAutoScroll() +{ + if ( d->scrollPos.x() < 0 ) { + // scroll left + int x = contentsX() - horizontalScrollBar()->lineStep(); + if ( x < 0 ) + x = 0; + if ( x != contentsX() ) { + d->mouseMoveColumn = columnAt( x ); + updateSelection(); + if ( x < contentsX() ) + setContentsPos( x, contentsY() ); + } + } else if ( d->scrollPos.x() > 0 ) { + // scroll right + int x = contentsX() + horizontalScrollBar()->lineStep(); + if ( x + visibleWidth() > contentsWidth() ) + x = contentsWidth() - visibleWidth(); + if ( x != contentsX() ) { + d->mouseMoveColumn = columnAt( x + visibleWidth() - 1 ); + updateSelection(); + if ( x > contentsX() ) + setContentsPos( x, contentsY() ); + } + } + + if ( d->scrollPos.y() < 0 ) { + // scroll up + int y = contentsY() - verticalScrollBar()->lineStep(); + if ( y < 0 ) + y = 0; + if ( y != contentsY() ) { + y = contentsY() - verticalScrollBar()->lineStep(); + d->mouseMoveRow = rowAt( y ); + updateSelection(); + } + } else if ( d->scrollPos.y() > 0 ) { + // scroll down + int y = contentsY() + verticalScrollBar()->lineStep(); + if ( y + visibleHeight() > contentsHeight() ) + y = contentsHeight() - visibleHeight(); + if ( y != contentsY() ) { + y = contentsY() + verticalScrollBar()->lineStep(); + d->mouseMoveRow = rowAt(y + visibleHeight() - 1 ); + updateSelection(); + } + } + + if ( d->scrollPos == QPoint( 0, 0 ) ) { + delete d->scrollTimer; + d->scrollTimer = 0; + } +} + + +/*! + \property QListBox::topItem + \brief the index of an item at the top of the screen. + + When getting this property and the listbox has multiple columns, + an arbitrary item is selected and returned. + + When setting this property, the list box is scrolled so the item + at position \e index in the list is displayed in the top row of + the list box. +*/ + +int QListBox::topItem() const +{ + doLayout(); + + // move rightwards to the best column + int col = columnAt( contentsX() ); + int row = rowAt( contentsY() ); + return col * numRows() + row; +} + + +/*! + \property QListBox::variableHeight + \brief whether this list box has variable-height rows + + When the list box has variable-height rows (the default), each row + is as high as the highest item in that row. When it has same-sized + rows, all rows are as high as the highest item in the list box. + + \sa variableWidth +*/ + +bool QListBox::variableHeight() const +{ + return d->variableHeight; +} + + +void QListBox::setVariableHeight( bool enable ) +{ + if ( (bool)d->variableHeight == enable ) + return; + + d->variableHeight = enable; + triggerUpdate( TRUE ); +} + + +/*! + \property QListBox::variableWidth + \brief whether this list box has variable-width columns + + When the list box has variable-width columns, each column is as + wide as the widest item in that column. When it has same-sized + columns (the default), all columns are as wide as the widest item + in the list box. + + \sa variableHeight +*/ + +bool QListBox::variableWidth() const +{ + return d->variableWidth; +} + + +void QListBox::setVariableWidth( bool enable ) +{ + if ( (bool)d->variableWidth == enable ) + return; + + d->variableWidth = enable; + triggerUpdate( TRUE ); +} + + +/*! + Repaints only what really needs to be repainted. +*/ +void QListBox::refreshSlot() +{ + if ( d->mustPaintAll || + d->layoutDirty ) { + d->mustPaintAll = FALSE; + bool currentItemVisible = itemVisible( currentItem() ); + doLayout(); + if ( hasFocus() && + currentItemVisible && + d->currentColumn >= 0 && + d->currentRow >= 0 && + ( d->columnPos[d->currentColumn] < contentsX() || + d->columnPos[d->currentColumn+1]>contentsX()+visibleWidth() || + d->rowPos[d->currentRow] < contentsY() || + d->rowPos[d->currentRow+1] > contentsY()+visibleHeight() ) ) + ensureCurrentVisible(); + viewport()->repaint( FALSE ); + return; + } + + QRegion r; + int x = contentsX(); + int y = contentsY(); + int col = columnAt( x ); + int row = rowAt( y ); + int top = row; + while( col < (int)d->columnPos.size()-1 && d->columnPos[col+1] < x ) + col++; + while( top < (int)d->rowPos.size()-1 && d->rowPos[top+1] < y ) + top++; + QListBoxItem * i = item( col * numRows() + row ); + + while ( i && (int)col < numColumns() && + d->columnPos[col] < x + visibleWidth() ) { + int cw = d->columnPos[col+1] - d->columnPos[col]; + while ( i && row < numRows() && d->rowPos[row] < + y + visibleHeight() ) { + if ( i->dirty ) + r = r.unite( QRect( d->columnPos[col] - x, d->rowPos[row] - y, + cw, d->rowPos[row+1] - d->rowPos[row] ) ); + row++; + i = i->n; + } + col++; + if ( numColumns() > 1 ) { + row = top; + i = item( col * numRows() + row ); + } + } + + if ( r.isEmpty() ) + viewport()->repaint( FALSE ); + else + viewport()->repaint( r, FALSE ); +} + + +/*! \reimp */ + +void QListBox::viewportPaintEvent( QPaintEvent * e ) +{ + doLayout(); + QWidget* vp = viewport(); + QPainter p( vp ); + QRegion r = e->region(); + +#if 0 + { + // this stuff has been useful enough times that from now I'm + // leaving it in the source. + uint i = 0; + qDebug( "%s/%s: %i rects", className(), name(), r.rects().size() ); + while( i < r.rects().size() ) { + qDebug( "rect %d: %d, %d, %d, %d", i, + r.rects()[i].left(), r.rects()[i].top(), + r.rects()[i].width(), r.rects()[i].height() ); + i++; + } + qDebug( "" ); + } +#endif + + int x = contentsX(); + int y = contentsY(); + int w = vp->width(); + int h = vp->height(); + + int col = columnAt( x ); + int top = rowAt( y ); + int row = top; + + QListBoxItem * i = item( col*numRows() + row ); + + const QColorGroup & g = colorGroup(); + p.setPen( g.text() ); + p.setBackgroundColor( backgroundBrush().color() ); + while ( i && (int)col < numColumns() && d->columnPos[col] < x + w ) { + int cw = d->columnPos[col+1] - d->columnPos[col]; + while ( i && (int)row < numRows() && d->rowPos[row] < y + h ) { + int ch = d->rowPos[row+1] - d->rowPos[row]; + QRect itemRect( d->columnPos[col]-x, d->rowPos[row]-y, cw, ch ); + QRegion tempRegion( itemRect ); + QRegion itemPaintRegion( tempRegion.intersect( r ) ); + if ( !itemPaintRegion.isEmpty() ) { + p.save(); + p.setClipRegion( itemPaintRegion ); + p.translate( d->columnPos[col]-x, d->rowPos[row]-y ); + paintCell( &p, row, col ); + p.restore(); + r = r.subtract( itemPaintRegion ); + } + row++; + if ( i->dirty ) { + // reset dirty flag only if the entire item was painted + if ( itemPaintRegion == QRegion( itemRect ) ) + i->dirty = FALSE; + } + i = i->n; + } + col++; + if ( numColumns() > 1 ) { + row = top; + i = item( col * numRows() + row ); + } + } + + if ( r.isEmpty() ) + return; + p.setClipRegion( r ); + p.fillRect( 0, 0, w, h, viewport()->backgroundBrush() ); +} + + +/*! + Returns the height in pixels of the item with index \a index. \a + index defaults to 0. + + If \a index is too large, this function returns 0. +*/ + +int QListBox::itemHeight( int index ) const +{ + if ( index >= (int)count() || index < 0 ) + return 0; + int r = index % numRows(); + return d->rowPos[r+1] - d->rowPos[r]; +} + + +/*! + Returns the index of the column at \a x, which is in the listbox's + coordinates, not in on-screen coordinates. + + If there is no column that spans \a x, columnAt() returns -1. +*/ + +int QListBox::columnAt( int x ) const +{ + if ( x < 0 ) + return -1; + if ( !d->columnPos.size() ) + return -1; + if ( x >= d->columnPos[(int)d->columnPos.size()-1 ] ) + return numColumns() - 1; + + int col = 0; + while( col < (int)d->columnPos.size()-1 && d->columnPos[col+1] < x ) + col++; + return col; +} + + +/*! + Returns the index of the row at \a y, which is in the listbox's + coordinates, not in on-screen coordinates. + + If there is no row that spans \a y, rowAt() returns -1. +*/ + +int QListBox::rowAt( int y ) const +{ + if ( y < 0 ) + return -1; + + // find the top item, use bsearch for speed + int l = 0; + int r = d->rowPos.size() - 2; + if ( r < 0 ) + return -1; + if ( l <= d->rowPosCache && d->rowPosCache <= r ) { + if ( d->rowPos[ QMAX( l, d->rowPosCache - 10 ) ] <= y + && y <= d->rowPos[ QMIN( r, d->rowPosCache + 10 ) ] ) { + l = QMAX( l, d->rowPosCache - 10 ); + r = QMIN( r, d->rowPosCache + 10 ); + } + } + int i = ( (l+r+1) / 2 ); + while ( r - l ) { + if ( d->rowPos[i] > y ) + r = i -1; + else + l = i; + i = ( (l+r+1) / 2 ); + } + d->rowPosCache = i; + if ( d->rowPos[i] <= y && y <= d->rowPos[i+1] ) + return i; + + return d->count - 1; +} + + +/*! + Returns the rectangle on the screen that \a item occupies in + viewport()'s coordinates, or an invalid rectangle if \a item is 0 + or is not currently visible. +*/ + +QRect QListBox::itemRect( QListBoxItem *item ) const +{ + if ( d->resizeTimer->isActive() ) + return QRect( 0, 0, -1, -1 ); + if ( !item ) + return QRect( 0, 0, -1, -1 ); + + int i = index( item ); + int col = i / numRows(); + int row = i % numRows(); + + int x = d->columnPos[ col ] - contentsX(); + int y = d->rowPos[ row ] - contentsY(); + + QRect r( x, y, d->columnPos[ col + 1 ] - d->columnPos[ col ], + d->rowPos[ row + 1 ] - d->rowPos[ row ] ); + if ( r.intersects( QRect( 0, 0, visibleWidth(), visibleHeight() ) ) ) + return r; + return QRect( 0, 0, -1, -1 ); +} + + +#ifndef QT_NO_COMPAT + +/*! + \obsolete + + Using this method is quite inefficient. We suggest to use insertItem() + for inserting and sort() afterwards. + + Inserts \a lbi at its sorted position in the list box and returns the + position. + + All items must be inserted with inSort() to maintain the sorting + order. inSort() treats any pixmap (or user-defined type) as + lexicographically less than any string. + + \sa insertItem(), sort() +*/ +int QListBox::inSort( const QListBoxItem * lbi ) +{ + qObsolete( "QListBox", "inSort", "insertItem" ); + if ( !lbi ) + return -1; + + QListBoxItem * i = d->head; + int c = 0; + + while( i && i->text() < lbi->text() ) { + i = i->n; + c++; + } + insertItem( lbi, c ); + return c; +} + +/*! + \obsolete + \overload + Using this method is quite inefficient. We suggest to use insertItem() + for inserting and sort() afterwards. + + Inserts a new item of \a text at its sorted position in the list box and + returns the position. + + All items must be inserted with inSort() to maintain the sorting + order. inSort() treats any pixmap (or user-defined type) as + lexicographically less than any string. + + \sa insertItem(), sort() +*/ +int QListBox::inSort( const QString& text ) +{ + qObsolete( "QListBox", "inSort", "insertItem" ); + return inSort( new QListBoxText(text) ); +} + +#endif + + +/*! \reimp */ + +void QListBox::resizeEvent( QResizeEvent *e ) +{ + d->layoutDirty = ( d->layoutDirty || + rowMode() == FitToHeight || + columnMode() == FitToWidth ); + + if ( !d->layoutDirty && columnMode() == FixedNumber && + d->numColumns == 1) { + int w = d->columnPosOne; + QSize s( viewportSize( w, contentsHeight() ) ); + w = QMAX( w, s.width() ); + d->columnPos[1] = QMAX( w, d->columnPosOne ); + resizeContents( d->columnPos[1], contentsHeight() ); + } + + if ( d->resizeTimer->isActive() ) + d->resizeTimer->stop(); + if ( d->rowMode == FixedNumber && d->columnMode == FixedNumber ) { + bool currentItemVisible = itemVisible( currentItem() ); + doLayout(); + QScrollView::resizeEvent( e ); + if ( currentItemVisible ) + ensureCurrentVisible(); + if ( d->current ) + viewport()->repaint( itemRect( d->current ), FALSE ); + } else if ( ( d->columnMode == FitToWidth || d->rowMode == FitToHeight ) && !(isVisible()) ) { + QScrollView::resizeEvent( e ); + } else if ( d->layoutDirty ) { + d->resizeTimer->start( 100, TRUE ); + resizeContents( contentsWidth() - ( e->oldSize().width() - e->size().width() ), + contentsHeight() - ( e->oldSize().height() - e->size().height() ) ); + QScrollView::resizeEvent( e ); + } else { + QScrollView::resizeEvent( e ); + } +} + +/*! + \internal +*/ + +void QListBox::adjustItems() +{ + triggerUpdate( TRUE ); + ensureCurrentVisible(); +} + + +/*! + Provided for compatibility with the old QListBox. We recommend + using QListBoxItem::paint() instead. + + Repaints the cell at \a row, \a col using painter \a p. +*/ + +void QListBox::paintCell( QPainter * p, int row, int col ) +{ + bool drawActiveSelection = hasFocus() || d->inMenuMode || + !style().styleHint( QStyle::SH_ItemView_ChangeHighlightOnFocus, this ); + const QColorGroup &g = ( drawActiveSelection ? colorGroup() : palette().inactive() ); + + int cw = d->columnPos[col+1] - d->columnPos[col]; + int ch = d->rowPos[row+1] - d->rowPos[row]; + QListBoxItem * i = item( col*numRows()+row ); + p->save(); + if ( i->s ) { + if ( i->custom_highlight ) { + p->fillRect( 0, 0, cw, ch, + g.brush( QPalette::backgroundRoleFromMode( viewport()->backgroundMode() ) ) ); + p->setPen( g.highlightedText() ); + p->setBackgroundColor( g.highlight() ); + } + else if ( numColumns() == 1 ) { + p->fillRect( 0, 0, cw, ch, g.brush( QColorGroup::Highlight ) ); + p->setPen( g.highlightedText() ); + p->setBackgroundColor( g.highlight() ); + } else { + int iw = i->width( this ); + p->fillRect( 0, 0, iw, ch, g.brush( QColorGroup::Highlight ) ); + p->fillRect( iw, 0, cw - iw + 1, ch, + g.brush( QPalette::backgroundRoleFromMode( viewport()->backgroundMode() ) ) ); + p->setPen( g.highlightedText() ); + p->setBackgroundColor( g.highlight() ); + } + } else { + p->fillRect( 0, 0, cw, ch, + g.brush( QPalette::backgroundRoleFromMode( viewport()->backgroundMode() ) ) ); + } + + i->paint( p ); + + if ( d->current == i && hasFocus() && !i->custom_highlight ) { + if ( numColumns() > 1 ) + cw = i->width( this ); + + style().drawPrimitive( QStyle::PE_FocusRect, p, QRect( 0, 0, cw, ch ), g, + QStyle::Style_FocusAtBorder, + QStyleOption(i->isSelected() ? g.highlight() : g.base() ) ); + } + + p->restore(); +} + +/*! + Returns the width of the widest item in the list box. +*/ + +long QListBox::maxItemWidth() const +{ + if ( d->layoutDirty ) + doLayout(); + long m = 0; + int i = d->columnPos.size(); + while( i-- ) + if ( m < d->columnPos[i] ) + m = d->columnPos[i]; + return m; +} + + +/*! \reimp */ + +void QListBox::showEvent( QShowEvent * ) +{ + d->ignoreMoves = FALSE; + d->mousePressRow = -1; + d->mousePressColumn = -1; + d->mustPaintAll = FALSE; + ensureCurrentVisible(); +} + +#ifndef QT_NO_COMPAT + +/*! + \obsolete + + Returns the vertical pixel-coordinate in \a *yPos, of the list box + item at position \a index in the list. Returns FALSE if the item is + outside the visible area. +*/ +bool QListBox::itemYPos( int index, int *yPos ) const +{ + qObsolete( "QListBox", "itemYPos" ); + QListBoxItem* i = item(index); + if ( !i ) + return FALSE; + if ( yPos ) + *yPos = i->y; + return TRUE; +} + +#endif + +/*! + \fn bool QListBoxItem::isSelected() const + + Returns TRUE if the item is selected; otherwise returns FALSE. + + \sa QListBox::isSelected(), isCurrent() +*/ + +/*! + \fn bool QListBoxItem::selected() const + \obsolete +*/ + +/*! + Returns TRUE if the item is the current item; otherwise returns + FALSE. + + \sa QListBox::currentItem(), QListBox::item(), isSelected() +*/ +bool QListBoxItem::isCurrent() const +{ + return listBox() && listBox()->hasFocus() && + listBox()->item( listBox()->currentItem() ) == this; +} +/*! + \fn bool QListBoxItem::current() const + \obsolete +*/ + +/*! + \fn void QListBox::centerCurrentItem() + \obsolete + + This function does exactly the same as ensureCurrentVisible() + + \sa QListBox::ensureCurrentVisible() +*/ + +/*! + Returns a pointer to the list box containing this item. +*/ + +QListBox * QListBoxItem::listBox() const +{ + return lbox; +} + + +/*! + Removes \a item from the list box and causes an update of the + screen display. The item is not deleted. You should normally not + need to call this function because QListBoxItem::~QListBoxItem() + calls it. The normal way to delete an item is with \c delete. + + \sa QListBox::insertItem() +*/ +void QListBox::takeItem( const QListBoxItem * item ) +{ + if ( !item || d->clearing ) + return; + d->cache = 0; + d->count--; + if ( item == d->last ) + d->last = d->last->p; + if ( item->p && item->p->n == item ) + item->p->n = item->n; + if ( item->n && item->n->p == item ) + item->n->p = item->p; + if ( d->head == item ) { + d->head = item->n; + d->currentColumn = d->currentRow = -1; + } + + if ( d->current == item ) { + d->current = item->n ? item->n : item->p; + QListBoxItem *i = d->current; + QString tmp; + if ( i ) + tmp = i->text(); + int tmp2 = index( i ); + emit highlighted( i ); + if ( !tmp.isNull() ) + emit highlighted( tmp ); + emit highlighted( tmp2 ); + emit currentChanged( i ); + } + if ( d->tmpCurrent == item ) + d->tmpCurrent = d->current; + if ( d->selectAnchor == item ) + d->selectAnchor = d->current; + + if ( item->s ) + emit selectionChanged(); + ((QListBoxItem *)item)->lbox = 0; + triggerUpdate( TRUE ); +} + +/*! + \internal + Finds the next item after start beginning with \a text. +*/ + +int QListBoxPrivate::findItemByName( int start, const QString &text ) +{ + if ( start < 0 || (uint)start >= listBox->count() ) + start = 0; + QString match = text.lower(); + if ( match.length() < 1 ) + return start; + QString curText; + int item = start; + do { + curText = listBox->text( item ).lower(); + if ( curText.startsWith( match ) ) + return item; + item++; + if ( (uint)item == listBox->count() ) + item = 0; + } while ( item != start ); + return -1; +} + +/*! + \internal --- obsolete! +*/ + +void QListBox::clearInputString() +{ + d->currInputString = QString::null; +} + +/*! + Finds the first list box item that has the text \a text and + returns it, or returns 0 of no such item could be found. + The search starts from the current item if the current item exists, + otherwise it starts from the first list box item. + If \c ComparisonFlags are specified in \a compare then these flags + are used, otherwise the default is a case-insensitive, "begins + with" search. + + \sa Qt::StringComparisonMode +*/ + +QListBoxItem *QListBox::findItem( const QString &text, ComparisonFlags compare ) const +{ + if ( text.isEmpty() ) + return 0; + + if ( compare == CaseSensitive || compare == 0 ) + compare |= ExactMatch; + + QString itmtxt; + QString comtxt = text; + if ( ! (compare & CaseSensitive ) ) + comtxt = text.lower(); + + QListBoxItem *item; + if ( d->current ) + item = d->current; + else + item = d->head; + + QListBoxItem *beginsWithItem = 0; + QListBoxItem *endsWithItem = 0; + QListBoxItem *containsItem = 0; + + if ( item ) { + for ( ; item; item = item->n ) { + if ( ! (compare & CaseSensitive) ) + itmtxt = item->text().lower(); + else + itmtxt = item->text(); + + if ( compare & ExactMatch && itmtxt == comtxt ) + return item; + if ( compare & BeginsWith && !beginsWithItem && itmtxt.startsWith( comtxt ) ) + beginsWithItem = containsItem = item; + if ( compare & EndsWith && !endsWithItem && itmtxt.endsWith( comtxt ) ) + endsWithItem = containsItem = item; + if ( compare & Contains && !containsItem && itmtxt.contains( comtxt ) ) + containsItem = item; + } + + if ( d->current && d->head ) { + item = d->head; + for ( ; item && item != d->current; item = item->n ) { + if ( ! (compare & CaseSensitive) ) + itmtxt = item->text().lower(); + else + itmtxt = item->text(); + + if ( compare & ExactMatch && itmtxt == comtxt ) + return item; + if ( compare & BeginsWith && !beginsWithItem && itmtxt.startsWith( comtxt ) ) + beginsWithItem = containsItem = item; + if ( compare & EndsWith && !endsWithItem && itmtxt.endsWith( comtxt ) ) + endsWithItem = containsItem = item; + if ( compare & Contains && !containsItem && itmtxt.contains( comtxt ) ) + containsItem = item; + } + } + } + + // Obey the priorities + if ( beginsWithItem ) + return beginsWithItem; + else if ( endsWithItem ) + return endsWithItem; + else if ( containsItem ) + return containsItem; + return 0; +} + +/*! + \internal +*/ + +void QListBox::drawRubber() +{ + if ( !d->rubber ) + return; + if ( !d->rubber->width() && !d->rubber->height() ) + return; + QPainter p( viewport() ); + p.setRasterOp( NotROP ); + style().drawPrimitive( QStyle::PE_RubberBand, &p, d->rubber->normalize(), + colorGroup() ); + p.end(); +} + +/*! + \internal +*/ + +void QListBox::doRubberSelection( const QRect &old, const QRect &rubber ) +{ + QListBoxItem *i = d->head; + QRect ir, pr; + bool changed = FALSE; + for ( ; i; i = i->n ) { + ir = itemRect( i ); + if ( ir == QRect( 0, 0, -1, -1 ) ) + continue; + if ( i->isSelected() && !ir.intersects( rubber ) && ir.intersects( old ) ) { + i->s = FALSE; + pr = pr.unite( ir ); + changed = TRUE; + } else if ( !i->isSelected() && ir.intersects( rubber ) ) { + if ( i->isSelectable() ) { + i->s = TRUE; + pr = pr.unite( ir ); + changed = TRUE; + } + } + } + if ( changed ) { + emit selectionChanged(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection ); +#endif + } + viewport()->repaint( pr, TRUE ); +} + + +/*! + Returns TRUE if the user is selecting items using a rubber band + rectangle; otherwise returns FALSE. +*/ + +bool QListBox::isRubberSelecting() const +{ + return d->rubber != 0; +} + + +/*! + Returns the item that comes after this in the list box. If this is + the last item, 0 is returned. + + \sa prev() +*/ + +QListBoxItem *QListBoxItem::next() const +{ + return n; +} + +/*! + Returns the item which comes before this in the list box. If this + is the first item, 0 is returned. + + \sa next() +*/ + +QListBoxItem *QListBoxItem::prev() const +{ + return p; +} + +/*! + Returns the first item in this list box. If the list box is empty, + returns 0. +*/ + +QListBoxItem *QListBox::firstItem() const +{ + return d->head; +} + +#if defined(Q_C_CALLBACKS) +extern "C" { +#endif + +#ifdef Q_OS_TEMP +static int _cdecl cmpListBoxItems( const void *n1, const void *n2 ) +#else +static int cmpListBoxItems( const void *n1, const void *n2 ) +#endif +{ + if ( !n1 || !n2 ) + return 0; + + QListBoxPrivate::SortableItem *i1 = (QListBoxPrivate::SortableItem *)n1; + QListBoxPrivate::SortableItem *i2 = (QListBoxPrivate::SortableItem *)n2; + + return i1->item->text().localeAwareCompare( i2->item->text() ); +} + +#if defined(Q_C_CALLBACKS) +} +#endif + +/*! + If \a ascending is TRUE sorts the items in ascending order; + otherwise sorts in descending order. + + To compare the items, the text (QListBoxItem::text()) of the items + is used. +*/ + +void QListBox::sort( bool ascending ) +{ + if ( count() == 0 ) + return; + + d->cache = 0; + + QListBoxPrivate::SortableItem *items = new QListBoxPrivate::SortableItem[ count() ]; + + QListBoxItem *item = d->head; + int i = 0; + for ( ; item; item = item->n ) + items[ i++ ].item = item; + + qsort( items, count(), sizeof( QListBoxPrivate::SortableItem ), cmpListBoxItems ); + + QListBoxItem *prev = 0; + item = 0; + if ( ascending ) { + for ( i = 0; i < (int)count(); ++i ) { + item = items[ i ].item; + if ( item ) { + item->p = prev; + item->dirty = TRUE; + if ( item->p ) + item->p->n = item; + item->n = 0; + } + if ( i == 0 ) + d->head = item; + prev = item; + } + } else { + for ( i = (int)count() - 1; i >= 0 ; --i ) { + item = items[ i ].item; + if ( item ) { + item->p = prev; + item->dirty = TRUE; + if ( item->p ) + item->p->n = item; + item->n = 0; + } + if ( i == (int)count() - 1 ) + d->head = item; + prev = item; + } + } + d->last = item; + + delete [] items; + + // We have to update explicitly in case the current "vieport" overlaps the + // new viewport we set (starting at (0,0)). + bool haveToUpdate = contentsX() < visibleWidth() || contentsY() < visibleHeight(); + setContentsPos( 0, 0 ); + if ( haveToUpdate ) + updateContents( 0, 0, visibleWidth(), visibleHeight() ); +} + +void QListBox::handleItemChange( QListBoxItem *old, bool shift, bool control ) +{ + if ( d->selectionMode == Single ) { + // nothing + } else if ( d->selectionMode == Extended ) { + if ( shift ) { + selectRange( d->selectAnchor ? d->selectAnchor : old, + d->current, FALSE, TRUE, (d->selectAnchor && !control) ? TRUE : FALSE ); + } else if ( !control ) { + bool block = signalsBlocked(); + blockSignals( TRUE ); + selectAll( FALSE ); + blockSignals( block ); + setSelected( d->current, TRUE ); + } + } else if ( d->selectionMode == Multi ) { + if ( shift ) + selectRange( old, d->current, TRUE, FALSE ); + } +} + +void QListBox::selectRange( QListBoxItem *from, QListBoxItem *to, bool invert, bool includeFirst, bool clearSel ) +{ + if ( !from || !to ) + return; + if ( from == to && !includeFirst ) + return; + QListBoxItem *i = 0; + int index =0; + int f_idx = -1, t_idx = -1; + for ( i = d->head; i; i = i->n, index++ ) { + if ( i == from ) + f_idx = index; + if ( i == to ) + t_idx = index; + if ( f_idx != -1 && t_idx != -1 ) + break; + } + if ( f_idx > t_idx ) { + i = from; + from = to; + to = i; + if ( !includeFirst ) + to = to->prev(); + } else { + if ( !includeFirst ) + from = from->next(); + } + + bool changed = FALSE; + if ( clearSel ) { + for ( i = d->head; i && i != from; i = i->n ) { + if ( i->s ) { + i->s = FALSE; + changed = TRUE; + updateItem( i ); + } + } + for ( i = to->n; i; i = i->n ) { + if ( i->s ) { + i->s = FALSE; + changed = TRUE; + updateItem( i ); + } + } + } + + for ( i = from; i; i = i->next() ) { + if ( !invert ) { + if ( !i->s && i->isSelectable() ) { + i->s = TRUE; + changed = TRUE; + updateItem( i ); + } + } else { + bool sel = !i->s; + if ( (bool)i->s != sel && sel && i->isSelectable() || !sel ) { + i->s = sel; + changed = TRUE; + updateItem( i ); + } + } + if ( i == to ) + break; + } + if ( changed ) { + emit selectionChanged(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection ); +#endif + } +} + +/*! \reimp */ +void QListBox::windowActivationChange( bool oldActive ) +{ + if ( oldActive && d->scrollTimer ) + d->scrollTimer->stop(); + if ( palette().active() != palette().inactive() ) + viewport()->update(); + QScrollView::windowActivationChange( oldActive ); +} + +int QListBoxItem::RTTI = 0; + +/*! + Returns 0. + + Make your derived classes return their own values for rtti(), and + you can distinguish between listbox items. You should use values + greater than 1000 preferably a large random number, to allow for + extensions to this class. +*/ + +int QListBoxItem::rtti() const +{ + return RTTI; +} + +#endif // QT_NO_LISTBOX diff --git a/src/widgets/qlistbox.h b/src/widgets/qlistbox.h new file mode 100644 index 0000000..2dd2109 --- /dev/null +++ b/src/widgets/qlistbox.h @@ -0,0 +1,435 @@ +/********************************************************************** +** +** Definition of QListBox widget class +** +** Created : 941121 +** +** 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. +** +**********************************************************************/ + +#ifndef QLISTBOX_H +#define QLISTBOX_H + +#ifndef QT_H +#include "qscrollview.h" +#include "qpixmap.h" +#endif // QT_H + +#ifndef QT_NO_LISTBOX + + +class QListBoxPrivate; +class QListBoxItem; +class QString; +class QStrList; +class QStringList; + + +class Q_EXPORT QListBox : public QScrollView +{ + friend class QListBoxItem; + friend class QListBoxPrivate; + + Q_OBJECT + Q_ENUMS( SelectionMode LayoutMode ) + Q_PROPERTY( uint count READ count ) + Q_PROPERTY( int numItemsVisible READ numItemsVisible ) + Q_PROPERTY( int currentItem READ currentItem WRITE setCurrentItem ) + Q_PROPERTY( QString currentText READ currentText ) + Q_PROPERTY( int topItem READ topItem WRITE setTopItem DESIGNABLE false ) + Q_PROPERTY( SelectionMode selectionMode READ selectionMode WRITE setSelectionMode ) + Q_PROPERTY( bool multiSelection READ isMultiSelection WRITE setMultiSelection DESIGNABLE false ) + Q_PROPERTY( LayoutMode columnMode READ columnMode WRITE setColumnMode ) + Q_PROPERTY( LayoutMode rowMode READ rowMode WRITE setRowMode ) + Q_PROPERTY( int numColumns READ numColumns ) + Q_PROPERTY( int numRows READ numRows ) + Q_PROPERTY( bool variableWidth READ variableWidth WRITE setVariableWidth ) + Q_PROPERTY( bool variableHeight READ variableHeight WRITE setVariableHeight ) + +public: + QListBox( QWidget* parent=0, const char* name=0, WFlags f=0 ); + ~QListBox(); + + virtual void setFont( const QFont & ); + + uint count() const; + + void insertStringList( const QStringList&, int index=-1 ); + void insertStrList( const QStrList *, int index=-1 ); + void insertStrList( const QStrList &, int index=-1 ); + void insertStrList( const char **, + int numStrings=-1, int index=-1 ); + + void insertItem( const QListBoxItem *, int index=-1 ); + void insertItem( const QListBoxItem *, const QListBoxItem *after ); + void insertItem( const QString &text, int index=-1 ); + void insertItem( const QPixmap &pixmap, int index=-1 ); + void insertItem( const QPixmap &pixmap, const QString &text, int index=-1 ); + + void removeItem( int index ); + + QString text( int index ) const; + const QPixmap *pixmap( int index ) const; + + void changeItem( const QListBoxItem *, int index ); + void changeItem( const QString &text, int index ); + void changeItem( const QPixmap &pixmap, int index ); + void changeItem( const QPixmap &pixmap, const QString &text, int index ); + + void takeItem( const QListBoxItem * ); + + int numItemsVisible() const; + + int currentItem() const; + QString currentText() const { return text(currentItem()); } + virtual void setCurrentItem( int index ); + virtual void setCurrentItem( QListBoxItem * ); + void centerCurrentItem() { ensureCurrentVisible(); } + int topItem() const; + virtual void setTopItem( int index ); + virtual void setBottomItem( int index ); + + long maxItemWidth() const; + + enum SelectionMode { Single, Multi, Extended, NoSelection }; + virtual void setSelectionMode( SelectionMode ); + SelectionMode selectionMode() const; + + void setMultiSelection( bool multi ); + bool isMultiSelection() const; + + virtual void setSelected( QListBoxItem *, bool ); + void setSelected( int, bool ); + bool isSelected( int ) const; + bool isSelected( const QListBoxItem * ) const; + QListBoxItem* selectedItem() const; + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + QListBoxItem *item( int index ) const; + int index( const QListBoxItem * ) const; + QListBoxItem *findItem( const QString &text, ComparisonFlags compare = BeginsWith ) const; + + void triggerUpdate( bool doLayout ); + + bool itemVisible( int index ); + bool itemVisible( const QListBoxItem * ); + + enum LayoutMode { FixedNumber, + FitToWidth, FitToHeight = FitToWidth, + Variable }; + virtual void setColumnMode( LayoutMode ); + virtual void setColumnMode( int ); + virtual void setRowMode( LayoutMode ); + virtual void setRowMode( int ); + + LayoutMode columnMode() const; + LayoutMode rowMode() const; + + int numColumns() const; + int numRows() const; + + bool variableWidth() const; + virtual void setVariableWidth( bool ); + + bool variableHeight() const; + virtual void setVariableHeight( bool ); + + void viewportPaintEvent( QPaintEvent * ); + +#ifndef QT_NO_COMPAT + bool dragSelect() const { return TRUE; } + void setDragSelect( bool ) {} + bool autoScroll() const { return TRUE; } + void setAutoScroll( bool ) {} + bool autoScrollBar() const { return vScrollBarMode() == Auto; } + void setAutoScrollBar( bool enable ) { setVScrollBarMode( enable ? Auto : AlwaysOff ); } + bool scrollBar() const { return vScrollBarMode() != AlwaysOff; } + void setScrollBar( bool enable ) { setVScrollBarMode( enable ? AlwaysOn : AlwaysOff ); } + bool autoBottomScrollBar() const { return hScrollBarMode() == Auto; } + void setAutoBottomScrollBar( bool enable ) { setHScrollBarMode( enable ? Auto : AlwaysOff ); } + bool bottomScrollBar() const { return hScrollBarMode() != AlwaysOff; } + void setBottomScrollBar( bool enable ) { setHScrollBarMode( enable ? AlwaysOn : AlwaysOff ); } + bool smoothScrolling() const { return FALSE; } + void setSmoothScrolling( bool ) {} + bool autoUpdate() const { return TRUE; } + void setAutoUpdate( bool ) {} + void setFixedVisibleLines( int lines ) { setRowMode( lines ); } + int inSort( const QListBoxItem * ); + int inSort( const QString& text ); + int cellHeight( int i ) const { return itemHeight(i); } + int cellHeight() const { return itemHeight(); } + int cellWidth() const { return maxItemWidth(); } + int cellWidth(int i) const { Q_ASSERT(i==0); Q_UNUSED(i) return maxItemWidth(); } + int numCols() const { return numColumns(); } +#endif + + int itemHeight( int index = 0 ) const; + QListBoxItem * itemAt( const QPoint & ) const; + + QRect itemRect( QListBoxItem *item ) const; + + QListBoxItem *firstItem() const; + + void sort( bool ascending = TRUE ); + +public slots: + void clear(); + virtual void ensureCurrentVisible(); + virtual void clearSelection(); + virtual void selectAll( bool select ); + virtual void invertSelection(); + +signals: + void highlighted( int index ); + void selected( int index ); + void highlighted( const QString &); + void selected( const QString &); + void highlighted( QListBoxItem * ); + void selected( QListBoxItem * ); + + void selectionChanged(); + void selectionChanged( QListBoxItem * ); + void currentChanged( QListBoxItem * ); + void clicked( QListBoxItem * ); + void clicked( QListBoxItem *, const QPoint & ); + void pressed( QListBoxItem * ); + void pressed( QListBoxItem *, const QPoint & ); + + void doubleClicked( QListBoxItem * ); + void returnPressed( QListBoxItem * ); + void rightButtonClicked( QListBoxItem *, const QPoint & ); + void rightButtonPressed( QListBoxItem *, const QPoint & ); + void mouseButtonPressed( int, QListBoxItem*, const QPoint& ); + void mouseButtonClicked( int, QListBoxItem*, const QPoint& ); + + void contextMenuRequested( QListBoxItem *, const QPoint & ); + + void onItem( QListBoxItem *item ); + void onViewport(); + +protected: + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseDoubleClickEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void contentsContextMenuEvent( QContextMenuEvent * ); + + void keyPressEvent( QKeyEvent *e ); + void focusInEvent( QFocusEvent *e ); + void focusOutEvent( QFocusEvent *e ); + void resizeEvent( QResizeEvent * ); + void showEvent( QShowEvent * ); + + bool eventFilter( QObject *o, QEvent *e ); + + void updateItem( int index ); + void updateItem( QListBoxItem * ); + +#ifndef QT_NO_COMPAT + void updateCellWidth() { } + int totalWidth() const { return contentsWidth(); } + int totalHeight() const { return contentsHeight(); } +#endif + + virtual void paintCell( QPainter *, int row, int col ); + + void toggleCurrentItem(); + bool isRubberSelecting() const; + + void doLayout() const; + + void windowActivationChange( bool ); + +#ifndef QT_NO_COMPAT + bool itemYPos( int index, int *yPos ) const; + int findItem( int yPos ) const { return index(itemAt(QPoint(0,yPos)) ); } +#endif + +protected slots: + void clearInputString(); + +private slots: + void refreshSlot(); + void doAutoScroll(); + void adjustItems(); + +private: + void mousePressEventEx( QMouseEvent * ); + void tryGeometry( int, int ) const; + int currentRow() const; + int currentColumn() const; + void updateSelection(); + void repaintSelection(); + void drawRubber(); + void doRubberSelection( const QRect &old, const QRect &rubber ); + void handleItemChange( QListBoxItem *old, bool shift, bool control ); + void selectRange( QListBoxItem *from, QListBoxItem *to, bool invert, bool includeFirst, bool clearSel = FALSE ); + + void emitChangedSignal( bool ); + + int columnAt( int ) const; + int rowAt( int ) const; + + QListBoxPrivate * d; + + static QListBox * changedListBox; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QListBox( const QListBox & ); + QListBox &operator=( const QListBox & ); +#endif +}; + + +class Q_EXPORT QListBoxItem +{ +public: + QListBoxItem( QListBox* listbox = 0 ); + QListBoxItem( QListBox* listbox, QListBoxItem *after ); + virtual ~QListBoxItem(); + + virtual QString text() const; + virtual const QPixmap *pixmap() const; + + virtual int height( const QListBox * ) const; + virtual int width( const QListBox * ) const; + + bool isSelected() const { return s; } + bool isCurrent() const; + +#ifndef QT_NO_COMPAT + bool selected() const { return isSelected(); } + bool current() const { return isCurrent(); } +#endif + + QListBox *listBox() const; + + void setSelectable( bool b ); + bool isSelectable() const; + + QListBoxItem *next() const; + QListBoxItem *prev() const; + + virtual int rtti() const; + static int RTTI; + +protected: + virtual void paint( QPainter * ) = 0; + virtual void setText( const QString &text ) { txt = text; } + void setCustomHighlighting( bool ); + +private: + QString txt; + uint s:1; + uint dirty:1; + uint custom_highlight : 1; + int x, y; + QListBoxItem * p, * n; + QListBox* lbox; + friend class QListBox; + friend class QListBoxPrivate; + friend class QComboBox; + friend class QComboBoxPopupItem; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QListBoxItem( const QListBoxItem & ); + QListBoxItem &operator=( const QListBoxItem & ); +#endif +}; + + +class Q_EXPORT QListBoxText : public QListBoxItem +{ +public: + QListBoxText( QListBox* listbox, const QString & text=QString::null ); + QListBoxText( const QString & text=QString::null ); + QListBoxText( QListBox* listbox, const QString & text, QListBoxItem *after ); + ~QListBoxText(); + + int height( const QListBox * ) const; + int width( const QListBox * ) const; + + int rtti() const; + static int RTTI; + +protected: + void paint( QPainter * ); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QListBoxText( const QListBoxText & ); + QListBoxText &operator=( const QListBoxText & ); +#endif +}; + + +class Q_EXPORT QListBoxPixmap : public QListBoxItem +{ +public: + QListBoxPixmap( QListBox* listbox, const QPixmap & ); + QListBoxPixmap( const QPixmap & ); + QListBoxPixmap( QListBox* listbox, const QPixmap & pix, QListBoxItem *after ); + QListBoxPixmap( QListBox* listbox, const QPixmap &, const QString& ); + QListBoxPixmap( const QPixmap &, const QString& ); + QListBoxPixmap( QListBox* listbox, const QPixmap & pix, const QString&, QListBoxItem *after ); + ~QListBoxPixmap(); + + const QPixmap *pixmap() const { return ± } + + int height( const QListBox * ) const; + int width( const QListBox * ) const; + + int rtti() const; + static int RTTI; + +protected: + void paint( QPainter * ); + +private: + QPixmap pm; +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QListBoxPixmap( const QListBoxPixmap & ); + QListBoxPixmap &operator=( const QListBoxPixmap & ); +#endif +}; + + +#endif // QT_NO_LISTBOX + +#endif // QLISTBOX_H diff --git a/src/widgets/qlistview.cpp b/src/widgets/qlistview.cpp new file mode 100644 index 0000000..d441a59 --- /dev/null +++ b/src/widgets/qlistview.cpp @@ -0,0 +1,8174 @@ +/**************************************************************************** +** +** Implementation of QListView widget class +** +** Created : 970809 +** +** 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 "qlistview.h" +#ifndef QT_NO_LISTVIEW +#include "qtimer.h" +#include "qheader.h" +#include "qpainter.h" +#include "qcursor.h" +#include "qptrstack.h" +#include "qptrlist.h" +#include "qstrlist.h" +#include "qapplication.h" +#include "qbitmap.h" +#include "qdatetime.h" +#include "qptrdict.h" +#include "qptrvector.h" +#include "qiconset.h" +#include "qcleanuphandler.h" +#include "qpixmapcache.h" +#include "qpopupmenu.h" +#include "qtl.h" +#include "qdragobject.h" +#include "qlineedit.h" +#include "qvbox.h" +#include "qtooltip.h" +#include "qstyle.h" +#include "qstylesheet.h" +#include "../kernel/qinternal_p.h" +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif + +const int Unsorted = 16383; + +static QCleanupHandler<QBitmap> qlv_cleanup_bitmap; + +struct QListViewItemIteratorPrivate +{ + QListViewItemIteratorPrivate( uint f ) : flags( f ) + { + // nothing yet + } + + uint flags; +}; + +static QPtrDict<QListViewItemIteratorPrivate> *qt_iteratorprivate_dict = 0; + +struct QListViewPrivate +{ + // classes that are here to avoid polluting the global name space + + // the magical hidden mother of all items + class Root: public QListViewItem { + public: + Root( QListView * parent ); + + void setHeight( int ); + void invalidateHeight(); + void setup(); + QListView * theListView() const; + + QListView * lv; + }; + + // for the stack used in drawContentsOffset() + class Pending { + public: + Pending( int level, int ypos, QListViewItem * item) + : l(level), y(ypos), i(item) {}; + + int l; // level of this item; root is -1 or 0 + int y; // level of this item in the tree + QListViewItem * i; // the item itself + }; + + // to remember what's on screen + class DrawableItem { + public: + DrawableItem( Pending * pi ) { y = pi->y; l = pi->l; i = pi->i; }; + int y; + int l; + QListViewItem * i; + }; + + // for sorting + class SortableItem { + public: + /* + We could be smarter and keep a pointer to the QListView + item instead of numCols, col and asc. This would then allow + us to use the physical ordering of columns rather than the + logical. Microsoft uses the logical ordering, so there is + some virtue in doing so, although it prevents the user from + chosing the secondary key. + */ + QListViewItem * item; + int numCols; + int col; + bool asc; + + int cmp( const SortableItem& i ) const { + int diff = item->compare( i.item, col, asc ); + if ( diff == 0 && numCols != 1 ) { + for ( int j = 0; j < numCols; j++ ) { + if ( j != col ) { + diff = item->compare( i.item, j, asc ); + if ( diff != 0 ) + break; + } + } + } + return diff; + } + bool operator<( const SortableItem& i ) const { return cmp( i ) < 0; } + bool operator<=( const SortableItem& i ) const { return cmp( i ) <= 0; } + bool operator>( const SortableItem& i ) const { return cmp( i ) > 0; } + }; + + class ItemColumnInfo { + public: + ItemColumnInfo(): pm( 0 ), next( 0 ), truncated( FALSE ), dirty( FALSE ), allow_rename( FALSE ), width( 0 ) {} + ~ItemColumnInfo() { delete pm; delete next; } + QString text, tmpText; + QPixmap * pm; + ItemColumnInfo * next; + uint truncated : 1; + uint dirty : 1; + uint allow_rename : 1; + int width; + }; + + class ViewColumnInfo { + public: + ViewColumnInfo(): align(Qt::AlignAuto), sortable(TRUE), next( 0 ) {} + ~ViewColumnInfo() { delete next; } + int align; + bool sortable; + ViewColumnInfo * next; + }; + + // private variables used in QListView + ViewColumnInfo * vci; + QHeader * h; + Root * r; + uint rootIsExpandable : 1; + int margin; + + QListViewItem * focusItem, *highlighted, *oldFocusItem; + + QTimer * timer; + QTimer * dirtyItemTimer; + QTimer * visibleTimer; + int levelWidth; + + // the list of drawables, and the range drawables covers entirely + // (it may also include a few items above topPixel) + QPtrList<DrawableItem> * drawables; + int topPixel; + int bottomPixel; + + QPtrDict<void> * dirtyItems; + + QListView::SelectionMode selectionMode; + + // Per-column structure for information not in the QHeader + struct Column { + QListView::WidthMode wmode; + }; + QPtrVector<Column> column; + + // suggested height for the items + int fontMetricsHeight; + int minLeftBearing, minRightBearing; + int ellipsisWidth; + + // currently typed prefix for the keyboard interface, and the time + // of the last key-press + QString currentPrefix; + QTime currentPrefixTime; + + // holds a list of iterators + QPtrList<QListViewItemIterator> *iterators; + QListViewItem *pressedItem, *selectAnchor; + + QTimer *scrollTimer; + QTimer *renameTimer; + QTimer *autoopenTimer; + + // sort column and order #### may need to move to QHeader [subclass] + int sortcolumn; + bool ascending :1; + bool sortIndicator :1; + // whether to select or deselect during this mouse press. + bool allColumnsShowFocus :1; + bool select :1; + + // TRUE if the widget should take notice of mouseReleaseEvent + bool buttonDown :1; + // TRUE if the widget should ignore a double-click + bool ignoreDoubleClick :1; + + bool clearing :1; + bool pressedSelected :1; + bool pressedEmptyArea :1; + + bool useDoubleBuffer :1; + bool toolTips :1; + bool fullRepaintOnComlumnChange:1; + bool updateHeader :1; + + bool startEdit : 1; + bool ignoreEditAfterFocus : 1; + bool inMenuMode :1; + + QListView::RenameAction defRenameAction; + + QListViewItem *startDragItem; + QPoint dragStartPos; + QListViewToolTip *toolTip; + int pressedColumn; + QListView::ResizeMode resizeMode; +}; + +#ifndef QT_NO_TOOLTIP +class QListViewToolTip : public QToolTip +{ +public: + QListViewToolTip( QWidget *parent, QListView *lv ); + + void maybeTip( const QPoint &pos ); + +private: + QListView *view; + +}; + +QListViewToolTip::QListViewToolTip( QWidget *parent, QListView *lv ) + : QToolTip( parent ), view( lv ) +{ +} + +void QListViewToolTip::maybeTip( const QPoint &pos ) +{ + if ( !parentWidget() || !view || !view->showToolTips() ) + return; + + QListViewItem *item = view->itemAt( pos ); + QPoint contentsPos = view->viewportToContents( pos ); + if ( !item || !item->columns ) + return; + int col = view->header()->sectionAt( contentsPos.x() ); + QListViewPrivate::ItemColumnInfo *ci = (QListViewPrivate::ItemColumnInfo*)item->columns; + for ( int i = 0; ci && ( i < col ); ++i ) + ci = ci->next; + + if ( !ci || !ci->truncated ) + return; + + QRect r = view->itemRect( item ); + int headerPos = view->header()->sectionPos( col ); + r.setLeft( headerPos ); + r.setRight( headerPos + view->header()->sectionSize( col ) ); + tip( r, QStyleSheet::escape(item->text( col ) ) ); +} +#endif + +// these should probably be in QListViewPrivate, for future thread safety +static bool activatedByClick; + +// Holds the position of activation. The position is relative to the top left corner in the row minus the root decoration and its branches/depth. +// Used to track the point clicked for CheckBoxes and RadioButtons. +static QPoint activatedP; + +#if defined(QT_ACCESSIBILITY_SUPPORT) +static int indexOfItem( QListViewItem *item ) +{ + if ( !QAccessible::isActive() ) + return 0; + + static QListViewItem *lastItem = 0; + static int lastIndex = 0; + + if ( !item || !item->listView() ) + return 0; + + if ( item == lastItem ) + return lastIndex; + + lastItem = item; + int index = 1; + + QListViewItemIterator it( item->listView() ); + while ( it.current() ) { + if ( it.current() == item ) { + lastIndex = index; + return index; + } + ++it; + ++index; + } + lastIndex = 0; + return 0; +} +#endif + +/*! + \internal + Creates a string with ... like "Trollte..." or "...olltech" depending on the alignment +*/ + +static QString qEllipsisText( const QString &org, const QFontMetrics &fm, int width, int align ) +{ + int ellWidth = fm.width( "..." ); + QString text = QString::fromLatin1(""); + int i = 0; + int len = org.length(); + int offset = (align & Qt::AlignRight) ? (len-1) - i : i; + while ( i < len && fm.width( text + org[ offset ] ) + ellWidth < width ) { + if ( align & Qt::AlignRight ) + text.prepend( org[ offset ] ); + else + text += org[ offset ]; + offset = (align & Qt::AlignRight) ? (len-1) - ++i : ++i; + } + if ( text.isEmpty() ) + text = ( align & Qt::AlignRight ) ? org.right( 1 ) : text = org.left( 1 ); + if ( align & Qt::AlignRight ) + text.prepend( "..." ); + else + text += "..."; + return text; +} + +/*! + \class QListViewItem + \brief The QListViewItem class implements a list view item. + + \ingroup advanced + + A list view item is a multi-column object capable of displaying + itself in a QListView. + + The easiest way to use QListViewItem is to construct one with a + few constant strings, and either a QListView or another + QListViewItem as parent. + \code + (void) new QListViewItem( listView, "Column 1", "Column 2" ); + (void) new QListViewItem( listView->firstChild(), "A", "B", "C" ); + \endcode + We've discarded the pointers to the items since we can still access + them via their parent \e listView. By default, QListView sorts its + items; this can be switched off with QListView::setSorting(-1). + + The parent must be another QListViewItem or a QListView. If the + parent is a QListView, the item becomes a top-level item within + that QListView. If the parent is another QListViewItem, the item + becomes a child of that list view item. + + If you keep the pointer, you can set or change the texts using + setText(), add pixmaps using setPixmap(), change its mode using + setSelectable(), setSelected(), setOpen() and setExpandable(). + You'll also be able to change its height using setHeight(), and + traverse its sub-items. You don't have to keep the pointer since + you can get a pointer to any QListViewItem in a QListView using + QListView::selectedItem(), QListView::currentItem(), + QListView::firstChild(), QListView::lastItem() and + QListView::findItem(). + + If you call \c delete on a list view item, it will be deleted as + expected, and as usual for \l{QObject}s, if it has any child items + (to any depth), all these will be deleted too. + + \l{QCheckListItem}s are list view items that have a checkbox or + radio button and can be used in place of plain QListViewItems. + + You can traverse the tree as if it were a doubly-linked list using + itemAbove() and itemBelow(); they return pointers to the items + directly above and below this item on the screen (even if none of + them are actually visible at the moment). + + Here's how to traverse all of an item's children (but not its + children's children, etc.): + Example: + \code + QListViewItem * myChild = myItem->firstChild(); + while( myChild ) { + doSomething( myChild ); + myChild = myChild->nextSibling(); + } + \endcode + + If you want to iterate over every item, to any level of depth use + an iterator. To iterate over the entire tree, initialize the + iterator with the list view itself; to iterate starting from a + particular item, initialize the iterator with the item: + + \code + QListViewItemIterator it( listview ); + while ( it.current() ) { + QListViewItem *item = it.current(); + doSomething( item ); + ++it; + } + \endcode + + Note that the order of the children will change when the sorting + order changes and is undefined if the items are not visible. You + can, however, call enforceSortOrder() at any time; QListView will + always call it before it needs to show an item. + + Many programs will need to reimplement QListViewItem. The most + commonly reimplemented functions are: + \table + \header \i Function \i Description + \row \i \l text() + \i Returns the text in a column. Many subclasses will compute + this on the fly. + \row \i \l key() + \i Used for sorting. The default key() simply calls + text(), but judicious use of key() can give you fine + control over sorting; for example, QFileDialog + reimplements key() to sort by date. + \row \i \l setup() + \i Called before showing the item and whenever the list + view's font changes, for example. + \row \i \l activate() + \i Called whenever the user clicks on the item or presses + Space when the item is the current item. + \endtable + + Some subclasses call setExpandable(TRUE) even when they have no + children, and populate themselves when setup() or setOpen(TRUE) is + called. The \c dirview/dirview.cpp example program uses this + technique to start up quickly: The files and subdirectories in a + directory aren't inserted into the tree until they're actually + needed. + + \img qlistviewitems.png List View Items + + \sa QCheckListItem QListView +*/ + +/*! + \fn int QCheckListItem::rtti() const + + Returns 1. + + Make your derived classes return their own values for rtti(), and + you can distinguish between list view items. You should use values + greater than 1000, to allow for extensions to this class. +*/ + +/*! + Constructs a new top-level list view item in the QListView \a + parent. +*/ + +QListViewItem::QListViewItem( QListView * parent ) +{ + init(); + parent->insertItem( this ); +} + + +/*! + Constructs a new list view item that is a child of \a parent and + first in the parent's list of children. +*/ + +QListViewItem::QListViewItem( QListViewItem * parent ) +{ + init(); + parent->insertItem( this ); +} + + + + +/*! + Constructs an empty list view item that is a child of \a parent + and is after item \a after in the parent's list of children. Since + \a parent is a QListView the item will be a top-level item. +*/ + +QListViewItem::QListViewItem( QListView * parent, QListViewItem * after ) +{ + init(); + parent->insertItem( this ); + moveToJustAfter( after ); +} + + +/*! + Constructs an empty list view item that is a child of \a parent + and is after item \a after in the parent's list of children. +*/ + +QListViewItem::QListViewItem( QListViewItem * parent, QListViewItem * after ) +{ + init(); + parent->insertItem( this ); + moveToJustAfter( after ); +} + + + +/*! + Constructs a new top-level list view item in the QListView \a + parent, with up to eight constant strings, \a label1, \a label2, \a + label3, \a label4, \a label5, \a label6, \a label7 and \a label8 + defining its columns' contents. + + \sa setText() +*/ + +QListViewItem::QListViewItem( QListView * parent, + QString label1, + QString label2, + QString label3, + QString label4, + QString label5, + QString label6, + QString label7, + QString label8 ) +{ + init(); + parent->insertItem( this ); + + setText( 0, label1 ); + setText( 1, label2 ); + setText( 2, label3 ); + setText( 3, label4 ); + setText( 4, label5 ); + setText( 5, label6 ); + setText( 6, label7 ); + setText( 7, label8 ); +} + + +/*! + Constructs a new list view item as a child of the QListViewItem \a + parent with up to eight constant strings, \a label1, \a label2, \a + label3, \a label4, \a label5, \a label6, \a label7 and \a label8 + as columns' contents. + + \sa setText() +*/ + +QListViewItem::QListViewItem( QListViewItem * parent, + QString label1, + QString label2, + QString label3, + QString label4, + QString label5, + QString label6, + QString label7, + QString label8 ) +{ + init(); + parent->insertItem( this ); + + setText( 0, label1 ); + setText( 1, label2 ); + setText( 2, label3 ); + setText( 3, label4 ); + setText( 4, label5 ); + setText( 5, label6 ); + setText( 6, label7 ); + setText( 7, label8 ); +} + +/*! + Constructs a new list view item in the QListView \a parent that is + included after item \a after and that has up to eight column + texts, \a label1, \a label2, \a label3, \a label4, \a label5, \a + label6, \a label7 and\a label8. + + Note that the order is changed according to QListViewItem::key() + unless the list view's sorting is disabled using + QListView::setSorting(-1). + + \sa setText() +*/ + +QListViewItem::QListViewItem( QListView * parent, QListViewItem * after, + QString label1, + QString label2, + QString label3, + QString label4, + QString label5, + QString label6, + QString label7, + QString label8 ) +{ + init(); + parent->insertItem( this ); + moveToJustAfter( after ); + + setText( 0, label1 ); + setText( 1, label2 ); + setText( 2, label3 ); + setText( 3, label4 ); + setText( 4, label5 ); + setText( 5, label6 ); + setText( 6, label7 ); + setText( 7, label8 ); +} + + +/*! + Constructs a new list view item as a child of the QListViewItem \a + parent. It is inserted after item \a after and may contain up to + eight strings, \a label1, \a label2, \a label3, \a label4, \a + label5, \a label6, \a label7 and \a label8 as column entries. + + Note that the order is changed according to QListViewItem::key() + unless the list view's sorting is disabled using + QListView::setSorting(-1). + + \sa setText() +*/ + +QListViewItem::QListViewItem( QListViewItem * parent, QListViewItem * after, + QString label1, + QString label2, + QString label3, + QString label4, + QString label5, + QString label6, + QString label7, + QString label8 ) +{ + init(); + parent->insertItem( this ); + moveToJustAfter( after ); + + setText( 0, label1 ); + setText( 1, label2 ); + setText( 2, label3 ); + setText( 3, label4 ); + setText( 4, label5 ); + setText( 5, label6 ); + setText( 6, label7 ); + setText( 7, label8 ); +} + +/*! + Sorts all this item's child items using the current sorting + configuration (sort column and direction). + + \sa enforceSortOrder() +*/ + +void QListViewItem::sort() +{ + if ( !listView() ) + return; + lsc = Unsorted; + enforceSortOrder(); + listView()->triggerUpdate(); +} + +int QListViewItem::RTTI = 0; + +/*! + Returns 0. + + Make your derived classes return their own values for rtti(), so + that you can distinguish between different kinds of list view + items. You should use values greater than 1000 to allow for + extensions to this class. +*/ + +int QListViewItem::rtti() const +{ + return RTTI; +} + +/* + Performs the initializations that's common to the constructors. +*/ + +void QListViewItem::init() +{ + ownHeight = 0; + maybeTotalHeight = -1; + open = FALSE; + + nChildren = 0; + parentItem = 0; + siblingItem = childItem = 0; + + columns = 0; + + selected = 0; + + lsc = Unsorted; + lso = TRUE; // unsorted in ascending order :) + configured = FALSE; + expandable = FALSE; + selectable = TRUE; + is_root = FALSE; + allow_drag = FALSE; + allow_drop = FALSE; + visible = TRUE; + renameBox = 0; + enabled = TRUE; + mlenabled = FALSE; +} + +/*! + If \a b is TRUE, the item is made visible; otherwise it is hidden. + + If the item is not visible, itemAbove() and itemBelow() will never + return this item, although you still can reach it by using e.g. + QListViewItemIterator. +*/ + +void QListViewItem::setVisible( bool b ) +{ + if ( b == (bool)visible ) + return; + QListView *lv = listView(); + if ( !lv ) + return; + if ( b && parent() && !parent()->isVisible() ) + return; + visible = b; + configured = FALSE; + setHeight( 0 ); + invalidateHeight(); + if ( parent() ) + parent()->invalidateHeight(); + else + lv->d->r->invalidateHeight(); + for ( QListViewItem *i = childItem; i; i = i->siblingItem ) + i->setVisible( b ); + if ( lv ) + lv->triggerUpdate(); +} + +/*! + Returns TRUE if the item is visible; otherwise returns FALSE. + + \sa setVisible() +*/ + +bool QListViewItem::isVisible() const +{ + return (bool)visible; +} + +/*! + If \a b is TRUE, this item can be in-place renamed in the column + \a col by the user; otherwise it cannot be renamed in-place. +*/ + +void QListViewItem::setRenameEnabled( int col, bool b ) +{ + QListViewPrivate::ItemColumnInfo * l = (QListViewPrivate::ItemColumnInfo*)columns; + if ( !l ) { + l = new QListViewPrivate::ItemColumnInfo; + columns = (void*)l; + } + for( int c = 0; c < col; c++ ) { + if ( !l->next ) + l->next = new QListViewPrivate::ItemColumnInfo; + l = l->next; + } + + if ( !l ) + return; + l->allow_rename = b; +} + +/*! + Returns TRUE if this item can be in-place renamed in column \a + col; otherwise returns FALSE. +*/ + +bool QListViewItem::renameEnabled( int col ) const +{ + QListViewPrivate::ItemColumnInfo * l = (QListViewPrivate::ItemColumnInfo*)columns; + if ( !l ) + return FALSE; + + while( col && l ) { + l = l->next; + col--; + } + + if ( !l ) + return FALSE; + return (bool)l->allow_rename; +} + +/*! + If \a b is TRUE the item is enabled; otherwise it is disabled. + Disabled items are drawn differently (e.g. grayed-out) and are not + accessible by the user. +*/ + +void QListViewItem::setEnabled( bool b ) +{ + if ( (bool)enabled == b ) + return; + enabled = b; + if ( !enabled ) + selected = FALSE; + QListView *lv = listView(); + if ( lv ) { + lv->triggerUpdate(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( lv->viewport(), indexOfItem( this ), QAccessible::StateChanged ); +#endif + } +} + +/*! + Returns TRUE if this item is enabled; otherwise returns FALSE. + + \sa setEnabled() +*/ + +bool QListViewItem::isEnabled() const +{ + return (bool)enabled; +} + +/*! + If in-place renaming of this item is enabled (see + renameEnabled()), this function starts renaming the item in column + \a col, by creating and initializing an edit box. +*/ + +void QListViewItem::startRename( int col ) +{ + if ( !renameEnabled( col ) ) + return; + if ( renameBox ) + cancelRename( col ); + QListView *lv = listView(); + if ( !lv ) + return; + + if ( lv->d->renameTimer ) + lv->d->renameTimer->stop(); + + lv->ensureItemVisible( this ); + + if ( lv->d->timer->isActive() ) { + // make sure that pending calculations get finished + lv->d->timer->stop(); + lv->updateContents(); + } + + if ( lv->currentItem() && lv->currentItem()->renameBox ) { + if ( lv->d->defRenameAction == QListView::Reject ) + lv->currentItem()->cancelRename( lv->currentItem()->renameCol ); + else + lv->currentItem()->okRename( lv->currentItem()->renameCol ); + } + + if ( this != lv->currentItem() ) + lv->setCurrentItem( this ); + + QRect r = lv->itemRect( this ); + r = QRect( lv->viewportToContents( r.topLeft() ), r.size() ); + r.setLeft( lv->header()->sectionPos( col ) ); + r.setWidth(QMIN(lv->header()->sectionSize(col) - 1, + lv->contentsX() + lv->visibleWidth() - r.left())); + if ( col == 0 ) + r.setLeft( r.left() + lv->itemMargin() + ( depth() + ( lv->rootIsDecorated() ? 1 : 0 ) ) * lv->treeStepSize() - 1 ); + if ( pixmap( col ) ) + r.setLeft( r.left() + pixmap( col )->width() ); + if ( r.x() - lv->contentsX() < 0 ) { + lv->scrollBy( r.x() - lv->contentsX(), 0 ); + r.setX( lv->contentsX() ); + } else if ( ( lv->contentsX() + lv->visibleWidth() ) < ( r.x() + r.width() ) ) { + lv->scrollBy( ( r.x() + r.width() ) - ( lv->contentsX() + lv->visibleWidth() ), 0 ); + } + if ( r.width() > lv->visibleWidth() ) + r.setWidth( lv->visibleWidth() ); + renameBox = new QLineEdit( lv->viewport(), "qt_renamebox" ); + renameBox->setFrameStyle( QFrame::Box | QFrame::Plain ); + renameBox->setLineWidth( 1 ); + renameBox->setText( text( col ) ); + renameBox->selectAll(); + renameBox->installEventFilter( lv ); + lv->addChild( renameBox, r.x(), r.y() ); + renameBox->resize( r.size() ); + lv->viewport()->setFocusProxy( renameBox ); + renameBox->setFocus(); + renameBox->show(); + renameCol = col; +} + +/*! + This function removes the rename box. +*/ + +void QListViewItem::removeRenameBox() +{ + // Sanity, it should be checked by the functions calling this first anyway + QListView *lv = listView(); + if ( !lv || !renameBox ) + return; + const bool resetFocus = lv->viewport()->focusProxy() == renameBox; + const bool renameBoxHadFocus = renameBox->hasFocus(); + delete renameBox; + renameBox = 0; + if ( resetFocus ) + lv->viewport()->setFocusProxy( lv ); + if (renameBoxHadFocus) + lv->setFocus(); +} + +/*! + This function is called if the user presses Enter during in-place + renaming of the item in column \a col. + + \sa cancelRename() +*/ + +void QListViewItem::okRename( int col ) +{ + QListView *lv = listView(); + if ( !lv || !renameBox ) + return; + setText( col, renameBox->text() ); + removeRenameBox(); + + // we set the parent lsc to Unsorted if that column is the sorted one + if ( parent() && (int)parent()->lsc == col ) + parent()->lsc = Unsorted; + + emit lv->itemRenamed( this, col ); + emit lv->itemRenamed( this, col, text( col ) ); +} + +/*! + This function is called if the user cancels in-place renaming of + this item in column \a col (e.g. by pressing Esc). + + \sa okRename() +*/ + +void QListViewItem::cancelRename( int ) +{ + QListView *lv = listView(); + if ( !lv || !renameBox ) + return; + removeRenameBox(); +} + +/*! + Destroys the item, deleting all its children and freeing up all + allocated resources. +*/ + +QListViewItem::~QListViewItem() +{ + if ( renameBox ) { + delete renameBox; + renameBox = 0; + } + + QListView *lv = listView(); + + if ( lv ) { + if ( lv->d->oldFocusItem == this ) + lv->d->oldFocusItem = 0; + if ( lv->d->iterators ) { + QListViewItemIterator *i = lv->d->iterators->first(); + while ( i ) { + if ( i->current() == this ) + i->currentRemoved(); + i = lv->d->iterators->next(); + } + } + } + + if ( parentItem ) + parentItem->takeItem( this ); + QListViewItem * i = childItem; + childItem = 0; + while ( i ) { + i->parentItem = 0; + QListViewItem * n = i->siblingItem; + delete i; + i = n; + } + delete (QListViewPrivate::ItemColumnInfo *)columns; +} + + +/*! + If \a b is TRUE each of the item's columns may contain multiple + lines of text; otherwise each of them may only contain a single + line. +*/ + +void QListViewItem::setMultiLinesEnabled( bool b ) +{ + mlenabled = b; +} + +/*! + Returns TRUE if the item can display multiple lines of text in its + columns; otherwise returns FALSE. +*/ + +bool QListViewItem::multiLinesEnabled() const +{ + return mlenabled; +} + +/*! + If \a allow is TRUE, the list view starts a drag (see + QListView::dragObject()) when the user presses and moves the mouse + on this item. +*/ + + +void QListViewItem::setDragEnabled( bool allow ) +{ + allow_drag = (uint)allow; +} + +/*! + If \a allow is TRUE, the list view accepts drops onto the item; + otherwise drops are not allowed. +*/ + +void QListViewItem::setDropEnabled( bool allow ) +{ + allow_drop = (uint)allow; +} + +/*! + Returns TRUE if this item can be dragged; otherwise returns FALSE. + + \sa setDragEnabled() +*/ + +bool QListViewItem::dragEnabled() const +{ + return (bool)allow_drag; +} + +/*! + Returns TRUE if this item accepts drops; otherwise returns FALSE. + + \sa setDropEnabled(), acceptDrop() +*/ + +bool QListViewItem::dropEnabled() const +{ + return (bool)allow_drop; +} + +/*! + Returns TRUE if the item can accept drops of type QMimeSource \a + mime; otherwise returns FALSE. + + The default implementation does nothing and returns FALSE. A + subclass must reimplement this to accept drops. +*/ + +bool QListViewItem::acceptDrop( const QMimeSource * ) const +{ + return FALSE; +} + +#ifndef QT_NO_DRAGANDDROP + +/*! + This function is called when something was dropped on the item. \a e + contains all the information about the drop. + + The default implementation does nothing, subclasses may need to + reimplement this function. +*/ + +void QListViewItem::dropped( QDropEvent *e ) +{ + Q_UNUSED( e ); +} + +#endif + +/*! + This function is called when a drag enters the item's bounding + rectangle. + + The default implementation does nothing, subclasses may need to + reimplement this function. +*/ + +void QListViewItem::dragEntered() +{ +} + +/*! + This function is called when a drag leaves the item's bounding + rectangle. + + The default implementation does nothing, subclasses may need to + reimplement this function. +*/ + +void QListViewItem::dragLeft() +{ +} + +/*! + Inserts \a newChild into this list view item's list of children. + You should not need to call this function; it is called + automatically by the constructor of \a newChild. + + \warning If you are using \c Single selection mode, then you + should only insert unselected items. +*/ + +void QListViewItem::insertItem( QListViewItem * newChild ) +{ + QListView *lv = listView(); + if ( lv && lv->currentItem() && lv->currentItem()->renameBox ) { + if ( lv->d->defRenameAction == QListView::Reject ) + lv->currentItem()->cancelRename( lv->currentItem()->renameCol ); + else + lv->currentItem()->okRename( lv->currentItem()->renameCol ); + } + + if ( !newChild || newChild->parentItem == this ) + return; + if ( newChild->parentItem ) + newChild->parentItem->takeItem( newChild ); + if ( open ) + invalidateHeight(); + newChild->siblingItem = childItem; + childItem = newChild; + nChildren++; + newChild->parentItem = this; + lsc = Unsorted; + newChild->ownHeight = 0; + newChild->configured = FALSE; + + if ( lv && !lv->d->focusItem ) { + lv->d->focusItem = lv->firstChild(); + lv->d->selectAnchor = lv->d->focusItem; + lv->repaintItem( lv->d->focusItem ); + } +} + + +/*! + \fn void QListViewItem::removeItem( QListViewItem * ) + \obsolete + + This function has been renamed takeItem(). +*/ + + +/*! + Removes \a item from this object's list of children and causes an + update of the screen display. The item is not deleted. You should + not normally need to call this function because + QListViewItem::~QListViewItem() calls it. + + The normal way to delete an item is to use \c delete. + + If you need to move an item from one place in the hierarchy to + another you can use takeItem() to remove the item from the list + view and then insertItem() to put the item back in its new + position. + + If a taken item is part of a selection in \c Single selection + mode, it is unselected and selectionChanged() is emitted. If a + taken item is part of a selection in \c Multi or \c Extended + selection mode, it remains selected. + + \warning This function leaves \a item and its children in a state + where most member functions are unsafe. Only a few functions work + correctly on an item in this state, most notably insertItem(). The + functions that work on taken items are explicitly documented as + such. + + \sa QListViewItem::insertItem() +*/ + +void QListViewItem::takeItem( QListViewItem * item ) +{ + if ( !item ) + return; + + QListView *lv = listView(); + if ( lv && lv->currentItem() && lv->currentItem()->renameBox ) { + if ( lv->d->defRenameAction == QListView::Reject ) + lv->currentItem()->cancelRename( lv->currentItem()->renameCol ); + else + lv->currentItem()->okRename( lv->currentItem()->renameCol ); + } + bool emit_changed = FALSE; + if ( lv && !lv->d->clearing ) { + if ( lv->d->oldFocusItem == this ) + lv->d->oldFocusItem = 0; + + if ( lv->d->iterators ) { + QListViewItemIterator *i = lv->d->iterators->first(); + while ( i ) { + if ( i->current() == item ) + i->currentRemoved(); + i = lv->d->iterators->next(); + } + } + + invalidateHeight(); + + if ( lv->d && lv->d->drawables ) { + delete lv->d->drawables; + lv->d->drawables = 0; + } + + if ( lv->d->dirtyItems ) { + if ( item->childItem ) { + delete lv->d->dirtyItems; + lv->d->dirtyItems = 0; + lv->d->dirtyItemTimer->stop(); + lv->triggerUpdate(); + } else { + lv->d->dirtyItems->take( (void *)item ); + } + } + + if ( lv->d->focusItem ) { + const QListViewItem * c = lv->d->focusItem; + while( c && c != item ) + c = c->parentItem; + if ( c == item ) { + if ( lv->selectedItem() ) { + // for Single, setSelected( FALSE ) when selectedItem() is taken + lv->selectedItem()->setSelected( FALSE ); + // we don't emit selectionChanged( 0 ) + emit lv->selectionChanged(); + } + if ( item->nextSibling() ) + lv->d->focusItem = item->nextSibling(); + else if ( item->itemAbove() ) + lv->d->focusItem = item->itemAbove(); + else + lv->d->focusItem = 0; + emit_changed = TRUE; + } + } + + // reset anchors etc. if they are set to this or any child + // items + const QListViewItem *ptr = lv->d->selectAnchor; + while (ptr && ptr != item) + ptr = ptr->parentItem; + if (ptr == item) + lv->d->selectAnchor = lv->d->focusItem; + + ptr = lv->d->startDragItem; + while (ptr && ptr != item) + ptr = ptr->parentItem; + if (ptr == item) + lv->d->startDragItem = 0; + + ptr = lv->d->pressedItem; + while (ptr && ptr != item) + ptr = ptr->parentItem; + if (ptr == item) + lv->d->pressedItem = 0; + + ptr = lv->d->highlighted; + while (ptr && ptr != item) + ptr = ptr->parentItem; + if (ptr == item) + lv->d->highlighted = 0; + } + + nChildren--; + + QListViewItem ** nextChild = &childItem; + while( nextChild && *nextChild && item != *nextChild ) + nextChild = &((*nextChild)->siblingItem); + + if ( nextChild && item == *nextChild ) + *nextChild = (*nextChild)->siblingItem; + item->parentItem = 0; + item->siblingItem = 0; + item->ownHeight = 0; + item->maybeTotalHeight = -1; + item->configured = FALSE; + + if ( emit_changed ) { + emit lv->currentChanged( lv->d->focusItem ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( lv->viewport(), 0, QAccessible::Focus ); +#endif + } +} + + +/*! + \fn QString QListViewItem::key( int column, bool ascending ) const + + Returns a key that can be used for sorting by column \a column. + The default implementation returns text(). Derived classes may + also incorporate the order indicated by \a ascending into this + key, although this is not recommended. + + If you want to sort on non-alphabetical data, e.g. dates, numbers, + etc., it is more efficient to reimplement compare(). + + \sa compare(), sortChildItems() +*/ + +QString QListViewItem::key( int column, bool ) const +{ + return text( column ); +} + + +/*! + Compares this list view item to \a i using the column \a col in \a + ascending order. Returns \< 0 if this item is less than \a i, 0 if + they are equal and \> 0 if this item is greater than \a i. + + This function is used for sorting. + + The default implementation compares the item keys (key()) using + QString::localeAwareCompare(). A reimplementation can use + different values and a different comparison function. Here is a + reimplementation that uses plain Unicode comparison: + + \code + int MyListViewItem::compare( QListViewItem *i, int col, + bool ascending ) const + { + return key( col, ascending ).compare( i->key( col, ascending) ); + } + \endcode + We don't recommend using \a ascending so your code can safely + ignore it. + + \sa key() QString::localeAwareCompare() QString::compare() +*/ + +int QListViewItem::compare( QListViewItem *i, int col, bool ascending ) const +{ + return key( col, ascending ).localeAwareCompare( i->key( col, ascending ) ); +} + +/*! + Sorts this item's children using column \a column. This is done in + ascending order if \a ascending is TRUE and in descending order if + \a ascending is FALSE. + + Asks some of the children to sort their children. (QListView and + QListViewItem ensure that all on-screen objects are properly + sorted but may avoid or defer sorting other objects in order to be + more responsive.) + + \sa key() compare() +*/ + +void QListViewItem::sortChildItems( int column, bool ascending ) +{ + // we try HARD not to sort. if we're already sorted, don't. + if ( column == (int)lsc && ascending == (bool)lso ) + return; + + if ( column < 0 ) + return; + + lsc = column; + lso = ascending; + + const int nColumns = ( listView() ? listView()->columns() : 0 ); + + // only sort if the item have more than one child. + if ( column > nColumns || childItem == 0 || (childItem->siblingItem == 0 && childItem->childItem == 0)) + return; + + // make an array for qHeapSort() + QListViewPrivate::SortableItem * siblings + = new QListViewPrivate::SortableItem[nChildren]; + QListViewItem * s = childItem; + int i = 0; + while ( s && i < nChildren ) { + siblings[i].numCols = nColumns; + siblings[i].col = column; + siblings[i].asc = ascending; + siblings[i].item = s; + s = s->siblingItem; + i++; + } + + // and sort it. + qHeapSort( siblings, siblings + nChildren ); + + // build the linked list of siblings, in the appropriate + // direction, and finally set this->childItem to the new top + // child. + if ( ascending ) { + for( i = 0; i < nChildren - 1; i++ ) + siblings[i].item->siblingItem = siblings[i+1].item; + siblings[nChildren-1].item->siblingItem = 0; + childItem = siblings[0].item; + } else { + for( i = nChildren - 1; i > 0; i-- ) + siblings[i].item->siblingItem = siblings[i-1].item; + siblings[0].item->siblingItem = 0; + childItem = siblings[nChildren-1].item; + } + for ( i = 0; i < nChildren; i++ ) { + if ( siblings[i].item->isOpen() ) + siblings[i].item->sort(); + } + delete[] siblings; +} + + +/*! + Sets this item's height to \a height pixels. This implicitly + changes totalHeight(), too. + + Note that a font change causes this height to be overwritten + unless you reimplement setup(). + + For best results in Windows style we suggest using an even number + of pixels. + + \sa height() totalHeight() isOpen(); +*/ + +void QListViewItem::setHeight( int height ) +{ + if ( ownHeight != height ) { + if ( visible ) + ownHeight = height; + else + ownHeight = 0; + invalidateHeight(); + } +} + + +/*! + Invalidates the cached total height of this item, including all + open children. + + \sa setHeight() height() totalHeight() +*/ + +void QListViewItem::invalidateHeight() +{ + if ( maybeTotalHeight < 0 ) + return; + maybeTotalHeight = -1; + if ( parentItem && parentItem->isOpen() ) + parentItem->invalidateHeight(); +} + + +/*! + Opens or closes an item, i.e. shows or hides an item's children. + + If \a o is TRUE all child items are shown initially. The user can + hide them by clicking the <b>-</b> icon to the left of the item. + If \a o is FALSE, the children of this item are initially hidden. + The user can show them by clicking the <b>+</b> icon to the left + of the item. + + \sa height() totalHeight() isOpen() +*/ + +void QListViewItem::setOpen( bool o ) +{ + if ( o == (bool)open || !enabled ) + return; + open = o; + + // If no children to show simply emit signals and return + if ( !nChildren ) { + QListView *lv = listView(); + if ( lv && this != lv->d->r ) { + if ( o ) + emit lv->expanded( this ); + else + emit lv->collapsed( this ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( lv->viewport(), indexOfItem( this ), QAccessible::StateChanged ); +#endif + } + return; + } + invalidateHeight(); + + if ( !configured ) { + QListViewItem * l = this; + QPtrStack<QListViewItem> s; + while( l ) { + if ( l->open && l->childItem ) { + s.push( l->childItem ); + } else if ( l->childItem ) { + // first invisible child is unconfigured + QListViewItem * c = l->childItem; + while( c ) { + c->configured = FALSE; + c = c->siblingItem; + } + } + l->configured = TRUE; + l->setup(); + l = (l == this) ? 0 : l->siblingItem; + if ( !l && !s.isEmpty() ) + l = s.pop(); + } + } + + QListView *lv = listView(); + + if ( open && lv) + enforceSortOrder(); + + if ( isVisible() && lv && lv->d && lv->d->drawables ) { + lv->d->drawables->clear(); + lv->buildDrawableList(); + } + + if ( lv && this != lv->d->r ) { + if ( o ) + emit lv->expanded( this ); + else + emit lv->collapsed( this ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( lv->viewport(), indexOfItem( this ), QAccessible::StateChanged ); +#endif + } +} + + +/*! + This virtual function is called before the first time QListView + needs to know the height or any other graphical attribute of this + object, and whenever the font, GUI style, or colors of the list + view change. + + The default calls widthChanged() and sets the item's height to the + height of a single line of text in the list view's font. (If you + use icons, multi-line text, etc., you will probably need to call + setHeight() yourself or reimplement it.) +*/ + +void QListViewItem::setup() +{ + widthChanged(); + QListView *lv = listView(); + + int ph = 0; + int h = 0; + if ( lv ) { + for ( uint i = 0; i < lv->d->column.size(); ++i ) { + if ( pixmap( i ) ) + ph = QMAX( ph, pixmap( i )->height() ); + } + + if ( mlenabled ) { + h = ph; + for ( int c = 0; c < lv->columns(); ++c ) { + int lines = text( c ).contains( QChar('\n') ) + 1; + int tmph = lv->d->fontMetricsHeight + + lv->fontMetrics().lineSpacing() * ( lines - 1 ); + h = QMAX( h, tmph ); + } + h += 2*lv->itemMargin(); + } else { + h = QMAX( lv->d->fontMetricsHeight, ph ) + 2*lv->itemMargin(); + } + } + + h = QMAX( h, QApplication::globalStrut().height()); + + if ( h % 2 > 0 ) + h++; + setHeight( h ); +} + + + + +/*! + This virtual function is called whenever the user presses the mouse + on this item or presses Space on it. + + \sa activatedPos() +*/ + +void QListViewItem::activate() +{ +} + + +/*! + When called from a reimplementation of activate(), this function + gives information on how the item was activated. Otherwise the + behavior is undefined. + + If activate() was caused by a mouse press, the function sets \a + pos to where the user clicked and returns TRUE; otherwise it + returns FALSE and does not change \a pos. + + \a pos is relative to the top-left corner of this item. + + \warning We recommend that you ignore this function; it is + scheduled to become obsolete. + + \sa activate() +*/ + +bool QListViewItem::activatedPos( QPoint &pos ) +{ + if ( activatedByClick ) + pos = activatedP; + return activatedByClick; +} + + +/*! + \fn bool QListViewItem::isSelectable() const + + Returns TRUE if the item is selectable (as it is by default); + otherwise returns FALSE + + \sa setSelectable() +*/ + + +/*! + Sets this item to be selectable if \a enable is TRUE (the + default) or not to be selectable if \a enable is FALSE. + + The user is not able to select a non-selectable item using either + the keyboard or the mouse. This also applies for the application + programmer (e.g. setSelected() respects this value). + + \sa isSelectable() +*/ + +void QListViewItem::setSelectable( bool enable ) +{ + selectable = enable; +} + + +/*! + \fn bool QListViewItem::isExpandable() const + + Returns TRUE if this item is expandable even when it has no + children; otherwise returns FALSE. +*/ + +/*! + Sets this item to be expandable even if it has no children if \a + enable is TRUE, and to be expandable only if it has children if \a + enable is FALSE (the default). + + The dirview example uses this in the canonical fashion. It checks + whether the directory is empty in setup() and calls + setExpandable(TRUE) if not; in setOpen() it reads the contents of + the directory and inserts items accordingly. This strategy means + that dirview can display the entire file system without reading + very much at startup. + + Note that root items are not expandable by the user unless + QListView::setRootIsDecorated() is set to TRUE. + + \sa setSelectable() +*/ + +void QListViewItem::setExpandable( bool enable ) +{ + expandable = enable; +} + + +/*! + Makes sure that this object's children are sorted appropriately. + + This only works if every item from the root item down to this item + is already sorted. + + \sa sortChildItems() +*/ + +void QListViewItem::enforceSortOrder() const +{ + QListView *lv = listView(); + if ( !lv || lv && (lv->d->clearing || lv->d->sortcolumn == Unsorted) ) + return; + if ( parentItem && + (parentItem->lsc != lsc || parentItem->lso != lso) ) + ((QListViewItem *)this)->sortChildItems( (int)parentItem->lsc, + (bool)parentItem->lso ); + else if ( !parentItem && + ( (int)lsc != lv->d->sortcolumn || (bool)lso != lv->d->ascending ) ) + ((QListViewItem *)this)->sortChildItems( lv->d->sortcolumn, lv->d->ascending ); +} + + +/*! + \fn bool QListViewItem::isSelected() const + + Returns TRUE if this item is selected; otherwise returns FALSE. + + \sa setSelected() QListView::setSelected() QListView::selectionChanged() +*/ + + +/*! + If \a s is TRUE this item is selected; otherwise it is deselected. + + This function does not maintain any invariants or repaint anything + -- QListView::setSelected() does that. + + \sa height() totalHeight() +*/ + +void QListViewItem::setSelected( bool s ) +{ + bool old = selected; + + QListView *lv = listView(); + if ( lv && lv->selectionMode() != QListView::NoSelection) { + if ( s && isSelectable() ) + selected = TRUE; + else + selected = FALSE; + +#if defined(QT_ACCESSIBILITY_SUPPORT) + if ( old != (bool)selected ) { + int ind = indexOfItem( this ); + QAccessible::updateAccessibility( lv->viewport(), ind, QAccessible::StateChanged ); + QAccessible::updateAccessibility( lv->viewport(), ind, selected ? QAccessible::SelectionAdd : QAccessible::SelectionRemove ); + } +#else + Q_UNUSED( old ); +#endif + } +} + +/*! + Returns the total height of this object, including any visible + children. This height is recomputed lazily and cached for as long + as possible. + + Functions which can affect the total height are, setHeight() which + is used to set an item's height, setOpen() to show or hide an + item's children, and invalidateHeight() to invalidate the cached + height. + + \sa height() +*/ + +int QListViewItem::totalHeight() const +{ + if ( !visible ) + return 0; + if ( maybeTotalHeight >= 0 ) + return maybeTotalHeight; + QListViewItem * that = (QListViewItem *)this; + if ( !that->configured ) { + that->configured = TRUE; + that->setup(); // ### virtual non-const function called in const + } + that->maybeTotalHeight = that->ownHeight; + + if ( !that->isOpen() || !that->childCount() ) + return that->ownHeight; + + QListViewItem * child = that->childItem; + while ( child != 0 ) { + that->maybeTotalHeight += child->totalHeight(); + child = child->siblingItem; + } + return that->maybeTotalHeight; +} + + +/*! + Returns the text in column \a column, or QString::null if there is + no text in that column. + + \sa key() paintCell() +*/ + +QString QListViewItem::text( int column ) const +{ + QListViewPrivate::ItemColumnInfo * l + = (QListViewPrivate::ItemColumnInfo*) columns; + + while( column && l ) { + l = l->next; + column--; + } + + return l ? l->text : QString::null; +} + + +/*! + Sets the text in column \a column to \a text, if \a column is a + valid column number and \a text is different from the existing + text. + + If \a text() has been reimplemented, this function may be a no-op. + + \sa text() key() +*/ + +void QListViewItem::setText( int column, const QString &text ) +{ + if ( column < 0 ) + return; + + QListViewPrivate::ItemColumnInfo * l + = (QListViewPrivate::ItemColumnInfo*) columns; + if ( !l ) { + l = new QListViewPrivate::ItemColumnInfo; + columns = (void*)l; + } + for( int c = 0; c < column; c++ ) { + if ( !l->next ) + l->next = new QListViewPrivate::ItemColumnInfo; + l = l->next; + } + if ( l->text == text ) + return; + + int oldLc = 0; + int newLc = 0; + if ( mlenabled ) { + if ( !l->text.isEmpty() ) + oldLc = l->text.contains( QChar( '\n' ) ) + 1; + if ( !text.isEmpty() ) + newLc = text.contains( QChar( '\n' ) ) + 1; + } + + l->dirty = TRUE; + l->text = text; + if ( column == (int)lsc ) + lsc = Unsorted; + + if ( mlenabled && oldLc != newLc ) + setup(); + else + widthChanged( column ); + + QListView * lv = listView(); + if ( lv ) { + lv->d->useDoubleBuffer = TRUE; + lv->triggerUpdate(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + if ( lv->isVisible() ) + QAccessible::updateAccessibility( lv->viewport(), indexOfItem(this), QAccessible::NameChanged ); +#endif + } +} + + +/*! + Sets the pixmap in column \a column to \a pm, if \a pm is non-null + and different from the current pixmap, and if \a column is + non-negative. + + \sa pixmap() setText() +*/ + +void QListViewItem::setPixmap( int column, const QPixmap & pm ) +{ + if ( column < 0 ) + return; + + int oldW = 0; + int oldH = 0; + if ( pixmap( column ) ) { + oldW = pixmap( column )->width(); + oldH = pixmap( column )->height(); + } + + QListViewPrivate::ItemColumnInfo * l + = (QListViewPrivate::ItemColumnInfo*) columns; + if ( !l ) { + l = new QListViewPrivate::ItemColumnInfo; + columns = (void*)l; + } + + for( int c = 0; c < column; c++ ) { + if ( !l->next ) + l->next = new QListViewPrivate::ItemColumnInfo; + l = l->next; + } + + if ( ( pm.isNull() && ( !l->pm || l->pm->isNull() ) ) || + ( l->pm && pm.serialNumber() == l->pm->serialNumber() ) ) + return; + + if ( pm.isNull() ) { + delete l->pm; + l->pm = 0; + } else { + if ( l->pm ) + *(l->pm) = pm; + else + l->pm = new QPixmap( pm ); + } + + int newW = 0; + int newH = 0; + if ( pixmap( column ) ) { + newW = pixmap( column )->width(); + newH = pixmap( column )->height(); + } + + if ( oldW != newW || oldH != newH ) { + setup(); + widthChanged( column ); + invalidateHeight(); + } + QListView *lv = listView(); + if ( lv ) { + lv->d->useDoubleBuffer = TRUE; + lv->triggerUpdate(); + } +} + + +/*! + Returns the pixmap for \a column, or 0 if there is no pixmap for + \a column. + + \sa setText() setPixmap() +*/ + +const QPixmap * QListViewItem::pixmap( int column ) const +{ + QListViewPrivate::ItemColumnInfo * l + = (QListViewPrivate::ItemColumnInfo*) columns; + + while( column && l ) { + l = l->next; + column--; + } + + return (l && l->pm) ? l->pm : 0; +} + + +/*! + This virtual function paints the contents of one column of an item + and aligns it as described by \a align. + + \a p is a QPainter open on the relevant paint device. \a p is + translated so (0, 0) is the top-left pixel in the cell and \a + width-1, height()-1 is the bottom-right pixel \e in the cell. The + other properties of \a p (pen, brush, etc) are undefined. \a cg is + the color group to use. \a column is the logical column number + within the item that is to be painted; 0 is the column which may + contain a tree. + + This function may use QListView::itemMargin() for readability + spacing on the left and right sides of data such as text, and + should honor isSelected() and QListView::allColumnsShowFocus(). + + If you reimplement this function, you should also reimplement + width(). + + The rectangle to be painted is in an undefined state when this + function is called, so you \e must draw on all the pixels. The + painter \a p has the right font on entry. + + \sa paintBranches(), QListView::drawContentsOffset() +*/ + +void QListViewItem::paintCell( QPainter * p, const QColorGroup & cg, + int column, int width, int align ) +{ + // Change width() if you change this. + + if ( !p ) + return; + + QListView *lv = listView(); + if ( !lv ) + return; + QFontMetrics fm( p->fontMetrics() ); + + // had, but we _need_ the column info for the ellipsis thingy!!! + if ( !columns ) { + for ( uint i = 0; i < lv->d->column.size(); ++i ) { + setText( i, text( i ) ); + } + } + + QString t = text( column ); + + if ( columns ) { + QListViewPrivate::ItemColumnInfo *ci = 0; + // try until we have a column info.... + while ( !ci ) { + ci = (QListViewPrivate::ItemColumnInfo*)columns; + for ( int i = 0; ci && (i < column); ++i ) + ci = ci->next; + + if ( !ci ) { + setText( column, t ); + ci = 0; + } + } + + // if the column width changed and this item was not painted since this change + if ( ci && ( ci->width != width || ci->text != t || ci->dirty ) ) { + ci->text = t; + ci->dirty = FALSE; + ci->width = width; + ci->truncated = FALSE; + // if we have to do the ellipsis thingy calc the truncated text + int pw = lv->itemMargin()*2 - lv->d->minLeftBearing - lv->d->minRightBearing; + pw += pixmap( column ) ? pixmap( column )->width() + lv->itemMargin() : 0; + if ( !mlenabled && fm.width( t ) + pw > width ) { + // take care of arabic shaping in width calculation (lars) + ci->truncated = TRUE; + ci->tmpText = qEllipsisText( t, fm, width - pw, align ); + } else if ( mlenabled && fm.width( t ) + pw > width ) { +#ifndef QT_NO_STRINGLIST + QStringList list = QStringList::split( QChar('\n'), t, TRUE ); + for ( QStringList::Iterator it = list.begin(); it != list.end(); ++it ) { + QString z = *it; + if ( fm.width( z ) + pw > width ) { + ci->truncated = TRUE; + *it = qEllipsisText( z, fm, width - pw, align ); + } + } + + if ( ci->truncated ) + ci->tmpText = list.join( QString("\n") ); +#endif + } + } + + // if we have to draw the ellipsis thingy, use the truncated text + if ( ci && ci->truncated ) + t = ci->tmpText; + } + + int marg = lv->itemMargin(); + int r = marg; + const QPixmap * icon = pixmap( column ); + + const BackgroundMode bgmode = lv->viewport()->backgroundMode(); + const QColorGroup::ColorRole crole = QPalette::backgroundRoleFromMode( bgmode ); + if ( cg.brush( crole ) != lv->colorGroup().brush( crole ) ) + p->fillRect( 0, 0, width, height(), cg.brush( crole ) ); + else + lv->paintEmptyArea( p, QRect( 0, 0, width, height() ) ); + + + // (lars) what does this do??? +#if 0 // RS: #### + if ( align != AlignLeft ) + marg -= lv->d->minRightBearing; +#endif + if ( isSelected() && + (column == 0 || lv->allColumnsShowFocus()) ) { + p->fillRect( r - marg, 0, width - r + marg, height(), + cg.brush( QColorGroup::Highlight ) ); + if ( enabled || !lv ) + p->setPen( cg.highlightedText() ); + else if ( !enabled && lv) + p->setPen( lv->palette().disabled().highlightedText() ); + } else { + if ( enabled || !lv ) + p->setPen( cg.text() ); + else if ( !enabled && lv) + p->setPen( lv->palette().disabled().text() ); + } + + +#if 0 + bool reverse = QApplication::reverseLayout(); +#else + bool reverse = FALSE; +#endif + int iconWidth = 0; + + if ( icon ) { + iconWidth = icon->width() + lv->itemMargin(); + int xo = r; + // we default to AlignVCenter. + int yo = ( height() - icon->height() ) / 2; + + // I guess we may as well always respect vertical alignment. + if ( align & AlignBottom ) + yo = height() - icon->height(); + else if ( align & AlignTop ) + yo = 0; + + // respect horizontal alignment when there is no text for an item. + if ( text(column).isEmpty() ) { + if ( align & AlignRight ) + xo = width - 2 * marg - iconWidth; + else if ( align & AlignHCenter ) + xo = ( width - iconWidth ) / 2; + } + if ( reverse ) + xo = width - 2 * marg - iconWidth; + p->drawPixmap( xo, yo, *icon ); + } + + if ( !t.isEmpty() ) { + if ( !mlenabled ) { + if ( !(align & AlignTop || align & AlignBottom) ) + align |= AlignVCenter; + } else { + if ( !(align & AlignVCenter || align & AlignBottom) ) + align |= AlignTop; + } + if ( !reverse ) + r += iconWidth; + + if ( !mlenabled ) { + p->drawText( r, 0, width-marg-r, height(), align, t ); + } else { + p->drawText( r, marg, width-marg-r, height(), align, t ); + } + } + + if ( mlenabled && column == 0 && isOpen() && childCount() ) { + int textheight = fm.size( align, t ).height() + 2 * lv->itemMargin(); + textheight = QMAX( textheight, QApplication::globalStrut().height() ); + if ( textheight % 2 > 0 ) + textheight++; + if ( textheight < height() ) { + int w = lv->treeStepSize() / 2; + lv->style().drawComplexControl( QStyle::CC_ListView, p, lv, + QRect( 0, textheight, w + 1, height() - textheight + 1 ), cg, + lv->isEnabled() ? QStyle::Style_Enabled : QStyle::Style_Default, + QStyle::SC_ListViewExpand, + (uint)QStyle::SC_All, QStyleOption( this ) ); + } + } +} + +/*! + Returns the number of pixels of width required to draw column \a c + of list view \a lv, using the metrics \a fm without cropping. The + list view containing this item may use this information depending + on the QListView::WidthMode settings for the column. + + The default implementation returns the width of the bounding + rectangle of the text of column \a c. + + \sa listView() widthChanged() QListView::setColumnWidthMode() + QListView::itemMargin() +*/ +int QListViewItem::width( const QFontMetrics& fm, + const QListView* lv, int c ) const +{ + int w; + if ( mlenabled ) + w = fm.size( AlignVCenter, text( c ) ).width() + lv->itemMargin() * 2 + - lv->d->minLeftBearing - lv->d->minRightBearing; + else + w = fm.width( text( c ) ) + lv->itemMargin() * 2 + - lv->d->minLeftBearing - lv->d->minRightBearing; + const QPixmap * pm = pixmap( c ); + if ( pm ) + w += pm->width() + lv->itemMargin(); // ### correct margin stuff? + return QMAX( w, QApplication::globalStrut().width() ); +} + + +/*! + Paints a focus indicator on the rectangle \a r using painter \a p + and colors \a cg. + + \a p is already clipped. + + \sa paintCell() paintBranches() QListView::setAllColumnsShowFocus() +*/ + +void QListViewItem::paintFocus( QPainter *p, const QColorGroup &cg, + const QRect & r ) +{ + QListView *lv = listView(); + if ( lv ) + lv->style().drawPrimitive( QStyle::PE_FocusRect, p, r, cg, + (isSelected() ? + QStyle::Style_FocusAtBorder : + QStyle::Style_Default), + QStyleOption(isSelected() ? cg.highlight() : cg.base() )); +} + + +/*! + Paints a set of branches from this item to (some of) its children. + + Painter \a p is set up with clipping and translation so that you + can only draw in the rectangle that needs redrawing; \a cg is the + color group to use; the update rectangle is at (0, 0) and has size + width \a w by height \a h. The top of the rectangle you own is at + \a y (which is never greater than 0 but can be outside the window + system's allowed coordinate range). + + The update rectangle is in an undefined state when this function + is called; this function must draw on \e all of the pixels. + + \sa paintCell(), QListView::drawContentsOffset() +*/ + +void QListViewItem::paintBranches( QPainter * p, const QColorGroup & cg, + int w, int y, int h ) +{ + QListView *lv = listView(); + if ( lv ) + lv->paintEmptyArea( p, QRect( 0, 0, w, h ) ); + if ( !visible || !lv ) + return; + lv->style().drawComplexControl( QStyle::CC_ListView, p, lv, + QRect( 0, y, w, h ), + cg, + lv->isEnabled() ? QStyle::Style_Enabled : + QStyle::Style_Default, + (QStyle::SC_ListViewBranch | + QStyle::SC_ListViewExpand), + QStyle::SC_None, QStyleOption(this) ); +} + + +QListViewPrivate::Root::Root( QListView * parent ) + : QListViewItem( parent ) +{ + lv = parent; + setHeight( 0 ); + setOpen( TRUE ); +} + + +void QListViewPrivate::Root::setHeight( int ) +{ + QListViewItem::setHeight( 0 ); +} + + +void QListViewPrivate::Root::invalidateHeight() +{ + QListViewItem::invalidateHeight(); + lv->triggerUpdate(); +} + + +QListView * QListViewPrivate::Root::theListView() const +{ + return lv; +} + + +void QListViewPrivate::Root::setup() +{ + // explicitly nothing +} + + + +/*! +\internal +If called after a mouse click, tells the list view to ignore a +following double click. This state is reset after the next mouse click. +*/ + +void QListViewItem::ignoreDoubleClick() +{ + QListView *lv = listView(); + if ( lv ) + lv->d->ignoreDoubleClick = TRUE; +} + + + +/*! + \fn void QListView::onItem( QListViewItem *i ) + + This signal is emitted when the user moves the mouse cursor onto + item \a i, similar to the QWidget::enterEvent() function. +*/ + +// ### bug here too? see qiconview.cppp onItem/onViewport + +/*! + \fn void QListView::onViewport() + + This signal is emitted when the user moves the mouse cursor from + an item to an empty part of the list view. +*/ + +/*! + \enum QListView::SelectionMode + + This enumerated type is used by QListView to indicate how it + reacts to selection by the user. + + \value Single When the user selects an item, any already-selected + item becomes unselected. The user can unselect the selected + item by clicking on some empty space within the view. + + \value Multi When the user selects an item in the usual way, the + selection status of that item is toggled and the other items are + left alone. Also, multiple items can be selected by dragging the + mouse while the left mouse button stays pressed. + + \value Extended When the user selects an item in the usual way, + the selection is cleared and the new item selected. However, if + the user presses the Ctrl key when clicking on an item, the + clicked item gets toggled and all other items are left untouched. + And if the user presses the Shift key while clicking on an item, + all items between the current item and the clicked item get + selected or unselected, depending on the state of the clicked + item. Also, multiple items can be selected by dragging the mouse + over them. + + \value NoSelection Items cannot be selected. + + In other words, \c Single is a real single-selection list view, \c + Multi a real multi-selection list view, \c Extended is a list view + where users can select multiple items but usually want to select + either just one or a range of contiguous items, and \c NoSelection + is a list view where the user can look but not touch. +*/ + +/*! + \enum QListView::ResizeMode + + This enum describes how the list view's header adjusts to resize + events which affect the width of the list view. + + \value NoColumn The columns do not get resized in resize events. + + \value AllColumns All columns are resized equally to fit the width + of the list view. + + \value LastColumn The last column is resized to fit the width of + the list view. +*/ + +/*! + \enum QListView::RenameAction + + This enum describes whether a rename operation is accepted if the + rename editor loses focus without the user pressing Enter. + + \value Accept Rename if Enter is pressed or focus is lost. + + \value Reject Discard the rename operation if focus is lost (and + Enter has not been pressed). +*/ + +/*! + \class QListView + \brief The QListView class implements a list/tree view. + + \ingroup advanced + \mainclass + + It can display and control a hierarchy of multi-column items, and + provides the ability to add new items at any time. The user may + select one or many items (depending on the \c SelectionMode) and + sort the list in increasing or decreasing order by any column. + + The simplest pattern of use is to create a QListView, add some + column headers using addColumn() and create one or more + QListViewItem or QCheckListItem objects with the QListView as + parent: + + \quotefile xml/tagreader-with-features/structureparser.h + \skipto QListView * table + \printline + \quotefile xml/tagreader-with-features/structureparser.cpp + \skipto addColumn + \printline addColumn + \printline + \skipto new QListViewItem( table + \printline + + Further nodes can be added to the list view object (the root of the + tree) or as child nodes to QListViewItems: + + \skipto for ( int i = 0 ; i < attributes.length(); + \printuntil } + + (From \link xml/tagreader-with-features/structureparser.cpp + xml/tagreader-with-features/structureparser.cpp\endlink) + + The main setup functions are: + \table + \header \i Function \i Action + \row \i \l addColumn() + \i Adds a column with a text label and perhaps width. Columns + are counted from the left starting with column 0. + \row \i \l setColumnWidthMode() + \i Sets the column to be resized automatically or not. + \row \i \l setAllColumnsShowFocus() + \i Sets whether items should show keyboard focus using all + columns or just column 0. The default is to show focus + just using column 0. + \row \i \l setRootIsDecorated() + \i Sets whether root items should show open/close decoration to their left. + The default is FALSE. + \row \i \l setTreeStepSize() + \i Sets how many pixels an item's children are indented + relative to their parent. The default is 20. This is + mostly a matter of taste. + \row \i \l setSorting() + \i Sets whether the items should be sorted, whether it should + be in ascending or descending order, and by what column + they should be sorted. By default the list view is sorted + by the first column; to switch this off call setSorting(-1). + \endtable + + To handle events such as mouse presses on the list view, derived + classes can reimplement the QScrollView functions: + \link QScrollView::contentsMousePressEvent() contentsMousePressEvent\endlink, + \link QScrollView::contentsMouseReleaseEvent() contentsMouseReleaseEvent\endlink, + \link QScrollView::contentsMouseDoubleClickEvent() contentsMouseDoubleClickEvent\endlink, + \link QScrollView::contentsMouseMoveEvent() contentsMouseMoveEvent\endlink, + \link QScrollView::contentsDragEnterEvent() contentsDragEnterEvent\endlink, + \link QScrollView::contentsDragMoveEvent() contentsDragMoveEvent\endlink, + \link QScrollView::contentsDragLeaveEvent() contentsDragLeaveEvent\endlink, + \link QScrollView::contentsDropEvent() contentsDropEvent\endlink, and + \link QScrollView::contentsWheelEvent() contentsWheelEvent\endlink. + + There are also several functions for mapping between items and + coordinates. itemAt() returns the item at a position on-screen, + itemRect() returns the rectangle an item occupies on the screen, + and itemPos() returns the position of any item (whether it is + on-screen or not). firstChild() returns the list view's first item + (not necessarily visible on-screen). + + You can iterate over visible items using + QListViewItem::itemBelow(); over a list view's top-level items + using QListViewItem::firstChild() and + QListViewItem::nextSibling(); or every item using a + QListViewItemIterator. See + the QListViewItem documentation for examples of traversal. + + An item can be moved amongst its siblings using + QListViewItem::moveItem(). To move an item in the hierarchy use + takeItem() and insertItem(). Item's (and all their child items) + are deleted with \c delete; to delete all the list view's items + use clear(). + + There are a variety of selection modes described in the + QListView::SelectionMode documentation. The default is \c Single + selection, which you can change using setSelectionMode(). + + Because QListView offers multiple selection it must display + keyboard focus and selection state separately. Therefore there are + functions both to set the selection state of an item + (setSelected()) and to set which item displays keyboard focus + (setCurrentItem()). + + QListView emits two groups of signals; one group signals changes + in selection/focus state and one indicates selection. The first + group consists of selectionChanged() (applicable to all list + views), selectionChanged(QListViewItem*) (applicable only to a + \c Single selection list view), and currentChanged(QListViewItem*). + The second group consists of doubleClicked(QListViewItem*), + returnPressed(QListViewItem*), + rightButtonClicked(QListViewItem*, const QPoint&, int), etc. + + Note that changing the state of the list view in a slot connected + to a list view signal may cause unexpected side effects. If you + need to change the list view's state in response to a signal, use + a \link QTimer::singleShot() single shot timer\endlink with a + time out of 0, and connect this timer to a slot that modifies the + list view's state. + + In Motif style, QListView deviates fairly strongly from the look + and feel of the Motif hierarchical tree view. This is done mostly + to provide a usable keyboard interface and to make the list view + look better with a white background. + + If selectionMode() is \c Single (the default) the user can select + one item at a time, e.g. by clicking an item with the mouse, see + \l QListView::SelectionMode for details. + + The list view can be navigated either using the mouse or the + keyboard. Clicking a <b>-</b> icon closes an item (hides its + children) and clicking a <b>+</b> icon opens an item (shows its + children). The keyboard controls are these: + \table + \header \i Keypress \i Action + \row \i Home + \i Make the first item current and visible. + \row \i End + \i Make the last item current and visible. + \row \i Page Up + \i Make the item above the top visible item current and visible. + \row \i Page Down + \i Make the item below the bottom visible item current and visible. + \row \i Up Arrow + \i Make the item above the current item current and visible. + \row \i Down Arrow + \i Make the item below the current item current and visible. + \row \i Left Arrow + \i If the current item is closed (<b>+</b> icon) or has no + children, make its parent item current and visible. If the + current item is open (<b>-</b> icon) close it, i.e. hide its + children. Exception: if the current item is the first item + and is closed and the horizontal scrollbar is offset to + the right the list view will be scrolled left. + \row \i Right Arrow + \i If the current item is closed (<b>+</b> icon) and has + children, the item is opened. If the current item is + opened (<b>-</b> icon) and has children the item's first + child is made current and visible. If the current item has + no children the list view is scrolled right. + \endtable + + If the user starts typing letters with the focus in the list view + an incremental search will occur. For example if the user types + 'd' the current item will change to the first item that begins + with the letter 'd'; if they then type 'a', the current item will + change to the first item that begins with 'da', and so on. If no + item begins with the letters they type the current item doesn't + change. + + \warning The list view assumes ownership of all list view items + and will delete them when it does not need them any more. + + <img src=qlistview-m.png> <img src=qlistview-w.png> + + \sa QListViewItem QCheckListItem +*/ + +/*! + \fn void QListView::itemRenamed( QListViewItem * item, int col ) + + \overload + + This signal is emitted when \a item has been renamed, e.g. by + in-place renaming, in column \a col. + + \sa QListViewItem::setRenameEnabled() +*/ + +/*! + \fn void QListView::itemRenamed( QListViewItem * item, int col, const QString &text) + + This signal is emitted when \a item has been renamed to \a text, + e.g. by in in-place renaming, in column \a col. + + \sa QListViewItem::setRenameEnabled() +*/ + +/*! + Constructs a new empty list view called \a name with parent \a + parent. + + Performance is boosted by modifying the widget flags \a f so that + only part of the QListViewItem children is redrawn. This may be + unsuitable for custom QListViewItem classes, in which case \c + WStaticContents and \c WNoAutoErase should be cleared. + + \sa QWidget::clearWFlags() Qt::WidgetFlags +*/ +QListView::QListView( QWidget * parent, const char *name, WFlags f ) + : QScrollView( parent, name, f | WStaticContents | WNoAutoErase ) +{ + init(); +} + +void QListView::init() +{ + d = new QListViewPrivate; + d->vci = 0; + d->timer = new QTimer( this ); + d->levelWidth = 20; + d->r = 0; + d->rootIsExpandable = 0; + d->h = new QHeader( this, "list view header" ); + d->h->installEventFilter( this ); + d->focusItem = 0; + d->oldFocusItem = 0; + d->drawables = 0; + d->dirtyItems = 0; + d->dirtyItemTimer = new QTimer( this ); + d->visibleTimer = new QTimer( this ); + d->renameTimer = new QTimer( this ); + d->autoopenTimer = new QTimer( this ); + d->margin = 1; + d->selectionMode = QListView::Single; + d->sortcolumn = 0; + d->ascending = TRUE; + d->allColumnsShowFocus = FALSE; + d->fontMetricsHeight = fontMetrics().height(); + d->h->setTracking(TRUE); + d->buttonDown = FALSE; + d->ignoreDoubleClick = FALSE; + d->column.setAutoDelete( TRUE ); + d->iterators = 0; + d->scrollTimer = 0; + d->sortIndicator = FALSE; + d->clearing = FALSE; + d->minLeftBearing = fontMetrics().minLeftBearing(); + d->minRightBearing = fontMetrics().minRightBearing(); + d->ellipsisWidth = fontMetrics().width( "..." ) * 2; + d->highlighted = 0; + d->pressedItem = 0; + d->selectAnchor = 0; + d->select = TRUE; + d->useDoubleBuffer = FALSE; + d->startDragItem = 0; + d->toolTips = TRUE; +#ifndef QT_NO_TOOLTIP + d->toolTip = new QListViewToolTip( viewport(), this ); +#endif + d->updateHeader = FALSE; + d->fullRepaintOnComlumnChange = FALSE; + d->resizeMode = NoColumn; + d->defRenameAction = Reject; + d->pressedEmptyArea = FALSE; + d->startEdit = TRUE; + d->ignoreEditAfterFocus = FALSE; + d->inMenuMode = FALSE; + d->pressedSelected = FALSE; + + setMouseTracking( TRUE ); + viewport()->setMouseTracking( TRUE ); + + connect( d->timer, SIGNAL(timeout()), + this, SLOT(updateContents()) ); + connect( d->dirtyItemTimer, SIGNAL(timeout()), + this, SLOT(updateDirtyItems()) ); + connect( d->visibleTimer, SIGNAL(timeout()), + this, SLOT(makeVisible()) ); + connect( d->renameTimer, SIGNAL(timeout()), + this, SLOT(startRename()) ); + connect( d->autoopenTimer, SIGNAL(timeout()), + this, SLOT(openFocusItem()) ); + + connect( d->h, SIGNAL(sizeChange(int,int,int)), + this, SLOT(handleSizeChange(int,int,int)) ); + connect( d->h, SIGNAL(indexChange(int,int,int)), + this, SLOT(handleIndexChange()) ); + connect( d->h, SIGNAL(sectionClicked(int)), + this, SLOT(changeSortColumn(int)) ); + connect( d->h, SIGNAL(sectionHandleDoubleClicked(int)), + this, SLOT(adjustColumn(int)) ); + connect( horizontalScrollBar(), SIGNAL(sliderMoved(int)), + d->h, SLOT(setOffset(int)) ); + connect( horizontalScrollBar(), SIGNAL(valueChanged(int)), + d->h, SLOT(setOffset(int)) ); + + // will access d->r + QListViewPrivate::Root * r = new QListViewPrivate::Root( this ); + r->is_root = TRUE; + d->r = r; + d->r->setSelectable( FALSE ); + + viewport()->setFocusProxy( this ); + viewport()->setFocusPolicy( WheelFocus ); + viewport()->setBackgroundMode( PaletteBase ); + setBackgroundMode( PaletteBackground, PaletteBase ); +} + +/*! + \property QListView::showSortIndicator + \brief whether the list view header should display a sort indicator. + + If this property is TRUE, an arrow is drawn in the header of the + list view to indicate the sort order of the list view contents. + The arrow will be drawn in the correct column and will point up or + down, depending on the current sort direction. The default is + FALSE (don't show an indicator). + + \sa QHeader::setSortIndicator() +*/ + +void QListView::setShowSortIndicator( bool show ) +{ + if ( show == d->sortIndicator ) + return; + + d->sortIndicator = show; + if ( d->sortcolumn != Unsorted && d->sortIndicator ) + d->h->setSortIndicator( d->sortcolumn, d->ascending ); + else + d->h->setSortIndicator( -1 ); +} + +bool QListView::showSortIndicator() const +{ + return d->sortIndicator; +} + +/*! + \property QListView::showToolTips + \brief whether this list view should show tooltips for truncated column texts + + The default is TRUE. +*/ + +void QListView::setShowToolTips( bool b ) +{ + d->toolTips = b; +} + +bool QListView::showToolTips() const +{ + return d->toolTips; +} + +/*! + \property QListView::resizeMode + \brief whether all, none or the only the last column should be resized + + Specifies whether all, none or only the last column should be + resized to fit the full width of the list view. The values for this + property can be one of: \c NoColumn (the default), \c AllColumns + or \c LastColumn. + + \warning Setting the resize mode should be done after all necessary + columns have been added to the list view, otherwise the behavior is + undefined. + + \sa QHeader, header() +*/ + +void QListView::setResizeMode( ResizeMode m ) +{ + d->resizeMode = m; + if ( m == NoColumn ) + header()->setStretchEnabled( FALSE ); + else if ( m == AllColumns ) + header()->setStretchEnabled( TRUE ); + else + header()->setStretchEnabled( TRUE, header()->count() - 1 ); +} + +QListView::ResizeMode QListView::resizeMode() const +{ + return d->resizeMode; +} + +/*! + Destroys the list view, deleting all its items, and frees up all + allocated resources. +*/ + +QListView::~QListView() +{ + if ( d->iterators ) { + QListViewItemIterator *i = d->iterators->first(); + while ( i ) { + i->listView = 0; + i = d->iterators->next(); + } + delete d->iterators; + d->iterators = 0; + } + + d->focusItem = 0; + delete d->r; + d->r = 0; + delete d->dirtyItems; + d->dirtyItems = 0; + delete d->drawables; + d->drawables = 0; + delete d->vci; + d->vci = 0; +#ifndef QT_NO_TOOLTIP + delete d->toolTip; + d->toolTip = 0; +#endif + delete d; + d = 0; +} + + +/*! + Calls QListViewItem::paintCell() and + QListViewItem::paintBranches() as necessary for all list view + items that require repainting in the \a cw pixels wide and \a ch + pixels high bounding rectangle starting at position \a cx, \a cy + with offset \a ox, \a oy. Uses the painter \a p. +*/ + +void QListView::drawContentsOffset( QPainter * p, int ox, int oy, + int cx, int cy, int cw, int ch ) +{ + if ( columns() == 0 ) { + paintEmptyArea( p, QRect( cx, cy, cw, ch ) ); + return; + } + + if ( !d->drawables || + d->drawables->isEmpty() || + d->topPixel > cy || + d->bottomPixel < cy + ch - 1 || + d->r->maybeTotalHeight < 0 ) + buildDrawableList(); + + if ( d->dirtyItems ) { + QRect br( cx - ox, cy - oy, cw, ch ); + QPtrDictIterator<void> it( *(d->dirtyItems) ); + QListViewItem * i; + while( (i = (QListViewItem *)(it.currentKey())) != 0 ) { + ++it; + QRect ir = itemRect( i ).intersect( viewport()->rect() ); + if ( ir.isEmpty() || br.contains( ir ) ) + // we're painting this one, or it needs no painting: forget it + d->dirtyItems->remove( (void *)i ); + } + if ( d->dirtyItems->count() ) { + // there are still items left that need repainting + d->dirtyItemTimer->start( 0, TRUE ); + } else { + // we're painting all items that need to be painted + delete d->dirtyItems; + d->dirtyItems = 0; + d->dirtyItemTimer->stop(); + } + } + + p->setFont( font() ); + + QPtrListIterator<QListViewPrivate::DrawableItem> it( *(d->drawables) ); + + QRect r; + int fx = -1, x, fc = 0, lc = 0; + int tx = -1; + QListViewPrivate::DrawableItem * current; + + while ( (current = it.current()) != 0 ) { + ++it; + if ( !current->i->isVisible() ) + continue; + int ih = current->i->height(); + int ith = current->i->totalHeight(); + int c; + int cs; + + // need to paint current? + if ( ih > 0 && current->y < cy+ch && current->y+ih > cy ) { + if ( fx < 0 ) { + // find first interesting column, once + x = 0; + c = 0; + cs = d->h->cellSize( 0 ); + while ( x + cs <= cx && c < d->h->count() ) { + x += cs; + c++; + if ( c < d->h->count() ) + cs = d->h->cellSize( c ); + } + fx = x; + fc = c; + while( x < cx + cw && c < d->h->count() ) { + x += cs; + c++; + if ( c < d->h->count() ) + cs = d->h->cellSize( c ); + } + lc = c; + } + + x = fx; + c = fc; + // draw to last interesting column + + bool drawActiveSelection = hasFocus() || d->inMenuMode || + !style().styleHint( QStyle::SH_ItemView_ChangeHighlightOnFocus, this ) || + ( currentItem() && currentItem()->renameBox && currentItem()->renameBox->hasFocus() ); + const QColorGroup &cg = ( drawActiveSelection ? colorGroup() : palette().inactive() ); + + while ( c < lc && d->drawables ) { + int i = d->h->mapToLogical( c ); + cs = d->h->cellSize( c ); + r.setRect( x - ox, current->y - oy, cs, ih ); + if ( i == 0 && current->i->parentItem ) + r.setLeft( r.left() + current->l * treeStepSize() ); + + p->save(); + // No need to paint if the cell isn't technically visible + if ( !( r.width() == 0 || r.height() == 0 ) ) { + p->translate( r.left(), r.top() ); + int ac = d->h->mapToLogical( c ); + // map to Left currently. This should change once we + // can really reverse the listview. + int align = columnAlignment( ac ); + if ( align == AlignAuto ) align = AlignLeft; + if ( d->useDoubleBuffer ) { + QRect a( 0, 0, r.width(), current->i->height() ); + QSharedDoubleBuffer buffer( p, a, QSharedDoubleBuffer::Force ); + if ( buffer.isBuffered() ) + paintEmptyArea( buffer.painter(), a ); + buffer.painter()->setFont( p->font() ); + buffer.painter()->setPen( p->pen() ); + buffer.painter()->setBrush( p->brush() ); + buffer.painter()->setBrushOrigin( -r.left(), -r.top() ); + current->i->paintCell( buffer.painter(), cg, ac, r.width(), + align ); + } else { + current->i->paintCell( p, cg, ac, r.width(), + align ); + } + } + p->restore(); + x += cs; + c++; + } + + if ( current->i == d->focusItem && hasFocus() && + !d->allColumnsShowFocus ) { + p->save(); + int cell = d->h->mapToActual( 0 ); + QRect r( d->h->cellPos( cell ) - ox, current->y - oy, d->h->cellSize( cell ), ih ); + if ( current->i->parentItem ) + r.setLeft( r.left() + current->l * treeStepSize() ); + if ( r.left() < r.right() ) + current->i->paintFocus( p, colorGroup(), r ); + p->restore(); + } + } + + const int cell = d->h->mapToActual( 0 ); + + // does current need focus indication? + if ( current->i == d->focusItem && hasFocus() && + d->allColumnsShowFocus ) { + p->save(); + int x = -contentsX(); + int w = header()->cellPos( header()->count() - 1 ) + + header()->cellSize( header()->count() - 1 ); + + r.setRect( x, current->y - oy, w, ih ); + if ( d->h->mapToActual( 0 ) == 0 || ( current->l == 0 && !rootIsDecorated() ) ) { + int offsetx = QMIN( current->l * treeStepSize(), d->h->cellSize( cell ) ); + r.setLeft( r.left() + offsetx ); + current->i->paintFocus( p, colorGroup(), r ); + } else { + int xdepth = QMIN( treeStepSize() * ( current->i->depth() + ( rootIsDecorated() ? 1 : 0) ) + + itemMargin(), d->h->cellSize( cell ) ); + xdepth += d->h->cellPos( cell ); + QRect r1( r ); + r1.setRight( d->h->cellPos( cell ) - 1 ); + QRect r2( r ); + r2.setLeft( xdepth - 1 ); + current->i->paintFocus( p, colorGroup(), r1 ); + current->i->paintFocus( p, colorGroup(), r2 ); + } + p->restore(); + } + + if ( tx < 0 ) + tx = d->h->cellPos( cell ); + + // do any children of current need to be painted? + if ( ih != ith && + (current->i != d->r || d->rootIsExpandable) && + current->y + ith > cy && + current->y + ih < cy + ch && + tx + current->l * treeStepSize() < cx + cw && + tx + (current->l+1) * treeStepSize() > cx ) { + // compute the clip rectangle the safe way + + int rtop = current->y + ih; + int rbottom = current->y + ith; + int rleft = tx + current->l*treeStepSize(); + int rright = rleft + treeStepSize(); + + int crtop = QMAX( rtop, cy ); + int crbottom = QMIN( rbottom, cy+ch ); + int crleft = QMAX( rleft, cx ); + int crright = QMIN( rright, cx+cw ); + + r.setRect( crleft-ox, crtop-oy, + crright-crleft, crbottom-crtop ); + + if ( r.isValid() ) { + p->save(); + p->translate( rleft-ox, crtop-oy ); + current->i->paintBranches( p, colorGroup(), treeStepSize(), + rtop - crtop, r.height() ); + p->restore(); + } + } + } + + if ( d->r->totalHeight() < cy + ch ) + paintEmptyArea( p, QRect( cx - ox, d->r->totalHeight() - oy, + cw, cy + ch - d->r->totalHeight() ) ); + + int c = d->h->count()-1; + if ( c >= 0 && + d->h->cellPos( c ) + d->h->cellSize( c ) < cx + cw ) { + c = d->h->cellPos( c ) + d->h->cellSize( c ); + paintEmptyArea( p, QRect( c - ox, cy - oy, cx + cw - c, ch ) ); + } +} + + + +/*! + Paints \a rect so that it looks like empty background using + painter \a p. \a rect is in widget coordinates, ready to be fed to + \a p. + + The default function fills \a rect with the + viewport()->backgroundBrush(). +*/ + +void QListView::paintEmptyArea( QPainter * p, const QRect & rect ) +{ + QStyleOption opt( d->sortcolumn, 0 ); // ### hack; in 3.1, add a property in QListView and QHeader + QStyle::SFlags how = QStyle::Style_Default; + if ( isEnabled() ) + how |= QStyle::Style_Enabled; + + style().drawComplexControl( QStyle::CC_ListView, + p, this, rect, colorGroup(), + how, QStyle::SC_ListView, QStyle::SC_None, + opt ); +} + + +/* + Rebuilds the list of drawable QListViewItems. This function is + const so that const functions can call it without requiring + d->drawables to be mutable. +*/ + +void QListView::buildDrawableList() const +{ + d->r->enforceSortOrder(); + + QPtrStack<QListViewPrivate::Pending> stack; + stack.push( new QListViewPrivate::Pending( ((int)d->rootIsExpandable)-1, + 0, d->r ) ); + + // could mess with cy and ch in order to speed up vertical + // scrolling + int cy = contentsY(); + int ch = ((QListView *)this)->visibleHeight(); + d->topPixel = cy + ch; // one below bottom + d->bottomPixel = cy - 1; // one above top + + QListViewPrivate::Pending * cur; + + // used to work around lack of support for mutable + QPtrList<QListViewPrivate::DrawableItem> * dl; + + dl = new QPtrList<QListViewPrivate::DrawableItem>; + dl->setAutoDelete( TRUE ); + if ( d->drawables ) + delete ((QListView *)this)->d->drawables; + ((QListView *)this)->d->drawables = dl; + + while ( !stack.isEmpty() ) { + cur = stack.pop(); + + int ih = cur->i->height(); + int ith = cur->i->totalHeight(); + + // if this is not true, buildDrawableList has been called recursivly + Q_ASSERT( dl == d->drawables ); + + // is this item, or its branch symbol, inside the viewport? + if ( cur->y + ith >= cy && cur->y < cy + ch ) { + dl->append( new QListViewPrivate::DrawableItem(cur)); + // perhaps adjust topPixel up to this item? may be adjusted + // down again if any children are not to be painted + if ( cur->y < d->topPixel ) + d->topPixel = cur->y; + // bottompixel is easy: the bottom item drawn contains it + d->bottomPixel = cur->y + ih - 1; + } + + // push younger sibling of cur on the stack? + if ( cur->y + ith < cy+ch && cur->i->siblingItem ) + stack.push( new QListViewPrivate::Pending(cur->l, + cur->y + ith, + cur->i->siblingItem)); + + // do any children of cur need to be painted? + if ( cur->i->isOpen() && cur->i->childCount() && + cur->y + ith > cy && + cur->y + ih < cy + ch ) { + cur->i->enforceSortOrder(); + + QListViewItem * c = cur->i->childItem; + int y = cur->y + ih; + + // if any of the children are not to be painted, skip them + // and invalidate topPixel + while ( c && y + c->totalHeight() <= cy ) { + y += c->totalHeight(); + c = c->siblingItem; + d->topPixel = cy + ch; + } + + // push one child on the stack, if there is at least one + // needing to be painted + if ( c && y < cy+ch ) + stack.push( new QListViewPrivate::Pending( cur->l + 1, + y, c ) ); + } + + delete cur; + } +} + +/*! + \property QListView::treeStepSize + \brief the number of pixels a child is offset from its parent + + The default is 20 pixels. + + Of course, this property is only meaningful for hierarchical list + views. +*/ + +int QListView::treeStepSize() const +{ + return d->levelWidth; +} + +void QListView::setTreeStepSize( int size ) +{ + if ( size != d->levelWidth ) { + d->levelWidth = size; + viewport()->repaint( FALSE ); + } +} + +/*! + Inserts item \a i into the list view as a top-level item. You do + not need to call this unless you've called takeItem(\a i) or + QListViewItem::takeItem(\a i) and need to reinsert \a i elsewhere. + + \sa QListViewItem::takeItem() takeItem() +*/ + +void QListView::insertItem( QListViewItem * i ) +{ + if ( d->r ) // not for d->r itself + d->r->insertItem( i ); +} + + +/*! + Removes and deletes all the items in this list view and triggers + an update. + + Note that the currentChanged() signal is not emitted when this slot is invoked. + \sa triggerUpdate() +*/ + +void QListView::clear() +{ + bool wasUpdatesEnabled = viewport()->isUpdatesEnabled(); + viewport()->setUpdatesEnabled( FALSE ); + setContentsPos( 0, 0 ); + viewport()->setUpdatesEnabled( wasUpdatesEnabled ); + bool block = signalsBlocked(); + blockSignals( TRUE ); + d->clearing = TRUE; + clearSelection(); + if ( d->iterators ) { + QListViewItemIterator *i = d->iterators->first(); + while ( i ) { + i->curr = 0; + i = d->iterators->next(); + } + } + + if ( d->drawables ) + d->drawables->clear(); + delete d->dirtyItems; + d->dirtyItems = 0; + d->dirtyItemTimer->stop(); + + d->focusItem = 0; + d->selectAnchor = 0; + d->pressedItem = 0; + d->highlighted = 0; + d->startDragItem = 0; + + // if it's down its downness makes no sense, so undown it + d->buttonDown = FALSE; + + QListViewItem *c = (QListViewItem *)d->r->firstChild(); + QListViewItem *n; + while( c ) { + n = (QListViewItem *)c->nextSibling(); + delete c; + c = n; + } + resizeContents( d->h->sizeHint().width(), contentsHeight() ); + delete d->r; + d->r = 0; + QListViewPrivate::Root * r = new QListViewPrivate::Root( this ); + r->is_root = TRUE; + d->r = r; + d->r->setSelectable( FALSE ); + blockSignals( block ); + triggerUpdate(); + d->clearing = FALSE; +} + +/*! + \reimp +*/ + +void QListView::setContentsPos( int x, int y ) +{ + updateGeometries(); + QScrollView::setContentsPos( x, y ); +} + +/*! + Adds a \a width pixels wide column with the column header \a label + to the list view, and returns the index of the new column. + + All columns apart from the first one are inserted to the right of + the existing ones. + + If \a width is negative, the new column's \l WidthMode is set to + \c Maximum instead of \c Manual. + + \sa setColumnText() setColumnWidth() setColumnWidthMode() +*/ +int QListView::addColumn( const QString &label, int width ) +{ + int c = d->h->addLabel( label, width ); + d->column.resize( c+1 ); + d->column.insert( c, new QListViewPrivate::Column ); + d->column[c]->wmode = width >=0 ? Manual : Maximum; + updateGeometries(); + updateGeometry(); + return c; +} + +/*! + \overload + + Adds a \a width pixels wide new column with the header \a label + and the \a iconset to the list view, and returns the index of the + column. + + If \a width is negative, the new column's \l WidthMode is set to + \c Maximum, and to \c Manual otherwise. + + \sa setColumnText() setColumnWidth() setColumnWidthMode() +*/ +int QListView::addColumn( const QIconSet& iconset, const QString &label, int width ) +{ + int c = d->h->addLabel( iconset, label, width ); + d->column.resize( c+1 ); + d->column.insert( c, new QListViewPrivate::Column ); + d->column[c]->wmode = width >=0 ? Manual : Maximum; + updateGeometries(); + updateGeometry(); + return c; +} + +/*! + \property QListView::columns + \brief the number of columns in this list view + + \sa addColumn(), removeColumn() +*/ + +int QListView::columns() const +{ + return d->column.count(); +} + +/*! + Removes the column at position \a index. + + If no columns remain after the column is removed, the + list view will be cleared. + + \sa clear() +*/ + +void QListView::removeColumn( int index ) +{ + if ( index < 0 || index > (int)d->column.count() - 1 ) + return; + + if ( d->vci ) { + QListViewPrivate::ViewColumnInfo *vi = d->vci, *prev = 0, *next = 0; + for ( int i = 0; i < index; ++i ) { + if ( vi ) { + prev = vi; + vi = vi->next; + } + } + if ( vi ) { + next = vi->next; + if ( prev ) + prev->next = next; + vi->next = 0; + delete vi; + if ( index == 0 ) + d->vci = next; + } + } + + QListViewItemIterator it( this ); + for ( ; it.current(); ++it ) { + QListViewPrivate::ItemColumnInfo *ci = (QListViewPrivate::ItemColumnInfo*)it.current()->columns; + if ( ci ) { + QListViewPrivate::ItemColumnInfo *prev = 0, *next = 0; + for ( int i = 0; i < index; ++i ) { + if ( ci ) { + prev = ci; + ci = ci->next; + } + } + if ( ci ) { + next = ci->next; + if ( prev ) + prev->next = next; + ci->next = 0; + delete ci; + if ( index == 0 ) + it.current()->columns = next; + } + } + } + + for ( int i = index; i < (int)d->column.size(); ++i ) { + QListViewPrivate::Column *c = d->column.take( i ); + if ( i == index ) + delete c; + if ( i < (int)d->column.size()-1 ) + d->column.insert( i, d->column[ i + 1 ] ); + } + d->column.resize( d->column.size() - 1 ); + + d->h->removeLabel( index ); + if (d->resizeMode == LastColumn) + d->h->setStretchEnabled( TRUE, d->h->count() - 1 ); + + updateGeometries(); + if ( d->column.count() == 0 ) + clear(); + updateGeometry(); + viewport()->update(); +} + +/*! + Sets the heading of column \a column to \a label. + + \sa columnText() +*/ +void QListView::setColumnText( int column, const QString &label ) +{ + if ( column < d->h->count() ) { + d->h->setLabel( column, label ); + updateGeometries(); + updateGeometry(); + } +} + +/*! + \overload + + Sets the heading of column \a column to \a iconset and \a label. + + \sa columnText() +*/ +void QListView::setColumnText( int column, const QIconSet& iconset, const QString &label ) +{ + if ( column < d->h->count() ) { + d->h->setLabel( column, iconset, label ); + updateGeometries(); + } +} + +/*! + Sets the width of column \a column to \a w pixels. Note that if + the column has a \c WidthMode other than \c Manual, this width + setting may be subsequently overridden. + + \sa columnWidth() +*/ +void QListView::setColumnWidth( int column, int w ) +{ + int oldw = d->h->sectionSize( column ); + if ( column < d->h->count() && oldw != w ) { + d->h->resizeSection( column, w ); + disconnect( d->h, SIGNAL(sizeChange(int,int,int)), + this, SLOT(handleSizeChange(int,int,int)) ); + emit d->h->sizeChange( column, oldw, w); + connect( d->h, SIGNAL(sizeChange(int,int,int)), + this, SLOT(handleSizeChange(int,int,int)) ); + updateGeometries(); + viewport()->update(); + } +} + + +/*! + Returns the text of column \a c. + + \sa setColumnText() +*/ + +QString QListView::columnText( int c ) const +{ + return d->h->label(c); +} + +/*! + Returns the width of column \a c. + + \sa setColumnWidth() +*/ + +int QListView::columnWidth( int c ) const +{ + int actual = d->h->mapToActual( c ); + return d->h->cellSize( actual ); +} + + +/*! + \enum QListView::WidthMode + + This enum type describes how the width of a column in the view + changes. + + \value Manual the column width does not change automatically. + + \value Maximum the column is automatically sized according to the + widths of all items in the column. (Note: The column never shrinks + in this case.) This means that the column is always resized to the + width of the item with the largest width in the column. + + \sa setColumnWidth() setColumnWidthMode() columnWidth() +*/ + + +/*! + Sets column \a{c}'s width mode to \a mode. The default depends on + the original width argument to addColumn(). + + \sa QListViewItem::width() +*/ + +void QListView::setColumnWidthMode( int c, WidthMode mode ) +{ + if ( c < d->h->count() ) + d->column[c]->wmode = mode; +} + + +/*! + Returns the \c WidthMode for column \a c. + + \sa setColumnWidthMode() +*/ + +QListView::WidthMode QListView::columnWidthMode( int c ) const +{ + if ( c < d->h->count() ) + return d->column[c]->wmode; + else + return Manual; +} + + +/*! + Sets column \a{column}'s alignment to \a align. The alignment is + ultimately passed to QListViewItem::paintCell() for each item in + the list view. For horizontally aligned text with Qt::AlignLeft or + Qt::AlignHCenter the ellipsis (...) will be to the right, for + Qt::AlignRight the ellipsis will be to the left. + + \sa Qt::AlignmentFlags +*/ + +void QListView::setColumnAlignment( int column, int align ) +{ + if ( column < 0 ) + return; + if ( !d->vci ) + d->vci = new QListViewPrivate::ViewColumnInfo; + QListViewPrivate::ViewColumnInfo * l = d->vci; + while( column ) { + if ( !l->next ) + l->next = new QListViewPrivate::ViewColumnInfo; + l = l->next; + column--; + } + if ( l->align == align ) + return; + l->align = align; + triggerUpdate(); +} + + +/*! + Returns the alignment of column \a column. The default is \c + AlignAuto. + + \sa Qt::AlignmentFlags +*/ + +int QListView::columnAlignment( int column ) const +{ + if ( column < 0 || !d->vci ) + return AlignAuto; + QListViewPrivate::ViewColumnInfo * l = d->vci; + while( column ) { + if ( !l->next ) + l->next = new QListViewPrivate::ViewColumnInfo; + l = l->next; + column--; + } + return l ? l->align : AlignAuto; +} + + + +/*! + \reimp + */ +void QListView::show() +{ + // Reimplemented to setx the correct background mode and viewed + // area size. + if ( !isVisible() ) { + reconfigureItems(); + updateGeometries(); + } + QScrollView::show(); +} + + +/*! + Updates the sizes of the viewport, header, scroll bars and so on. + + \warning Don't call this directly; call triggerUpdate() instead. +*/ + +void QListView::updateContents() +{ + if ( d->updateHeader ) + header()->adjustHeaderSize(); + d->updateHeader = FALSE; + if ( !isVisible() ) { + // Not in response to a setText/setPixmap any more. + d->useDoubleBuffer = FALSE; + + return; + } + if ( d->drawables ) { + delete d->drawables; + d->drawables = 0; + } + viewport()->setUpdatesEnabled( FALSE ); + updateGeometries(); + viewport()->setUpdatesEnabled( TRUE ); + viewport()->repaint( FALSE ); + d->useDoubleBuffer = FALSE; +} + + +void QListView::updateGeometries() +{ + int th = d->r->totalHeight(); + int tw = d->h->headerWidth(); + if ( d->h->offset() && + tw < d->h->offset() + d->h->width() ) + horizontalScrollBar()->setValue( tw - QListView::d->h->width() ); +#if 0 + if ( QApplication::reverseLayout() && d->h->offset() != horizontalScrollBar()->value() ) + horizontalScrollBar()->setValue( d->h->offset() ); +#endif + verticalScrollBar()->raise(); + resizeContents( tw, th ); + if ( d->h->isHidden() ) { + setMargins( 0, 0, 0, 0 ); + } else { + QSize hs( d->h->sizeHint() ); + setMargins( 0, hs.height(), 0, 0 ); + d->h->setGeometry( viewport()->x(), viewport()->y()-hs.height(), + visibleWidth(), hs.height() ); + } +} + + +/*! + Updates the display when the section \a section has changed size + from the old size, \a os, to the new size, \a ns. +*/ + +void QListView::handleSizeChange( int section, int os, int ns ) +{ + bool upe = viewport()->isUpdatesEnabled(); + viewport()->setUpdatesEnabled( FALSE ); + int sx = horizontalScrollBar()->value(); + bool sv = horizontalScrollBar()->isVisible(); + updateGeometries(); + bool fullRepaint = d->fullRepaintOnComlumnChange || sx != horizontalScrollBar()->value() + || sv != horizontalScrollBar()->isVisible(); + d->fullRepaintOnComlumnChange = FALSE; + viewport()->setUpdatesEnabled( upe ); + + if ( fullRepaint ) { + viewport()->repaint( FALSE ); + return; + } + + int actual = d->h->mapToActual( section ); + int dx = ns - os; + int left = d->h->cellPos( actual ) - contentsX() + d->h->cellSize( actual ); + if ( dx > 0 ) + left -= dx; + if ( left < visibleWidth() ) + viewport()->scroll( dx, 0, QRect( left, 0, visibleWidth() - left, visibleHeight() ) ); + viewport()->repaint( left - 4 - d->ellipsisWidth, 0, 4 + d->ellipsisWidth, + visibleHeight(), FALSE ); // border between the items and ellipses width + + // map auto to left for now. Need to fix this once we support + // reverse layout on the listview. + int align = columnAlignment( section ); + if ( align == AlignAuto ) align = AlignLeft; + if ( align != AlignAuto && align != AlignLeft ) + viewport()->repaint( d->h->cellPos( actual ) - contentsX(), 0, + d->h->cellSize( actual ), visibleHeight() ); + + if ( currentItem() && currentItem()->renameBox ) { + QRect r = itemRect( currentItem() ); + r = QRect( viewportToContents( r.topLeft() ), r.size() ); + r.setLeft( header()->sectionPos( currentItem()->renameCol ) ); + r.setWidth( header()->sectionSize( currentItem()->renameCol ) - 1 ); + if ( currentItem()->renameCol == 0 ) + r.setLeft( r.left() + itemMargin() + ( currentItem()->depth() + + ( rootIsDecorated() ? 1 : 0 ) ) * treeStepSize() - 1 ); + if ( currentItem()->pixmap( currentItem()->renameCol ) ) + r.setLeft( r.left() + currentItem()->pixmap( currentItem()->renameCol )->width() ); + if ( r.x() - contentsX() < 0 ) + r.setX( contentsX() ); + if ( r.width() > visibleWidth() ) + r.setWidth( visibleWidth() ); + addChild( currentItem()->renameBox, r.x(), r.y() ); + currentItem()->renameBox->resize( r.size() ); + } +} + + +/* + Very smart internal slot that repaints \e only the items that need + to be repainted. Don't use this directly; call repaintItem() + instead. +*/ + +void QListView::updateDirtyItems() +{ + if ( d->timer->isActive() || !d->dirtyItems ) + return; + QRect ir; + QPtrDictIterator<void> it( *(d->dirtyItems) ); + QListViewItem * i; + while( (i = (QListViewItem *)(it.currentKey())) != 0 ) { + ++it; + ir = ir.unite( itemRect(i) ); + } + if ( !ir.isEmpty() ) { // rectangle to be repainted + if ( ir.x() < 0 ) + ir.moveBy( -ir.x(), 0 ); + viewport()->repaint( ir, FALSE ); + } +} + + +void QListView::makeVisible() +{ + if ( d->focusItem ) + ensureItemVisible( d->focusItem ); +} + + +/*! + Ensures that the header is correctly sized and positioned when the + resize event \a e occurs. +*/ + +void QListView::resizeEvent( QResizeEvent *e ) +{ + QScrollView::resizeEvent( e ); + d->fullRepaintOnComlumnChange = TRUE; + d->h->resize( visibleWidth(), d->h->height() ); +} + +/*! \reimp */ + +void QListView::viewportResizeEvent( QResizeEvent *e ) +{ + QScrollView::viewportResizeEvent( e ); + d->h->resize( visibleWidth(), d->h->height() ); + if ( resizeMode() != NoColumn && currentItem() && currentItem()->renameBox ) { + QRect r = itemRect( currentItem() ); + r = QRect( viewportToContents( r.topLeft() ), r.size() ); + r.setLeft( header()->sectionPos( currentItem()->renameCol ) ); + r.setWidth( header()->sectionSize( currentItem()->renameCol ) - 1 ); + if ( currentItem()->renameCol == 0 ) + r.setLeft( r.left() + itemMargin() + ( currentItem()->depth() + + ( rootIsDecorated() ? 1 : 0 ) ) * treeStepSize() - 1 ); + if ( currentItem()->pixmap( currentItem()->renameCol ) ) + r.setLeft( r.left() + currentItem()->pixmap( currentItem()->renameCol )->width() ); + if ( r.x() - contentsX() < 0 ) + r.setX( contentsX() ); + if ( r.width() > visibleWidth() ) + r.setWidth( visibleWidth() ); + addChild( currentItem()->renameBox, r.x(), r.y() ); + currentItem()->renameBox->resize( r.size() ); + } +} + +/*! + Triggers a size, geometry and content update during the next + iteration of the event loop. Ensures that there'll be just one + update to avoid flicker. +*/ + +void QListView::triggerUpdate() +{ + if ( !isVisible() || !isUpdatesEnabled() ) { + // Not in response to a setText/setPixmap any more. + d->useDoubleBuffer = FALSE; + + return; // it will update when shown, or something. + } + + d->timer->start( 0, TRUE ); +} + + +/*! + Redirects the event \a e relating to object \a o, for the viewport + to mousePressEvent(), keyPressEvent() and friends. +*/ + +bool QListView::eventFilter( QObject * o, QEvent * e ) +{ + if ( o == d->h && + e->type() >= QEvent::MouseButtonPress && + e->type() <= QEvent::MouseMove ) { + QMouseEvent * me = (QMouseEvent *)e; + QMouseEvent me2( me->type(), + QPoint( me->pos().x(), + me->pos().y() - d->h->height() ), + me->button(), me->state() ); + switch( me2.type() ) { + case QEvent::MouseButtonDblClick: + if ( me2.button() == RightButton ) + return TRUE; + break; + case QEvent::MouseMove: + if ( me2.state() & RightButton ) { + viewportMouseMoveEvent( &me2 ); + return TRUE; + } + break; + default: + break; + } + } else if ( o == viewport() ) { + QFocusEvent * fe = (QFocusEvent *)e; + + switch( e->type() ) { + case QEvent::FocusIn: + focusInEvent( fe ); + return TRUE; + case QEvent::FocusOut: + focusOutEvent( fe ); + return TRUE; + default: + // nothing + break; + } + } else if ( ::qt_cast<QLineEdit*>(o) ) { + if ( currentItem() && currentItem()->renameBox ) { + if ( e->type() == QEvent::KeyPress ) { + QKeyEvent *ke = (QKeyEvent*)e; + if ( ke->key() == Key_Return || + ke->key() == Key_Enter ) { + currentItem()->okRename( currentItem()->renameCol ); + return TRUE; + } else if ( ke->key() == Key_Escape ) { + currentItem()->cancelRename( currentItem()->renameCol ); + return TRUE; + } + } else if ( e->type() == QEvent::FocusOut ) { + if ( ( (QFocusEvent*)e )->reason() != QFocusEvent::Popup ) { + QCustomEvent *e = new QCustomEvent( 9999 ); + QApplication::postEvent( o, e ); + return TRUE; + } + } else if ( e->type() == 9999 ) { + if ( d->defRenameAction == Reject ) + currentItem()->cancelRename( currentItem()->renameCol ); + else + currentItem()->okRename( currentItem()->renameCol ); + return TRUE; + } + } + } + + return QScrollView::eventFilter( o, e ); +} + + +/*! + Returns a pointer to the list view containing this item. + + Note that this function traverses the items to the root to find the + listview. This function will return 0 for taken items - see + QListViewItem::takeItem() +*/ + +QListView * QListViewItem::listView() const +{ + const QListViewItem* c = this; + while ( c && !c->is_root ) + c = c->parentItem; + if ( !c ) + return 0; + return ((QListViewPrivate::Root*)c)->theListView(); +} + + +/*! + Returns the depth of this item. +*/ +int QListViewItem::depth() const +{ + return parentItem ? parentItem->depth()+1 : -1; // -1 == the hidden root +} + + +/*! + Returns a pointer to the item immediately above this item on the + screen. This is usually the item's closest older sibling, but it + may also be its parent or its next older sibling's youngest child, + or something else if anyoftheabove->height() returns 0. Returns 0 + if there is no item immediately above this item. + + This function assumes that all parents of this item are open (i.e. + that this item is visible, or can be made visible by scrolling). + + This function might be relatively slow because of the tree + traversions needed to find the correct item. + + \sa itemBelow() QListView::itemRect() +*/ + +QListViewItem * QListViewItem::itemAbove() +{ + if ( !parentItem ) + return 0; + + QListViewItem * c = parentItem; + if ( c->childItem != this ) { + c = c->childItem; + while( c && c->siblingItem != this ) + c = c->siblingItem; + if ( !c ) + return 0; + while( c->isOpen() && c->childItem ) { + c = c->childItem; + while( c->siblingItem ) + c = c->siblingItem; // assign c's sibling to c + } + } + if ( c && ( !c->height() || !c->isEnabled() ) ) + return c->itemAbove(); + return c; +} + + +/*! + Returns a pointer to the item immediately below this item on the + screen. This is usually the item's eldest child, but it may also + be its next younger sibling, its parent's next younger sibling, + grandparent's, etc., or something else if anyoftheabove->height() + returns 0. Returns 0 if there is no item immediately below this + item. + + This function assumes that all parents of this item are open (i.e. + that this item is visible or can be made visible by scrolling). + + \sa itemAbove() QListView::itemRect() +*/ + +QListViewItem * QListViewItem::itemBelow() +{ + QListViewItem * c = 0; + if ( isOpen() && childItem ) { + c = childItem; + } else if ( siblingItem ) { + c = siblingItem; + } else if ( parentItem ) { + c = this; + do { + c = c->parentItem; + } while( c->parentItem && !c->siblingItem ); + if ( c ) + c = c->siblingItem; + } + if ( c && ( !c->height() || !c->isEnabled() ) ) + return c->itemBelow(); + return c; +} + + +/*! + \fn bool QListViewItem::isOpen () const + + Returns TRUE if this list view item has children \e and they are + not explicitly hidden; otherwise returns FALSE. + + \sa setOpen() +*/ + +/*! + Returns the first (top) child of this item, or 0 if this item has + no children. + + Note that the children are not guaranteed to be sorted properly. + QListView and QListViewItem try to postpone or avoid sorting to + the greatest degree possible, in order to keep the user interface + snappy. + + \sa nextSibling() sortChildItems() +*/ + +QListViewItem* QListViewItem::firstChild() const +{ + enforceSortOrder(); + return childItem; +} + + +/*! + Returns the parent of this item, or 0 if this item has no parent. + + \sa firstChild(), nextSibling() +*/ + +QListViewItem* QListViewItem::parent() const +{ + if ( !parentItem || parentItem->is_root ) return 0; + return parentItem; +} + + +/*! + \fn QListViewItem* QListViewItem::nextSibling() const + + Returns the sibling item below this item, or 0 if there is no + sibling item after this item. + + Note that the siblings are not guaranteed to be sorted properly. + QListView and QListViewItem try to postpone or avoid sorting to + the greatest degree possible, in order to keep the user interface + snappy. + + \sa firstChild() sortChildItems() +*/ + +/*! + \fn int QListViewItem::childCount () const + + Returns how many children this item has. The count only includes + the item's immediate children. +*/ + + +/*! + Returns the height of this item in pixels. This does not include + the height of any children; totalHeight() returns that. +*/ +int QListViewItem::height() const +{ + QListViewItem * that = (QListViewItem *)this; + if ( !that->configured ) { + that->configured = TRUE; + that->setup(); // ### virtual non-const function called in const + } + + return visible ? ownHeight : 0; +} + +/*! + Call this function when the value of width() may have changed for + column \a c. Normally, you should call this if text(c) changes. + Passing -1 for \a c indicates that all columns may have changed. + It is more efficient to pass -1 if two or more columns have + changed than to call widthChanged() separately for each one. + + \sa width() +*/ +void QListViewItem::widthChanged( int c ) const +{ + QListView *lv = listView(); + if ( lv ) + lv->widthChanged( this, c ); +} + +/*! + \fn void QListView::dropped ( QDropEvent * e ) + + This signal is emitted, when a drop event occurred on the + viewport (not onto an item). + + \a e provides all the information about the drop. +*/ + +/*! + \fn void QListView::selectionChanged() + + This signal is emitted whenever the set of selected items has + changed (normally before the screen update). It is available in + \c Single, \c Multi, and \c Extended selection modes, but is most + useful in \c Multi selection mode. + + \warning Do not delete any QListViewItem objects in slots + connected to this signal. + + \sa setSelected() QListViewItem::setSelected() +*/ + + +/*! + \fn void QListView::pressed( QListViewItem *item ) + + This signal is emitted whenever the user presses the mouse button + in a list view. \a item is the list view item on which the user + pressed the mouse button, or 0 if the user didn't press the mouse + on an item. + + \warning Do not delete any QListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void QListView::pressed( QListViewItem *item, const QPoint &pnt, int c ) + + \overload + + This signal is emitted whenever the user presses the mouse button + in a list view. \a item is the list view item on which the user + pressed the mouse button, or 0 if the user didn't press the mouse + on an item. \a pnt is the position of the mouse cursor in global + coordinates, and \a c is the column where the mouse cursor was + when the user pressed the mouse button. + + \warning Do not delete any QListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void QListView::clicked( QListViewItem *item ) + + This signal is emitted whenever the user clicks (mouse pressed \e + and mouse released) in the list view. \a item is the clicked list + view item, or 0 if the user didn't click on an item. + + \warning Do not delete any QListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void QListView::mouseButtonClicked(int button, QListViewItem * item, const QPoint & pos, int c) + + This signal is emitted whenever the user clicks (mouse pressed \e + and mouse released) in the list view at position \a pos. \a button + is the mouse button that the user pressed, \a item is the clicked + list view item or 0 if the user didn't click on an item. If \a + item is not 0, \a c is the list view column into which the user + pressed; if \a item is 0 \a{c}'s value is undefined. + + \warning Do not delete any QListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void QListView::mouseButtonPressed(int button, QListViewItem * item, const QPoint & pos, int c) + + This signal is emitted whenever the user pressed the mouse button + in the list view at position \a pos. \a button is the mouse button + which the user pressed, \a item is the pressed list view item or 0 + if the user didn't press on an item. If \a item is not 0, \a c is + the list view column into which the user pressed; if \a item is 0 + \a{c}'s value is undefined. + + \warning Do not delete any QListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void QListView::clicked( QListViewItem *item, const QPoint &pnt, int c ) + + \overload + + This signal is emitted whenever the user clicks (mouse pressed \e + and mouse released) in the list view. \a item is the clicked list + view item, or 0 if the user didn't click on an item. \a pnt is the + position where the user has clicked in global coordinates. If \a + item is not 0, \a c is the list view column into which the user + pressed; if \a item is 0 \a{c}'s value is undefined. + + \warning Do not delete any QListViewItem objects in slots + connected to this signal. +*/ + +/*! + \fn void QListView::selectionChanged( QListViewItem * ) + + \overload + + This signal is emitted whenever the selected item has changed in + \c Single selection mode (normally after the screen update). The + argument is the newly selected item. If the selection is cleared + (when, for example, the user clicks in the unused area of the list + view) then this signal will not be emitted. + + In \c Multi selection mode, use the no argument overload of this + signal. + + \warning Do not delete any QListViewItem objects in slots + connected to this signal. + + \sa setSelected() QListViewItem::setSelected() currentChanged() +*/ + + +/*! + \fn void QListView::currentChanged( QListViewItem * ) + + This signal is emitted whenever the current item has changed + (normally after the screen update). The current item is the item + responsible for indicating keyboard focus. + + The argument is the newly current item, or 0 if the change made no + item current. This can happen, for example, if all items in the + list view are deleted. + + \warning Do not delete any QListViewItem objects in slots + connected to this signal. + + \sa setCurrentItem() currentItem() +*/ + + +/*! + \fn void QListView::expanded( QListViewItem *item ) + + This signal is emitted when \a item has been expanded, i.e. when + the children of \a item are shown. + + \sa setOpen() collapsed() +*/ + +/*! + \fn void QListView::collapsed( QListViewItem *item ) + + This signal is emitted when the \a item has been collapsed, i.e. + when the children of \a item are hidden. + + \sa setOpen() expanded() +*/ + +/*! + Processes the mouse press event \a e on behalf of the viewed widget. +*/ +void QListView::contentsMousePressEvent( QMouseEvent * e ) +{ + contentsMousePressEventEx( e ); +} + +void QListView::contentsMousePressEventEx( QMouseEvent * e ) +{ + if ( !e ) + return; + + if ( !d->ignoreEditAfterFocus ) + d->startEdit = TRUE; + d->ignoreEditAfterFocus = FALSE; + + if ( currentItem() && currentItem()->renameBox && + !itemRect( currentItem() ).contains( e->pos() ) ) { + d->startEdit = FALSE; + if ( d->defRenameAction == Reject ) + currentItem()->cancelRename( currentItem()->renameCol ); + else + currentItem()->okRename( currentItem()->renameCol ); + } + + d->startDragItem = 0; + d->dragStartPos = e->pos(); + QPoint vp = contentsToViewport( e->pos() ); + + d->ignoreDoubleClick = FALSE; + d->buttonDown = TRUE; + + QListViewItem * i = itemAt( vp ); + d->pressedEmptyArea = e->y() > contentsHeight(); + if ( i && !i->isEnabled() ) + return; + if ( d->startEdit && ( i != currentItem() || (i && !i->isSelected()) ) ) + d->startEdit = FALSE; + QListViewItem *oldCurrent = currentItem(); + + if ( e->button() == RightButton && (e->state() & ControlButton ) ) + goto emit_signals; + + if ( !i ) { + if ( !( e->state() & ControlButton ) ) + clearSelection(); + goto emit_signals; + } else { + // No new anchor when using shift + if ( !(e->state() & ShiftButton) ) + d->selectAnchor = i; + } + + if ( (i->isExpandable() || i->childCount()) && + d->h->mapToLogical( d->h->cellAt( vp.x() ) ) == 0 ) { + int x1 = vp.x() + + d->h->offset() - + d->h->cellPos( d->h->mapToActual( 0 ) ); + QPtrListIterator<QListViewPrivate::DrawableItem> it( *(d->drawables) ); + while( it.current() && it.current()->i != i ) + ++it; + + if ( it.current() ) { + x1 -= treeStepSize() * (it.current()->l - 1); + QStyle::SubControl ctrl = + style().querySubControl( QStyle::CC_ListView, + this, QPoint(x1, e->pos().y()), + QStyleOption(i) ); + if( ctrl == QStyle::SC_ListViewExpand && + e->type() == style().styleHint(QStyle::SH_ListViewExpand_SelectMouseType, this)) { + d->buttonDown = FALSE; + if ( e->button() == LeftButton ) { + bool close = i->isOpen(); + setOpen( i, !close ); + // ### Looks dangerous, removed because of reentrance problems + // qApp->processEvents(); + if ( !d->focusItem ) { + d->focusItem = i; + repaintItem( d->focusItem ); + emit currentChanged( d->focusItem ); + } + if ( close ) { + bool newCurrent = FALSE; + QListViewItem *ci = d->focusItem; + while ( ci ) { + if ( ci->parent() && ci->parent() == i ) { + newCurrent = TRUE; + break; + } + ci = ci->parent(); + } + if ( newCurrent ) { + setCurrentItem( i ); + } + } + } + d->ignoreDoubleClick = TRUE; + d->buttonDown = FALSE; + goto emit_signals; + } + } + } + + d->select = d->selectionMode == Multi ? !i->isSelected() : TRUE; + + {// calculate activatedP + activatedByClick = TRUE; + QPoint topLeft = itemRect( i ).topLeft(); //### inefficient? + activatedP = vp - topLeft; + int xdepth = treeStepSize() * (i->depth() + (rootIsDecorated() ? 1 : 0)) + + itemMargin(); + + // This returns the position of the first visual section?!? Shouldn't that always be 0? Keep it just in case we have missed something. + xdepth += d->h->sectionPos( d->h->mapToSection( 0 ) ); + activatedP.rx() -= xdepth; + } + i->activate(); + activatedByClick = FALSE; + + if ( i != d->focusItem ) + setCurrentItem( i ); + else + repaintItem( i ); + + d->pressedSelected = i && i->isSelected(); + + if ( i->isSelectable() && selectionMode() != NoSelection ) { + if ( selectionMode() == Single ) + setSelected( i, TRUE ); + else if ( selectionMode() == Multi ) + setSelected( i, d->select ); + else if ( selectionMode() == Extended ) { + bool changed = FALSE; + if ( !(e->state() & (ControlButton | ShiftButton)) ) { + if ( !i->isSelected() ) { + bool blocked = signalsBlocked(); + blockSignals( TRUE ); + clearSelection(); + blockSignals( blocked ); + i->setSelected( TRUE ); + changed = TRUE; + } + } else { + if ( e->state() & ShiftButton ) + d->pressedSelected = FALSE; + if ( (e->state() & ControlButton) && !(e->state() & ShiftButton) && i ) { + i->setSelected( !i->isSelected() ); + changed = TRUE; + d->pressedSelected = FALSE; + } else if ( !oldCurrent || !i || oldCurrent == i ) { + if ( (bool)i->selected != d->select ) { + changed = TRUE; + i->setSelected( d->select ); + } + // Shift pressed in Extended mode --- + } else { + changed = selectRange( i, oldCurrent, d->selectAnchor ); + } + } + if ( changed ) { + d->useDoubleBuffer = TRUE; + triggerUpdate(); + emit selectionChanged(); + +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection ); +#endif + } + } + } + + emit_signals: + + if ( i && !d->buttonDown && + vp.x() + contentsX() < itemMargin() + ( i->depth() + ( rootIsDecorated() ? 1 : 0 ) ) * treeStepSize() ) + i = 0; + d->pressedItem = i; + + int c = i ? d->h->mapToLogical( d->h->cellAt( vp.x() ) ) : -1; + if ( !i || ( i && i->isEnabled() ) ) { + emit pressed( i ); + emit pressed( i, viewport()->mapToGlobal( vp ), c ); + } + emit mouseButtonPressed( e->button(), i, viewport()->mapToGlobal( vp ), c ); + + if ( e->button() == RightButton && i == d->pressedItem ) { + if ( !i && !(e->state() & ControlButton) ) + clearSelection(); + + emit rightButtonPressed( i, viewport()->mapToGlobal( vp ), c ); + } +} + +/*! + \reimp +*/ + +void QListView::contentsContextMenuEvent( QContextMenuEvent *e ) +{ + if ( !receivers( SIGNAL(contextMenuRequested(QListViewItem*,const QPoint&,int)) ) ) { + e->ignore(); + return; + } + if ( e->reason() == QContextMenuEvent::Keyboard ) { + QListViewItem *item = currentItem(); + if ( item ) { + QRect r = itemRect( item ); + QPoint p = r.topLeft(); + if ( allColumnsShowFocus() ) + p += QPoint( width() / 2, ( r.height() / 2 ) ); + else + p += QPoint( columnWidth( 0 ) / 2, ( r.height() / 2 ) ); + p.rx() = QMAX( 0, p.x() ); + p.rx() = QMIN( visibleWidth(), p.x() ); + emit contextMenuRequested( item, viewport()->mapToGlobal( p ), -1 ); + } + } else { + QPoint vp = contentsToViewport( e->pos() ); + QListViewItem * i = itemAt( vp ); + int c = i ? d->h->mapToLogical( d->h->cellAt( vp.x() ) ) : -1; + emit contextMenuRequested( i, viewport()->mapToGlobal( vp ), c ); + } +} + +/*! + Processes the mouse release event \a e on behalf of the viewed widget. +*/ +void QListView::contentsMouseReleaseEvent( QMouseEvent * e ) +{ + contentsMouseReleaseEventEx( e ); +} + +void QListView::contentsMouseReleaseEventEx( QMouseEvent * e ) +{ + d->startDragItem = 0; + bool emitClicked = !d->pressedItem || d->buttonDown; + d->buttonDown = FALSE; + // delete and disconnect autoscroll timer, if we have one + if ( d->scrollTimer ) { + disconnect( d->scrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll()) ); + d->scrollTimer->stop(); + delete d->scrollTimer; + d->scrollTimer = 0; + } + + if ( !e ) + return; + + if ( d->selectionMode == Extended && + d->focusItem == d->pressedItem && + d->pressedSelected && d->focusItem && + e->button() == LeftButton) { + bool block = signalsBlocked(); + blockSignals( TRUE ); + clearSelection(); + blockSignals( block ); + d->focusItem->setSelected( TRUE ); + emit selectionChanged(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), 0, QAccessible::Selection ); +#endif + } + + QPoint vp = contentsToViewport(e->pos()); + QListViewItem *i = itemAt( vp ); + if ( i && !i->isEnabled() ) + return; + + if ( i && i == d->pressedItem && (i->isExpandable() || i->childCount()) && + !d->h->mapToLogical( d->h->cellAt( vp.x() ) ) && e->button() == LeftButton && + e->type() == style().styleHint(QStyle::SH_ListViewExpand_SelectMouseType, this)) { + QPtrListIterator<QListViewPrivate::DrawableItem> it( *(d->drawables) ); + while( it.current() && it.current()->i != i ) + ++it; + if ( it.current() ) { + int x1 = vp.x() + d->h->offset() - d->h->cellPos( d->h->mapToActual( 0 ) ) - + (treeStepSize() * (it.current()->l - 1)); + QStyle::SubControl ctrl = style().querySubControl( QStyle::CC_ListView, + this, QPoint(x1, e->pos().y()), + QStyleOption(i) ); + if( ctrl == QStyle::SC_ListViewExpand ) { + bool close = i->isOpen(); + setOpen( i, !close ); + // ### Looks dangerous, removed because of reentrance problems + // qApp->processEvents(); + if ( !d->focusItem ) { + d->focusItem = i; + repaintItem( d->focusItem ); + emit currentChanged( d->focusItem ); + } + if ( close ) { + bool newCurrent = FALSE; + QListViewItem *ci = d->focusItem; + while ( ci ) { + if ( ci->parent() && ci->parent() == i ) { + newCurrent = TRUE; + break; + } + ci = ci->parent(); + } + if ( newCurrent ) + setCurrentItem( i ); + d->ignoreDoubleClick = TRUE; + } + } + } + } + + if ( i == d->pressedItem && i && i->isSelected() && e->button() == LeftButton && d->startEdit ) { + QRect r = itemRect( currentItem() ); + r = QRect( viewportToContents( r.topLeft() ), r.size() ); + d->pressedColumn = header()->sectionAt( e->pos().x() ); + r.setLeft( header()->sectionPos( d->pressedColumn ) ); + r.setWidth( header()->sectionSize( d->pressedColumn ) - 1 ); + if ( d->pressedColumn == 0 ) + r.setLeft( r.left() + itemMargin() + ( currentItem()->depth() + + ( rootIsDecorated() ? 1 : 0 ) ) * treeStepSize() - 1 ); + if ( r.contains( e->pos() ) && + !( e->state() & ( ShiftButton | ControlButton ) ) ) + d->renameTimer->start( QApplication::doubleClickInterval(), TRUE ); + } + // Check if we are clicking at the root decoration. + int rootColumnPos = d->h->sectionPos( 0 ); + if ( i && vp.x() + contentsX() < rootColumnPos + itemMargin() + ( i->depth() + ( rootIsDecorated() ? 1 : 0 ) ) * treeStepSize() + && vp.x() + contentsX() > rootColumnPos ) { + i = 0; + } + emitClicked = emitClicked && d->pressedItem == i; + d->pressedItem = 0; + d->highlighted = 0; + + if ( emitClicked ) { + if ( !i || ( i && i->isEnabled() ) ) { + emit clicked( i ); + emit clicked( i, viewport()->mapToGlobal( vp ), d->h->mapToLogical( d->h->cellAt( vp.x() ) ) ); + } + emit mouseButtonClicked( e->button(), i, viewport()->mapToGlobal( vp ), + i ? d->h->mapToLogical( d->h->cellAt( vp.x() ) ) : -1 ); + + if ( e->button() == RightButton ) { + if ( !i ) { + if ( !(e->state() & ControlButton) ) + clearSelection(); + emit rightButtonClicked( 0, viewport()->mapToGlobal( vp ), -1 ); + return; + } + + int c = d->h->mapToLogical( d->h->cellAt( vp.x() ) ); + emit rightButtonClicked( i, viewport()->mapToGlobal( vp ), c ); + } + } +} + + +/*! + Processes the mouse double-click event \a e on behalf of the viewed widget. +*/ +void QListView::contentsMouseDoubleClickEvent( QMouseEvent * e ) +{ + d->renameTimer->stop(); + d->startEdit = FALSE; + if ( !e || e->button() != LeftButton ) + return; + + // ensure that the following mouse moves and eventual release is + // ignored. + d->buttonDown = FALSE; + + if ( d->ignoreDoubleClick ) { + d->ignoreDoubleClick = FALSE; + return; + } + + QPoint vp = contentsToViewport(e->pos()); + + QListViewItem * i = itemAt( vp ); + + // we emit doubleClicked when the item is null (or enabled) to be consistent with + // rightButtonClicked etc. + if ( !i || i->isEnabled() ) { + int c = d->h->mapToLogical( d->h->cellAt( vp.x() ) ); + emit doubleClicked( i, viewport()->mapToGlobal( vp ), c ); + } + + if ( !i || !i->isEnabled() ) + return; + + if ( !i->isOpen() ) { + if ( i->isExpandable() || i->childCount() ) + setOpen( i, TRUE ); + } else { + setOpen( i, FALSE ); + } + + // we emit the 'old' obsolete doubleClicked only if the item is not null and enabled + emit doubleClicked( i ); +} + + +/*! + Processes the mouse move event \a e on behalf of the viewed widget. +*/ +void QListView::contentsMouseMoveEvent( QMouseEvent * e ) +{ + if ( !e ) + return; + + bool needAutoScroll = FALSE; + + QPoint vp = contentsToViewport(e->pos()); + + QListViewItem * i = itemAt( vp ); + if ( i && !i->isEnabled() ) + return; + if ( i != d->highlighted && + !(d->pressedItem && + ( d->pressedItem->isSelected() || d->selectionMode == NoSelection ) && + d->pressedItem->dragEnabled() )) { + + if ( i ) { + emit onItem( i ); + } else { + emit onViewport(); + } + d->highlighted = i; + } + + if ( d->startDragItem ) + i = d->startDragItem; + + if ( !d->buttonDown || + ( ( e->state() & LeftButton ) != LeftButton && + ( e->state() & MidButton ) != MidButton && + ( e->state() & RightButton ) != RightButton ) ) + return; + + if ( d->pressedItem && + ( d->pressedItem->isSelected() || d->selectionMode == NoSelection ) && + d->pressedItem->dragEnabled() ) { + + if ( !d->startDragItem ) { + setSelected( d->pressedItem, TRUE ); + d->startDragItem = d->pressedItem; + } + if ( ( d->dragStartPos - e->pos() ).manhattanLength() > QApplication::startDragDistance() ) { + d->buttonDown = FALSE; +#ifndef QT_NO_DRAGANDDROP + startDrag(); +#endif + } + return; + } + + // check, if we need to scroll + if ( vp.y() > visibleHeight() || vp.y() < 0 ) + needAutoScroll = TRUE; + + // if we need to scroll and no autoscroll timer is started, + // connect the timer + if ( needAutoScroll && !d->scrollTimer ) { + d->scrollTimer = new QTimer( this ); + connect( d->scrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll()) ); + d->scrollTimer->start( 100, FALSE ); + // call it once manually + doAutoScroll( vp ); + } + + // if we don't need to autoscroll + if ( !needAutoScroll ) { + // if there is a autoscroll timer, delete it + if ( d->scrollTimer ) { + disconnect( d->scrollTimer, SIGNAL(timeout()), + this, SLOT(doAutoScroll()) ); + d->scrollTimer->stop(); + delete d->scrollTimer; + d->scrollTimer = 0; + } + // call this to select an item ( using the pos from the event ) + doAutoScroll( vp ); + } +} + + +/*! + This slot handles auto-scrolling when the mouse button is pressed + and the mouse is outside the widget. +*/ +void QListView::doAutoScroll() +{ + doAutoScroll( QPoint() ); +} + +/* + Handles auto-scrolling when the mouse button is pressed + and the mouse is outside the widget. + + If cursorPos is (0,0) (isNull == TRUE) it uses the current QCursor::pos, otherwise it uses cursorPos +*/ +void QListView::doAutoScroll( const QPoint &cursorPos ) +{ + QPoint pos = cursorPos.isNull() ? viewport()->mapFromGlobal( QCursor::pos() ) : cursorPos; + if ( !d->focusItem || ( d->pressedEmptyArea && pos.y() > contentsHeight() ) ) + return; + + bool down = pos.y() > itemRect( d->focusItem ).y(); + + int g = pos.y() + contentsY(); + + if ( down && pos.y() > height() ) + g = height() + contentsY(); + else if ( pos.y() < 0 ) + g = contentsY(); + + QListViewItem *c = d->focusItem, *old = 0; + QListViewItem *oldCurrent = c; + if ( down ) { + int y = itemRect( d->focusItem ).y() + contentsY(); + while( c && y + c->height() <= g ) { + y += c->height(); + old = c; + c = c->itemBelow(); + } + if ( !c && old ) + c = old; + } else { + int y = itemRect( d->focusItem ).y() + contentsY(); + while( c && y >= g ) { + old = c; + c = c->itemAbove(); + if ( c ) + y -= c->height(); + } + if ( !c && old ) + c = old; + } + + if ( !c || c == d->focusItem ) + return; + + if ( d->focusItem ) { + if ( d->selectionMode == Multi ) { + // also (de)select the ones in between + QListViewItem * b = d->focusItem; + bool down = ( itemPos( c ) > itemPos( b ) ); + while( b && b != c ) { + if ( b->isSelectable() ) + setSelected( b, d->select ); + b = down ? b->itemBelow() : b->itemAbove(); + } + if ( c->isSelectable() ) + setSelected( c, d->select ); + } else if ( d->selectionMode == Extended ) { + if ( selectRange( c, oldCurrent, d->selectAnchor ) ) { + d->useDoubleBuffer = TRUE; + triggerUpdate(); + emit selectionChanged(); + } + } + } + + setCurrentItem( c ); + d->visibleTimer->start( 1, TRUE ); +} + +/*! + \reimp +*/ + +void QListView::focusInEvent( QFocusEvent* ) +{ + d->inMenuMode = FALSE; + if ( d->focusItem ) { + repaintItem( d->focusItem ); + } else if ( firstChild() && QFocusEvent::reason() != QFocusEvent::Mouse ) { + d->focusItem = firstChild(); + emit currentChanged( d->focusItem ); + repaintItem( d->focusItem ); + } + if ( QFocusEvent::reason() == QFocusEvent::Mouse ) { + d->ignoreEditAfterFocus = TRUE; + d->startEdit = FALSE; + } + if ( style().styleHint( QStyle::SH_ItemView_ChangeHighlightOnFocus, this ) ) { + bool db = d->useDoubleBuffer; + d->useDoubleBuffer = TRUE; + viewport()->repaint( FALSE ); + d->useDoubleBuffer = db; + } + + QRect mfrect = itemRect( d->focusItem ); + if ( mfrect.isValid() ) { + if ( header() && header()->isVisible() ) + setMicroFocusHint( mfrect.x(), mfrect.y()+header()->height(), mfrect.width(), mfrect.height(), FALSE ); + else + setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE ); + } +} + + +/*! + \reimp +*/ + +void QListView::focusOutEvent( QFocusEvent* ) +{ + if ( QFocusEvent::reason() == QFocusEvent::Popup && d->buttonDown ) + d->buttonDown = FALSE; + if ( style().styleHint( QStyle::SH_ItemView_ChangeHighlightOnFocus, this ) ) { + d->inMenuMode = + QFocusEvent::reason() == QFocusEvent::Popup + || (qApp->focusWidget() && qApp->focusWidget()->inherits("QMenuBar")); + if ( !d->inMenuMode ) { + bool db = d->useDoubleBuffer; + d->useDoubleBuffer = TRUE; + viewport()->repaint( FALSE ); + d->useDoubleBuffer = db; + } + } + + if ( d->focusItem ) + repaintItem( d->focusItem ); +} + + +/*! + \reimp +*/ + +void QListView::keyPressEvent( QKeyEvent * e ) +{ + if (currentItem() && currentItem()->renameBox) + return; + if (!firstChild()) { + e->ignore(); + return; // subclass bug + } + + QListViewItem* oldCurrent = currentItem(); + if ( !oldCurrent ) { + setCurrentItem( firstChild() ); + if ( d->selectionMode == Single ) + setSelected( firstChild(), TRUE ); + return; + } + + QListViewItem * i = currentItem(); + QListViewItem *old = i; + + QRect r( itemRect( i ) ); + QListViewItem * i2; + + bool singleStep = FALSE; + bool selectCurrent = TRUE; + bool wasNavigation = TRUE; + + switch( e->key() ) { + case Key_Backspace: + case Key_Delete: + d->currentPrefix.truncate( 0 ); + break; + case Key_Enter: + case Key_Return: + d->currentPrefix.truncate( 0 ); + if ( i && !i->isSelectable() && i->isEnabled() && + ( i->childCount() || i->isExpandable() || i->isOpen() ) ) { + i->setOpen( !i->isOpen() ); + return; + } + e->ignore(); + if ( currentItem() && !currentItem()->isEnabled() ) + break; + emit returnPressed( currentItem() ); + // do NOT accept. QDialog. + return; + case Key_Down: + selectCurrent = FALSE; + i = i->itemBelow(); + d->currentPrefix.truncate( 0 ); + singleStep = TRUE; + break; + case Key_Up: + selectCurrent = FALSE; + i = i->itemAbove(); + d->currentPrefix.truncate( 0 ); + singleStep = TRUE; + break; + case Key_Home: + selectCurrent = FALSE; + i = firstChild(); + if (!i->height() || !i->isEnabled()) + i = i->itemBelow(); + d->currentPrefix.truncate( 0 ); + break; + case Key_End: + selectCurrent = FALSE; + i = firstChild(); + while (i->nextSibling() && i->nextSibling()->height() && i->nextSibling()->isEnabled()) + i = i->nextSibling(); + while ( i->itemBelow() ) + i = i->itemBelow(); + d->currentPrefix.truncate( 0 ); + break; + case Key_Next: + selectCurrent = FALSE; + i2 = itemAt( QPoint( 0, visibleHeight()-1 ) ); + if ( i2 == i || !r.isValid() || + visibleHeight() <= itemRect( i ).bottom() ) { + if ( i2 ) + i = i2; + int left = visibleHeight(); + while( (i2 = i->itemBelow()) != 0 && left > i2->height() ) { + left -= i2->height(); + i = i2; + } + } else { + if ( !i2 ) { + // list is shorter than the view, goto last item + while( (i2 = i->itemBelow()) != 0 ) + i = i2; + } else { + i = i2; + } + } + d->currentPrefix.truncate( 0 ); + break; + case Key_Prior: + selectCurrent = FALSE; + i2 = itemAt( QPoint( 0, 0 ) ); + if ( i == i2 || !r.isValid() || r.top() <= 0 ) { + if ( i2 ) + i = i2; + int left = visibleHeight(); + while( (i2 = i->itemAbove()) != 0 && left > i2->height() ) { + left -= i2->height(); + i = i2; + } + } else { + i = i2; + } + d->currentPrefix.truncate( 0 ); + break; + case Key_Plus: + d->currentPrefix.truncate( 0 ); + if ( !i->isOpen() && (i->isExpandable() || i->childCount()) ) + setOpen( i, TRUE ); + else + return; + break; + case Key_Right: + d->currentPrefix.truncate( 0 ); + if ( i->isOpen() && i->childItem) { + QListViewItem *childItem = i->childItem; + while (childItem && !childItem->isVisible()) + childItem = childItem->nextSibling(); + if (childItem) + i = childItem; + } else if ( !i->isOpen() && (i->isExpandable() || i->childCount()) ) { + setOpen( i, TRUE ); + } else if ( contentsX() + visibleWidth() < contentsWidth() ) { + horizontalScrollBar()->addLine(); + return; + } else { + return; + } + break; + case Key_Minus: + d->currentPrefix.truncate( 0 ); + if ( i->isOpen() ) + setOpen( i, FALSE ); + else + return; + break; + case Key_Left: + d->currentPrefix.truncate( 0 ); + if ( i->isOpen() ) { + setOpen( i, FALSE ); + } else if ( i->parentItem && i->parentItem != d->r ) { + i = i->parentItem; + } else if ( contentsX() ) { + horizontalScrollBar()->subtractLine(); + return; + } else { + return; + } + break; + case Key_Space: + activatedByClick = FALSE; + d->currentPrefix.truncate( 0 ); + if ( currentItem() && !currentItem()->isEnabled() ) + break; + i->activate(); + if ( i->isSelectable() && ( d->selectionMode == Multi || d->selectionMode == Extended ) ) { + setSelected( i, !i->isSelected() ); + d->currentPrefix.truncate( 0 ); + } + emit spacePressed( currentItem() ); + break; + case Key_Escape: + e->ignore(); // For QDialog + return; + case Key_F2: + if ( currentItem() && currentItem()->renameEnabled( 0 ) ) + currentItem()->startRename( 0 ); + default: + if ( e->text().length() > 0 && e->text()[ 0 ].isPrint() ) { + selectCurrent = FALSE; + wasNavigation = FALSE; + QString input( d->currentPrefix ); + QListViewItem * keyItem = i; + QTime now( QTime::currentTime() ); + bool tryFirst = TRUE; + while( keyItem ) { + // try twice, first with the previous string and this char + if ( d->currentPrefixTime.msecsTo( now ) <= 400 ) + input = input + e->text().lower(); + else + input = e->text().lower(); + if ( input.length() == e->text().length() ) { + if ( keyItem->itemBelow() ) { + keyItem = keyItem->itemBelow(); + tryFirst = TRUE; + } else { + keyItem = firstChild(); + tryFirst = FALSE; + } + } + QString keyItemKey; + QString prefix; + while( keyItem ) { + keyItemKey = QString::null; + // Look first in the sort column, then left to right + if (d->sortcolumn != Unsorted) + keyItemKey = keyItem->text(d->sortcolumn); + for ( int col = 0; col < d->h->count() && keyItemKey.isNull(); ++col ) + keyItemKey = keyItem->text( d->h->mapToSection(col) ); + if ( !keyItemKey.isEmpty() ) { + prefix = keyItemKey; + prefix.truncate( input.length() ); + prefix = prefix.lower(); + if ( prefix == input ) { + d->currentPrefix = input; + d->currentPrefixTime = now; + i = keyItem; + // nonoptimal double-break... + keyItem = 0; + input.truncate( 0 ); + tryFirst = FALSE; + } + } + if ( keyItem ) + keyItem = keyItem->itemBelow(); + if ( !keyItem && tryFirst ) { + keyItem = firstChild(); + tryFirst = FALSE; + } + } + // then, if appropriate, with just this character + if ( input.length() > e->text().length() ) { + input.truncate(0); + keyItem = i; + } + } + } else { + d->currentPrefix.truncate( 0 ); + if ( e->state() & ControlButton ) { + d->currentPrefix = QString::null; + switch ( e->key() ) { + case Key_A: + selectAll( TRUE ); + break; + } + } + e->ignore(); + return; + } + } + + if ( !i ) + return; + + if ( !( e->state() & ShiftButton ) || !d->selectAnchor ) + d->selectAnchor = i; + + setCurrentItem( i ); + if ( i->isSelectable() ) + handleItemChange( old, wasNavigation && (e->state() & ShiftButton), + wasNavigation && (e->state() & ControlButton) ); + + if ( d->focusItem && !d->focusItem->isSelected() && d->selectionMode == Single && selectCurrent ) + setSelected( d->focusItem, TRUE ); + + if ( singleStep ) + d->visibleTimer->start( 1, TRUE ); + else + ensureItemVisible( i ); +} + + +/*! + Returns the list view item at \a viewPos. Note that \a viewPos is + in the viewport()'s coordinate system, not in the list view's own, + much larger, coordinate system. + + itemAt() returns 0 if there is no such item. + + Note that you also get the pointer to the item if \a viewPos + points to the root decoration (see setRootIsDecorated()) of the + item. To check whether or not \a viewPos is on the root decoration + of the item, you can do something like this: + + \code + QListViewItem *i = itemAt( p ); + if ( i ) { + if ( p.x() > header()->sectionPos( header()->mapToIndex( 0 ) ) + + treeStepSize() * ( i->depth() + ( rootIsDecorated() ? 1 : 0) ) + itemMargin() || + p.x() < header()->sectionPos( header()->mapToIndex( 0 ) ) ) { + ; // p is not on root decoration + else + ; // p is on the root decoration + } + \endcode + + This might be interesting if you use this function to find out + where the user clicked and if you want to start a drag (which you + do not want to do if the user clicked onto the root decoration of + an item). + + \sa itemPos() itemRect() viewportToContents() +*/ + +QListViewItem * QListView::itemAt( const QPoint & viewPos ) const +{ + if ( viewPos.x() > contentsWidth() - contentsX() ) + return 0; + + if ( !d->drawables || d->drawables->isEmpty() ) + buildDrawableList(); + + QListViewPrivate::DrawableItem * c = d->drawables->first(); + int g = viewPos.y() + contentsY(); + + while( c && c->i && ( c->y + c->i->height() <= g || + !c->i->isVisible() || + c->i->parent() && !c->i->parent()->isVisible() ) ) + c = d->drawables->next(); + + QListViewItem *i = (c && c->y <= g) ? c->i : 0; + return i; +} + + +/*! + Returns the y-coordinate of \a item in the list view's coordinate + system. This function is normally much slower than itemAt() but it + works for all items, whereas itemAt() normally works only for + items on the screen. + + This is a thin wrapper around QListViewItem::itemPos(). + + \sa itemAt() itemRect() +*/ + +int QListView::itemPos( const QListViewItem * item ) +{ + return item ? item->itemPos() : 0; +} + + +/*! \obsolete + \property QListView::multiSelection + \brief whether the list view is in multi-selection or extended-selection mode + + If you enable multi-selection, \c Multi, mode, it is possible to + specify whether or not this mode should be extended. \c Extended + means that the user can select multiple items only when pressing + the Shift or Ctrl key at the same time. + + The default selection mode is \c Single. + + \sa selectionMode() +*/ + +void QListView::setMultiSelection( bool enable ) +{ + if ( !enable ) + d->selectionMode = QListView::Single; + else if ( d->selectionMode != Multi && d->selectionMode != Extended ) + d->selectionMode = QListView::Multi; +} + +bool QListView::isMultiSelection() const +{ + return d->selectionMode == QListView::Extended || d->selectionMode == QListView::Multi; +} + +/*! + \property QListView::selectionMode + \brief the list view's selection mode + + The mode can be \c Single (the default), \c Extended, \c Multi or + \c NoSelection. + + \sa multiSelection +*/ + +void QListView::setSelectionMode( SelectionMode mode ) +{ + if ( d->selectionMode == mode ) + return; + + if ( ( d->selectionMode == Multi || d->selectionMode == Extended ) && + ( mode == QListView::Single || mode == QListView::NoSelection ) ){ + clearSelection(); + if ( ( mode == QListView::Single ) && currentItem() ) + currentItem()->selected = TRUE; + } + + d->selectionMode = mode; +} + +QListView::SelectionMode QListView::selectionMode() const +{ + return d->selectionMode; +} + + +/*! + If \a selected is TRUE the \a item is selected; otherwise it is + unselected. + + If the list view is in \c Single selection mode and \a selected is + TRUE, the currently selected item is unselected and \a item is + made current. Unlike QListViewItem::setSelected(), this function + updates the list view as necessary and emits the + selectionChanged() signals. + + \sa isSelected() setMultiSelection() isMultiSelection() + setCurrentItem(), setSelectionAnchor() +*/ + +void QListView::setSelected( QListViewItem * item, bool selected ) +{ + if ( !item || item->isSelected() == selected || + !item->isSelectable() || selectionMode() == NoSelection ) + return; + + bool emitHighlighted = FALSE; + if ( selectionMode() == Single && d->focusItem != item ) { + QListViewItem *o = d->focusItem; + if ( d->focusItem && d->focusItem->selected ) + d->focusItem->setSelected( FALSE ); + d->focusItem = item; + if ( o ) + repaintItem( o ); + emitHighlighted = TRUE; + } + + item->setSelected( selected ); + + repaintItem( item ); + + if ( d->selectionMode == Single && selected ) + emit selectionChanged( item ); + emit selectionChanged(); + + if ( emitHighlighted ) + emit currentChanged( d->focusItem ); +} + +/*! + Sets the selection anchor to \a item, if \a item is selectable. + + The selection anchor is the item that remains selected when + Shift-selecting with either mouse or keyboard in \c Extended + selection mode. + + \sa setSelected() +*/ + +void QListView::setSelectionAnchor( QListViewItem *item ) +{ + if ( item && item->isSelectable() ) + d->selectAnchor = item; +} + +/*! + Sets all the items to be not selected, updates the list view as + necessary, and emits the selectionChanged() signals. Note that for + \c Multi selection list views this function needs to iterate over + \e all items. + + \sa setSelected(), setMultiSelection() +*/ + +void QListView::clearSelection() +{ + selectAll( FALSE ); +} + +/*! + If \a select is TRUE, all the items get selected; otherwise all + the items get unselected. This only works in the selection modes \c + Multi and \c Extended. In \c Single and \c NoSelection mode the + selection of the current item is just set to \a select. +*/ + +void QListView::selectAll( bool select ) +{ + if ( d->selectionMode == Multi || d->selectionMode == Extended ) { + bool b = signalsBlocked(); + blockSignals( TRUE ); + bool anything = FALSE; + QListViewItemIterator it( this ); + while ( it.current() ) { + QListViewItem *i = it.current(); + if ( (bool)i->selected != select ) { + i->setSelected( select ); + anything = TRUE; + } + ++it; + } + blockSignals( b ); + if ( anything ) { + emit selectionChanged(); + d->useDoubleBuffer = TRUE; + triggerUpdate(); + } + } else if ( d->focusItem ) { + QListViewItem * i = d->focusItem; + setSelected( i, select ); + } +} + +/*! + Inverts the selection. Only works in \c Multi and \c Extended + selection modes. +*/ + +void QListView::invertSelection() +{ + if ( d->selectionMode == Single || + d->selectionMode == NoSelection ) + return; + + bool b = signalsBlocked(); + blockSignals( TRUE ); + QListViewItemIterator it( this ); + for ( ; it.current(); ++it ) + it.current()->setSelected( !it.current()->isSelected() ); + blockSignals( b ); + emit selectionChanged(); + triggerUpdate(); +} + + +/*! + Returns TRUE if the list view item \a i is selected; otherwise + returns FALSE. + + \sa QListViewItem::isSelected() +*/ + +bool QListView::isSelected( const QListViewItem * i ) const +{ + return i ? i->isSelected() : FALSE; +} + + +/*! + Returns the selected item if the list view is in \c Single + selection mode and an item is selected. + + If no items are selected or the list view is not in \c Single + selection mode this function returns 0. + + \sa setSelected() setMultiSelection() +*/ + +QListViewItem * QListView::selectedItem() const +{ + if ( d->selectionMode != Single ) + return 0; + if ( d->focusItem && d->focusItem->isSelected() ) + return d->focusItem; + return 0; +} + + +/*! + Sets item \a i to be the current item and repaints appropriately + (i.e. highlights the item). The current item is used for keyboard + navigation and focus indication; it is independent of any selected + items, although a selected item can also be the current item. + + This function does not set the selection anchor. Use + setSelectionAnchor() instead. + + \sa currentItem() setSelected() +*/ + +void QListView::setCurrentItem( QListViewItem * i ) +{ + if ( !i || d->focusItem == i || !i->isEnabled() ) + return; + + if ( currentItem() && currentItem()->renameBox ) { + if ( d->defRenameAction == Reject ) + currentItem()->cancelRename( currentItem()->renameCol ); + else + currentItem()->okRename( currentItem()->renameCol ); + } + + QListViewItem * prev = d->focusItem; + d->focusItem = i; + + QRect mfrect = itemRect( i ); + if ( mfrect.isValid() ) { + if ( header() && header()->isVisible() ) + setMicroFocusHint( mfrect.x(), mfrect.y()+header()->height(), mfrect.width(), mfrect.height(), FALSE ); + else + setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE ); + } + + if ( i != prev ) { + if ( i && d->selectionMode == Single ) { + bool changed = FALSE; + if ( prev && prev->selected ) { + changed = TRUE; + prev->setSelected( FALSE ); + } + if ( i && !i->selected && d->selectionMode != NoSelection && i->isSelectable() ) { + i->setSelected( TRUE ); + changed = TRUE; + emit selectionChanged( i ); + } + if ( changed ) + emit selectionChanged(); + } + + if ( i ) + repaintItem( i ); + if ( prev ) + repaintItem( prev ); + emit currentChanged( i ); + +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), indexOfItem( i ), QAccessible::Focus ); +#endif + } +} + + +/*! + Returns the current item, or 0 if there isn't one. + + \sa setCurrentItem() +*/ + +QListViewItem * QListView::currentItem() const +{ + return d->focusItem; +} + + +/*! + Returns the rectangle on the screen that item \a i occupies in + viewport()'s coordinates, or an invalid rectangle if \a i is 0 or + is not currently visible. + + The rectangle returned does not include any children of the + rectangle (i.e. it uses QListViewItem::height(), rather than + QListViewItem::totalHeight()). If you want the rectangle to + include children you can use something like this: + + \code + QRect r( listView->itemRect( item ) ); + r.setHeight( (QCOORD)(QMIN( item->totalHeight(), + listView->viewport->height() - r.y() ) ) ) + \endcode + + Note the way it avoids too-high rectangles. totalHeight() can be + much larger than the window system's coordinate system allows. + + itemRect() is comparatively slow. It's best to call it only for + items that are probably on-screen. +*/ + +QRect QListView::itemRect( const QListViewItem * i ) const +{ + if ( !d->drawables || d->drawables->isEmpty() ) + buildDrawableList(); + + QListViewPrivate::DrawableItem * c = d->drawables->first(); + + while( c && c->i && c->i != i ) + c = d->drawables->next(); + + if ( c && c->i == i ) { + int y = c->y - contentsY(); + if ( y + c->i->height() >= 0 && + y < ((QListView *)this)->visibleHeight() ) { + QRect r( -contentsX(), y, d->h->width(), i->height() ); + return r; + } + } + + return QRect( 0, 0, -1, -1 ); +} + + +/*! + \fn void QListView::doubleClicked( QListViewItem *item ) + + \obsolete (use doubleClicked( QListViewItem *, const QPoint&, int )) + + This signal is emitted whenever an item is double-clicked. It's + emitted on the second button press, not the second button release. + \a item is the list view item on which the user did the + double-click. +*/ + +/*! + \fn void QListView::doubleClicked( QListViewItem *, const QPoint&, int ) + + This signal is emitted whenever an item is double-clicked. It's + emitted on the second button press, not the second button release. + The arguments are the relevant QListViewItem (may be 0), the point + in global coordinates and the relevant column (or -1 if the click + was outside the list). + + \warning Do not delete any QListViewItem objects in slots + connected to this signal. +*/ + + +/*! + \fn void QListView::returnPressed( QListViewItem * ) + + This signal is emitted when Enter or Return is pressed. The + argument is the currentItem(). +*/ + +/*! + \fn void QListView::spacePressed( QListViewItem * ) + + This signal is emitted when Space is pressed. The argument is + the currentItem(). +*/ + + +/*! + Sets the list view to be sorted by column \a column in ascending + order if \a ascending is TRUE or descending order if it is FALSE. + + If \a column is -1, sorting is disabled and the user cannot sort + columns by clicking on the column headers. If \a column is larger + than the number of columns the user must click on a column + header to sort the list view. +*/ + +void QListView::setSorting( int column, bool ascending ) +{ + if ( column == -1 ) + column = Unsorted; + + if ( d->sortcolumn == column && d->ascending == ascending ) + return; + + d->ascending = ascending; + d->sortcolumn = column; + if ( d->sortcolumn != Unsorted && d->sortIndicator ) + d->h->setSortIndicator( d->sortcolumn, d->ascending ); + else + d->h->setSortIndicator( -1 ); + + triggerUpdate(); + +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( viewport(), 0, QAccessible::ObjectReorder ); +#endif +} + +/*! + \enum Qt::SortOrder + + This enum describes how the items in a widget are sorted. + + \value Ascending The items are sorted ascending e.g. starts with + 'AAA' ends with 'ZZZ' in Latin-1 locales + + \value Descending The items are sorted descending e.g. starts with + 'ZZZ' ends with 'AAA' in Latin-1 locales +*/ + +/*! + Sets the \a column the list view is sorted by. + + Sorting is triggered by choosing a header section. +*/ + +void QListView::changeSortColumn( int column ) +{ + if ( isRenaming() ) { + if ( d->defRenameAction == QListView::Reject ) { + currentItem()->cancelRename( currentItem()->renameCol ); + } else { + currentItem()->okRename( currentItem()->renameCol ); + } + } + if ( d->sortcolumn != Unsorted ) { + int lcol = d->h->mapToLogical( column ); + setSorting( lcol, d->sortcolumn == lcol ? !d->ascending : TRUE); + } +} + +/*! + \internal + Handles renaming when sections are being swapped by the user. +*/ + +void QListView::handleIndexChange() +{ + if ( isRenaming() ) { + if ( d->defRenameAction == QListView::Reject ) { + currentItem()->cancelRename( currentItem()->renameCol ); + } else { + currentItem()->okRename( currentItem()->renameCol ); + } + } + triggerUpdate(); +} + +/*! + Returns the column by which the list view is sorted, or -1 if + sorting is disabled. + + \sa sortOrder() +*/ + +int QListView::sortColumn() const +{ + return d->sortcolumn == Unsorted ? -1 : d->sortcolumn; +} + +/*! + Sets the sorting column for the list view. + + If \a column is -1, sorting is disabled and the user cannot sort + columns by clicking on the column headers. If \a column is larger + than the number of columns the user must click on a column header + to sort the list view. + + \sa setSorting() +*/ +void QListView::setSortColumn( int column ) +{ + setSorting( column, d->ascending ); +} + +/*! + Returns the sorting order of the list view items. + + \sa sortColumn() +*/ +Qt::SortOrder QListView::sortOrder() const +{ + if ( d->ascending ) + return Ascending; + return Descending; +} + +/*! + Sets the sort order for the items in the list view to \a order. + + \sa setSorting() +*/ +void QListView::setSortOrder( SortOrder order ) +{ + setSorting( d->sortcolumn, order == Ascending ? TRUE : FALSE ); +} + +/*! + Sorts the list view using the last sorting configuration (sort + column and ascending/descending). +*/ + +void QListView::sort() +{ + if ( d->r ) + d->r->sort(); +} + +/*! + \property QListView::itemMargin + \brief the advisory item margin that list items may use + + The item margin defaults to one pixel and is the margin between + the item's edges and the area where it draws its contents. + QListViewItem::paintFocus() draws in the margin. + + \sa QListViewItem::paintCell() +*/ + +void QListView::setItemMargin( int m ) +{ + if ( d->margin == m ) + return; + d->margin = m; + if ( isVisible() ) { + if ( d->drawables ) + d->drawables->clear(); + triggerUpdate(); + } +} + +int QListView::itemMargin() const +{ + return d->margin; +} + + +/*! + \fn void QListView::rightButtonClicked( QListViewItem *, const QPoint&, int ) + + This signal is emitted when the right button is clicked (i.e. when + it's released). The arguments are the relevant QListViewItem (may + be 0), the point in global coordinates and the relevant column (or + -1 if the click was outside the list). +*/ + + +/*! + \fn void QListView::rightButtonPressed (QListViewItem *, const QPoint &, int) + + This signal is emitted when the right button is pressed. The + arguments are the relevant QListViewItem (may be 0), the point in + global coordinates and the relevant column (or -1 if the click was + outside the list). +*/ + +/*! + \fn void QListView::contextMenuRequested( QListViewItem *item, const QPoint & pos, int col ) + + This signal is emitted when the user invokes a context menu with + the right mouse button or with special system keys. If the + keyboard was used \a item is the current item; if the mouse was + used, \a item is the item under the mouse pointer or 0 if there is + no item under the mouse pointer. If no item is clicked, the column + index emitted is -1. + + \a pos is the position for the context menu in the global + coordinate system. + + \a col is the column on which the user pressed, or -1 if the + signal was triggered by a key event. +*/ + +/*! + \reimp +*/ +void QListView::styleChange( QStyle& old ) +{ + QScrollView::styleChange( old ); + reconfigureItems(); +} + + +/*! + \reimp +*/ +void QListView::setFont( const QFont & f ) +{ + QScrollView::setFont( f ); + reconfigureItems(); +} + + +/*! + \reimp +*/ +void QListView::setPalette( const QPalette & p ) +{ + QScrollView::setPalette( p ); + reconfigureItems(); +} + + +/*! + Ensures that setup() is called for all currently visible items, + and that it will be called for currently invisible items as soon + as their parents are opened. + + (A visible item, here, is an item whose parents are all open. The + item may happen to be off-screen.) + + \sa QListViewItem::setup() +*/ + +void QListView::reconfigureItems() +{ + d->fontMetricsHeight = fontMetrics().height(); + d->minLeftBearing = fontMetrics().minLeftBearing(); + d->minRightBearing = fontMetrics().minRightBearing(); + d->ellipsisWidth = fontMetrics().width( "..." ) * 2; + d->r->setOpen( FALSE ); + d->r->configured = FALSE; + d->r->setOpen( TRUE ); +} + +/*! + Ensures that the width mode of column \a c is updated according to + the width of \a item. +*/ + +void QListView::widthChanged( const QListViewItem* item, int c ) +{ + if ( c >= d->h->count() ) + return; + + + QFontMetrics fm = fontMetrics(); + int col = c < 0 ? 0 : c; + while ( col == c || ( c < 0 && col < d->h->count() ) ) { + if ( d->column[col]->wmode == Maximum ) { + int w = item->width( fm, this, col ); + if ( showSortIndicator() ) { + int tw = d->h->sectionSizeHint( col, fm ).width(); + tw += 40; //add space for the sort indicator + w = QMAX( w, tw ); + } + if ( col == 0 ) { + int indent = treeStepSize() * item->depth(); + if ( rootIsDecorated() ) + indent += treeStepSize(); + w += indent; + } + if ( w > columnWidth( col ) && !d->h->isStretchEnabled() && !d->h->isStretchEnabled( col ) ) { + d->updateHeader = TRUE; + setColumnWidth( col, w ); + } + } + col++; + } +} + +/*! + \property QListView::allColumnsShowFocus + \brief whether items should show keyboard focus using all columns + + If this property is TRUE all columns will show focus and selection + states, otherwise only column 0 will show focus. + + The default is FALSE. + + Setting this to TRUE if it's not necessary may cause noticeable + flicker. +*/ + +void QListView::setAllColumnsShowFocus( bool enable ) +{ + d->allColumnsShowFocus = enable; +} + +bool QListView::allColumnsShowFocus() const +{ + return d->allColumnsShowFocus; +} + + +/*! + Returns the first item in this QListView. Returns 0 if there is no + first item. + + A list view's items can be traversed using firstChild() + and nextSibling() or using a QListViewItemIterator. + + \sa itemAt() QListViewItem::itemBelow() QListViewItem::itemAbove() +*/ + +QListViewItem * QListView::firstChild() const +{ + if ( !d->r ) + return 0; + + d->r->enforceSortOrder(); + return d->r->childItem; +} + +/*! + Returns the last item in the list view tree. Returns 0 if there + are no items in the QListView. + + This function is slow because it traverses the entire tree to find + the last item. +*/ + +QListViewItem* QListView::lastItem() const +{ + QListViewItem* item = firstChild(); + if ( item ) { + while ( item->nextSibling() || item->firstChild() ) { + if ( item->nextSibling() ) + item = item->nextSibling(); + else + item = item->firstChild(); + } + } + return item; +} + +/*! + Repaints this item on the screen if it is currently visible. +*/ + +void QListViewItem::repaint() const +{ + QListView *lv = listView(); + if ( lv ) + lv->repaintItem( this ); +} + + +/*! + Repaints \a item on the screen if \a item is currently visible. + Takes care to avoid multiple repaints. +*/ + +void QListView::repaintItem( const QListViewItem * item ) const +{ + if ( !item ) + return; + d->dirtyItemTimer->start( 0, TRUE ); + if ( !d->dirtyItems ) + d->dirtyItems = new QPtrDict<void>(); + d->dirtyItems->replace( (void *)item, (void *)item ); +} + + +struct QCheckListItemPrivate +{ + QCheckListItemPrivate(): + exclusive( 0 ), + currentState( QCheckListItem::Off ), + statesDict( 0 ), + tristate( FALSE ) {} + + QCheckListItem *exclusive; + QCheckListItem::ToggleState currentState; + QPtrDict<QCheckListItem::ToggleState> *statesDict; + bool tristate; +}; + + +/*! + \class QCheckListItem + \brief The QCheckListItem class provides checkable list view items. + + \ingroup advanced + + QCheckListItems are used in \l{QListView}s to provide + \l{QListViewItem}s that are checkboxes, radio buttons or + controllers. + + Checkbox and controller check list items may be inserted at any + level in a list view. Radio button check list items must be + children of a controller check list item. + + The item can be checked or unchecked with setOn(). Its type can be + retrieved with type() and its text retrieved with text(). + + \img qlistviewitems.png List View Items + + \sa QListViewItem QListView +*/ + +// ### obscenity is warranted. + +/*! + \enum QCheckListItem::Type + + This enum type specifies a QCheckListItem's type: + + \value RadioButton + \value CheckBox + \value Controller \e obsolete (use \c RadioButtonController instead) + \value RadioButtonController + \value CheckBoxController +*/ + +/*! + \enum QCheckListItem::ToggleState + + This enum specifies a QCheckListItem's toggle state. + + \value Off + \value NoChange + \value On +*/ + + +/*! + Constructs a checkable item with parent \a parent, text \a text + and of type \a tt. Note that a \c RadioButton must be the child of a + \c RadioButtonController, otherwise it will not toggle. +*/ +QCheckListItem::QCheckListItem( QCheckListItem *parent, const QString &text, + Type tt ) + : QListViewItem( parent, text, QString::null ) +{ + myType = tt; + init(); + if ( myType == RadioButton ) { + if ( parent->type() != RadioButtonController ) + qWarning( "QCheckListItem::QCheckListItem(), radio button must be " + "child of a controller" ); + else + d->exclusive = parent; + } +} + +/*! + Constructs a checkable item with parent \a parent, which is after + \a after in the parent's list of children, and with text \a text + and of type \a tt. Note that a \c RadioButton must be the child of + a \c RadioButtonController, otherwise it will not toggle. +*/ +QCheckListItem::QCheckListItem( QCheckListItem *parent, QListViewItem *after, + const QString &text, Type tt ) + : QListViewItem( parent, after, text ) +{ + myType = tt; + init(); + if ( myType == RadioButton ) { + if ( parent->type() != RadioButtonController ) + qWarning( "QCheckListItem::QCheckListItem(), radio button must be " + "child of a controller" ); + else + d->exclusive = parent; + } +} + +/*! + Constructs a checkable item with parent \a parent, text \a text + and of type \a tt. Note that this item must \e not be a \c + RadioButton. Radio buttons must be children of a \c + RadioButtonController. +*/ +QCheckListItem::QCheckListItem( QListViewItem *parent, const QString &text, + Type tt ) + : QListViewItem( parent, text, QString::null ) +{ + myType = tt; + if ( myType == RadioButton ) { + qWarning( "QCheckListItem::QCheckListItem(), radio button must be " + "child of a QCheckListItem" ); + } + init(); +} + +/*! + Constructs a checkable item with parent \a parent, which is after + \a after in the parent's list of children, with text \a text and + of type \a tt. Note that this item must \e not be a \c + RadioButton. Radio buttons must be children of a \c + RadioButtonController. +*/ +QCheckListItem::QCheckListItem( QListViewItem *parent, QListViewItem *after, + const QString &text, Type tt ) + : QListViewItem( parent, after, text ) +{ + myType = tt; + if ( myType == RadioButton ) { + qWarning( "QCheckListItem::QCheckListItem(), radio button must be " + "child of a QCheckListItem" ); + } + init(); +} + + +/*! + Constructs a checkable item with parent \a parent, text \a text + and of type \a tt. Note that \a tt must \e not be \c RadioButton. + Radio buttons must be children of a \c RadioButtonController. +*/ +QCheckListItem::QCheckListItem( QListView *parent, const QString &text, + Type tt ) + : QListViewItem( parent, text ) +{ + myType = tt; + if ( tt == RadioButton ) + qWarning( "QCheckListItem::QCheckListItem(), radio button must be " + "child of a QCheckListItem" ); + init(); +} + +/*! + Constructs a checkable item with parent \a parent, which is after + \a after in the parent's list of children, with text \a text and + of type \a tt. Note that \a tt must \e not be \c RadioButton. + Radio buttons must be children of a \c RadioButtonController. +*/ +QCheckListItem::QCheckListItem( QListView *parent, QListViewItem *after, + const QString &text, Type tt ) + : QListViewItem( parent, after, text ) +{ + myType = tt; + if ( tt == RadioButton ) + qWarning( "QCheckListItem::QCheckListItem(), radio button must be " + "child of a QCheckListItem" ); + init(); +} + + +int QCheckListItem::RTTI = 1; + +/* \reimp */ + +int QCheckListItem::rtti() const +{ + return RTTI; +} + +/*! + Constructs a \c RadioButtonController item with parent \a parent, + text \a text and pixmap \a p. +*/ +QCheckListItem::QCheckListItem( QListView *parent, const QString &text, + const QPixmap & p ) + : QListViewItem( parent, text ) +{ + myType = RadioButtonController; + setPixmap( 0, p ); + init(); +} + +/*! + Constructs a \c RadioButtonController item with parent \a parent, + text \a text and pixmap \a p. +*/ +QCheckListItem::QCheckListItem( QListViewItem *parent, const QString &text, + const QPixmap & p ) + : QListViewItem( parent, text ) +{ + myType = RadioButtonController; + setPixmap( 0, p ); + init(); +} + +void QCheckListItem::init() +{ + d = new QCheckListItemPrivate(); + on = FALSE; // ### remove on ver 4 + if ( myType == CheckBoxController || myType == CheckBox ) { + d->statesDict = new QPtrDict<ToggleState>(101); + d->statesDict->setAutoDelete( TRUE ); + } + // CheckBoxControllers by default have tristate set to TRUE + if ( myType == CheckBoxController ) + setTristate( TRUE ); +} + +/*! + Destroys the item, and all its children to any depth, freeing up + all allocated resources. +*/ +QCheckListItem::~QCheckListItem() +{ + if ( myType == RadioButton + && d->exclusive && d->exclusive->d + && d->exclusive->d->exclusive == this ) + d->exclusive->turnOffChild(); + d->exclusive = 0; // so the children won't try to access us. + if ( d->statesDict ) + delete d->statesDict; + delete d; + d = 0; +} + +/*! + \fn QCheckListItem::Type QCheckListItem::type() const + + Returns the type of this item. +*/ + +/*! + \fn bool QCheckListItem::isOn() const + + Returns TRUE if the item is toggled on; otherwise returns FALSE. +*/ + +/*! + Sets tristate to \a b if the \c Type is either a \c CheckBoxController or + a \c CheckBox. + + \c CheckBoxControllers are tristate by default. + + \sa state() isTristate() +*/ +void QCheckListItem::setTristate( bool b ) +{ + if ( ( myType != CheckBoxController ) && ( myType != CheckBox ) ) { + qWarning( "QCheckListItem::setTristate(), has no effect on RadioButton " + "or RadioButtonController." ); + return; + } + d->tristate = b; +} + +/*! + Returns TRUE if the item is tristate; otherwise returns FALSE. + + \sa setTristate() +*/ +bool QCheckListItem::isTristate() const +{ + return d->tristate; +} + +/*! + Returns the state of the item. + + \sa QCheckListItem::ToggleState +*/ +QCheckListItem::ToggleState QCheckListItem::state() const +{ + if ( !isTristate() && internalState() == NoChange ) + return Off; + else + return d->currentState; +} + +/* + Same as the public state() except this one does not mask NoChange into Off + when tristate is disabled. +*/ +QCheckListItem::ToggleState QCheckListItem::internalState() const +{ + return d->currentState; +} + + + + +/*! + Sets the toggle state of the checklistitem to \a s. \a s can be + \c Off, \c NoChange or \c On. + + Tristate can only be enabled for \c CheckBox or \c CheckBoxController, + therefore the \c NoChange only applies to them. + + Setting the state to \c On or \c Off on a \c CheckBoxController + will recursivly set the states of its children to the same state. + + Setting the state to \c NoChange on a \c CheckBoxController will + make it recursivly recall the previous stored state of its + children. If there was no previous stored state the children are + all set to \c On. +*/ +void QCheckListItem::setState( ToggleState s ) +{ + if ( myType == CheckBoxController && state() == NoChange ) + updateStoredState( (void*) this ); + setState( s, TRUE, TRUE ); +} + +/* + Sets the toggle state of the checklistitems. \a update tells if the + controller / parent controller should be aware of these changes, \a store + tells if the parent should store its children if certain conditions arise +*/ +void QCheckListItem::setState( ToggleState s, bool update, bool store) +{ + + if ( s == internalState() ) + return; + + if ( myType == CheckBox ) { + setCurrentState( s ); + stateChange( state() ); + if ( update && parent() && parent()->rtti() == 1 + && ((QCheckListItem*)parent())->type() == CheckBoxController ) + ((QCheckListItem*)parent())->updateController( update, store ); + } else if ( myType == CheckBoxController ) { + if ( s == NoChange && childCount()) { + restoreState( (void*) this ); + } else { + QListViewItem *item = firstChild(); + int childCount = 0; + while( item ) { + if ( item->rtti() == 1 && + ( ((QCheckListItem*)item)->type() == CheckBox || + ((QCheckListItem*)item)->type() == CheckBoxController ) ) { + QCheckListItem *checkItem = (QCheckListItem*)item; + checkItem->setState( s, FALSE, FALSE ); + childCount++; + } + item = item->nextSibling(); + } + if ( update ) { + if ( childCount > 0 ) { + ToggleState oldState = internalState(); + updateController( FALSE, FALSE ); + if ( oldState != internalState() && + parent() && parent()->rtti() == 1 && + ((QCheckListItem*)parent())->type() == CheckBoxController ) + ((QCheckListItem*)parent())->updateController( update, store ); + + updateController( update, store ); + } else { + // if there are no children we simply set the CheckBoxController and update its parent + setCurrentState( s ); + stateChange( state() ); + if ( parent() && parent()->rtti() == 1 + && ((QCheckListItem*)parent())->type() == CheckBoxController ) + ((QCheckListItem*)parent())->updateController( update, store ); + } + } else { + setCurrentState( s ); + stateChange( state() ); + } + + } + } else if ( myType == RadioButton ) { + if ( s == On ) { + if ( d->exclusive && d->exclusive->d->exclusive != this ) + d->exclusive->turnOffChild(); + setCurrentState( s ); + if ( d->exclusive ) + d->exclusive->d->exclusive = this; + } else { + if ( d->exclusive && d->exclusive->d->exclusive == this ) + d->exclusive->d->exclusive = 0; + setCurrentState( Off ); + } + stateChange( state() ); + } + repaint(); +} + +/* + this function is needed becase we need to update "on" everytime + we update d->currentState. In order to retain binary compatibility + the inline function isOn() needs the "on" bool + ### should be changed in ver 4 +*/ +void QCheckListItem::setCurrentState( ToggleState s ) +{ + ToggleState old = d->currentState; + d->currentState = s; + if (d->currentState == On) + on = TRUE; + else + on = FALSE; + +#if defined(QT_ACCESSIBILITY_SUPPORT) + if ( old != d->currentState && listView() ) + QAccessible::updateAccessibility( listView()->viewport(), indexOfItem( this ), QAccessible::StateChanged ); +#else + Q_UNUSED( old ); +#endif +} + + + +/* + updates the internally stored state of this item for the parent (key) +*/ +void QCheckListItem::setStoredState( ToggleState newState, void *key ) +{ + if ( myType == CheckBox || myType == CheckBoxController ) + d->statesDict->replace( key, new ToggleState(newState) ); +} + +/* + Returns the stored state for this item for the given key. + If the key is not found it returns Off. +*/ +QCheckListItem::ToggleState QCheckListItem::storedState( void *key ) const +{ + if ( !d->statesDict ) + return Off; + + ToggleState *foundState = d->statesDict->find( key ); + if ( foundState ) + return ToggleState( *foundState ); + else + return Off; +} + + +/*! + \fn QString QCheckListItem::text() const + + Returns the item's text. +*/ + + +/*! + If this is a \c RadioButtonController that has \c RadioButton + children, turn off the child that is on. +*/ +void QCheckListItem::turnOffChild() +{ + if ( myType == RadioButtonController && d->exclusive ) + d->exclusive->setOn( FALSE ); +} + +/*! + Toggle check box or set radio button to on. +*/ +void QCheckListItem::activate() +{ + QListView * lv = listView(); + + if ( lv && !lv->isEnabled() || !isEnabled() ) + return; + + QPoint pos; + int boxsize = lv->style().pixelMetric(QStyle::PM_CheckListButtonSize, lv); + if ( activatedPos( pos ) ) { + bool parentControl = FALSE; + if ( parent() && parent()->rtti() == 1 && + ((QCheckListItem*) parent())->type() == RadioButtonController ) + parentControl = TRUE; + + int x = parentControl ? 0 : 3; + int align = lv->columnAlignment( 0 ); + int marg = lv->itemMargin(); + int y = 0; + + if ( align & AlignVCenter ) + y = ( ( height() - boxsize ) / 2 ) + marg; + else + y = (lv->fontMetrics().height() + 2 + marg - boxsize) / 2; + + QRect r( x, y, boxsize-3, boxsize-3 ); + // columns might have been swapped + r.moveBy( lv->header()->sectionPos( 0 ), 0 ); + if ( !r.contains( pos ) ) + return; + } + if ( ( myType == CheckBox ) || ( myType == CheckBoxController) ) { + switch ( internalState() ) { + case On: + setState( Off ); + break; + case Off: + if ( (!isTristate() && myType == CheckBox) || + (myType == CheckBoxController && !childCount()) ) { + setState( On ); + } else { + setState( NoChange ); + if ( myType == CheckBoxController && internalState() != NoChange ) + setState( On ); + } + break; + case NoChange: + setState( On ); + break; + } + ignoreDoubleClick(); + } else if ( myType == RadioButton ) { + setOn( TRUE ); + ignoreDoubleClick(); + } +} + +/*! + Sets the button on if \a b is TRUE, otherwise sets it off. + Maintains radio button exclusivity. +*/ +void QCheckListItem::setOn( bool b ) +{ + if ( b ) + setState( On , TRUE, TRUE ); + else + setState( Off , TRUE, TRUE ); +} + + +/*! + This virtual function is called when the item changes its state. + \c NoChange (if tristate is enabled and the type is either \c + CheckBox or \c CheckBoxController) reports the same as \c Off, so + use state() to determine if the state is actually \c Off or \c + NoChange. +*/ +void QCheckListItem::stateChange( bool ) +{ +} + +/* + Calls the public virtual function if the state is changed to either On, NoChange or Off. + NoChange reports the same as Off - ### should be fixed in ver4 +*/ +void QCheckListItem::stateChange( ToggleState s ) +{ + stateChange( s == On ); +} + +/* + sets the state of the CheckBox and CheckBoxController back to + previous stored state +*/ +void QCheckListItem::restoreState( void *key, int depth ) +{ + switch ( type() ) { + case CheckBox: + setCurrentState( storedState( key ) ); + stateChange( state() ); + repaint(); + break; + case CheckBoxController: { + QListViewItem *item = firstChild(); + int childCount = 0; + while ( item ) { + // recursively calling restoreState for children of type CheckBox and CheckBoxController + if ( item->rtti() == 1 && + ( ((QCheckListItem*)item)->type() == CheckBox || + ((QCheckListItem*)item)->type() == CheckBoxController ) ) { + ((QCheckListItem*)item)->restoreState( key , depth+1 ); + childCount++; + } + item = item->nextSibling(); + } + if ( childCount > 0 ) { + if ( depth == 0 ) + updateController( TRUE ); + else + updateController( FALSE ); + } else { + // if there are no children we retrieve the CheckBoxController state directly. + setState( storedState( key ), TRUE, FALSE ); + } + } + break; + default: + break; + } +} + + +/* + Checks the childrens state and updates the controllers state + if necessary. If the controllers state change, then his parent again is + called to update itself. +*/ +void QCheckListItem::updateController( bool update , bool store ) +{ + if ( myType != CheckBoxController ) + return; + + QCheckListItem *controller = 0; + // checks if this CheckBoxController has another CheckBoxController as parent + if ( parent() && parent()->rtti() == 1 + && ((QCheckListItem*)parent())->type() == CheckBoxController ) + controller = (QCheckListItem*)parent(); + + ToggleState theState = Off; + bool first = TRUE; + QListViewItem *item = firstChild(); + while( item && theState != NoChange ) { + if ( item->rtti() == 1 && + ( ((QCheckListItem*)item)->type() == CheckBox || + ((QCheckListItem*)item)->type() == CheckBoxController ) ) { + QCheckListItem *checkItem = (QCheckListItem*)item; + if ( first ) { + theState = checkItem->internalState(); + first = FALSE; + } else { + if ( checkItem->internalState() == NoChange || + theState != checkItem->internalState() ) + theState = NoChange; + else + theState = checkItem->internalState(); + } + } + item = item->nextSibling(); + } + if ( internalState() != theState ) { + setCurrentState( theState ); + if ( store && ( internalState() == On || internalState() == Off ) ) + updateStoredState( (void*) this ); + stateChange( state() ); + if ( update && controller ) { + controller->updateController( update, store ); + } + repaint(); + } +} + + +/* + Makes all the children CheckBoxes update their storedState +*/ +void QCheckListItem::updateStoredState( void *key ) +{ + if ( myType != CheckBoxController ) + return; + + QListViewItem *item = firstChild(); + while( item ) { + if ( item->rtti() == 1 ) { + QCheckListItem *checkItem = (QCheckListItem*)item; + if ( checkItem->type() == CheckBox ) + checkItem->setStoredState( checkItem->internalState(), key ); + else if (checkItem->type() == CheckBoxController ) + checkItem->updateStoredState( key ); + } + item = item->nextSibling(); + } + // this state is only needed if the CheckBoxController has no CheckBox / CheckBoxController children. + setStoredState( internalState() , key ); +} + + +/*! + \reimp +*/ +void QCheckListItem::setup() +{ + QListViewItem::setup(); + int h = height(); + QListView *lv = listView(); + if ( lv ) + h = QMAX( lv->style().pixelMetric(QStyle::PM_CheckListButtonSize, lv), + h ); + h = QMAX( h, QApplication::globalStrut().height() ); + setHeight( h ); +} + +/*! + \reimp +*/ + +int QCheckListItem::width( const QFontMetrics& fm, const QListView* lv, int column) const +{ + int r = QListViewItem::width( fm, lv, column ); + if ( column == 0 ) { + r += lv->itemMargin(); + if ( myType == RadioButtonController && pixmap( 0 ) ) { + // r += 0; + } else { + r += lv->style().pixelMetric(QStyle::PM_CheckListButtonSize, lv) + 4; + } + } + return QMAX( r, QApplication::globalStrut().width() ); +} + +/*! + Paints the item using the painter \a p and the color group \a cg. + The item is in column \a column, has width \a width and has + alignment \a align. (See Qt::AlignmentFlags for valid alignments.) +*/ +void QCheckListItem::paintCell( QPainter * p, const QColorGroup & cg, + int column, int width, int align ) +{ + if ( !p ) + return; + + QListView *lv = listView(); + if ( !lv ) + return; + + const BackgroundMode bgmode = lv->viewport()->backgroundMode(); + const QColorGroup::ColorRole crole = QPalette::backgroundRoleFromMode( bgmode ); + if ( cg.brush( crole ) != lv->colorGroup().brush( crole ) ) + p->fillRect( 0, 0, width, height(), cg.brush( crole ) ); + else + lv->paintEmptyArea( p, QRect( 0, 0, width, height() ) ); + + if ( column != 0 ) { + // The rest is text, or for subclasses to change. + QListViewItem::paintCell( p, cg, column, width, align ); + return; + } + + bool parentControl = FALSE; + if ( parent() && parent()->rtti() == 1 && + ((QCheckListItem*) parent())->type() == RadioButtonController ) + parentControl = TRUE; + + QFontMetrics fm( lv->fontMetrics() ); + int boxsize = lv->style().pixelMetric( myType == RadioButtonController ? QStyle::PM_CheckListControllerSize : + QStyle::PM_CheckListButtonSize, lv); + int marg = lv->itemMargin(); + int r = marg; + + // Draw controller / checkbox / radiobutton --------------------- + int styleflags = QStyle::Style_Default; + if ( internalState() == On ) { + styleflags |= QStyle::Style_On; + } else if ( internalState() == NoChange ) { + if ( myType == CheckBoxController && !isTristate() ) + styleflags |= QStyle::Style_Off; + else + styleflags |= QStyle::Style_NoChange; + } else { + styleflags |= QStyle::Style_Off; + } + if ( isSelected() ) + styleflags |= QStyle::Style_Selected; + if ( isEnabled() && lv->isEnabled() ) + styleflags |= QStyle::Style_Enabled; + + if ( myType == RadioButtonController ) { + int x = 0; + if(!parentControl) + x += 3; + if ( !pixmap( 0 ) ) { + lv->style().drawPrimitive(QStyle::PE_CheckListController, p, + QRect(x, 0, boxsize, + fm.height() + 2 + marg), + cg, styleflags, QStyleOption(this)); + r += boxsize + 4; + } + } else { + Q_ASSERT( lv ); //### + int x = 0; + int y = 0; + if ( !parentControl ) + x += 3; + if ( align & AlignVCenter ) + y = ( ( height() - boxsize ) / 2 ) + marg; + else + y = (fm.height() + 2 + marg - boxsize) / 2; + + if ( ( myType == CheckBox ) || ( myType == CheckBoxController ) ) { + lv->style().drawPrimitive(QStyle::PE_CheckListIndicator, p, + QRect(x, y, boxsize, + fm.height() + 2 + marg), + cg, styleflags, QStyleOption(this)); + } else { //radio button look + lv->style().drawPrimitive(QStyle::PE_CheckListExclusiveIndicator, + p, QRect(x, y, boxsize, + fm.height() + 2 + marg), + cg, styleflags, QStyleOption(this)); + } + r += boxsize + 4; + } + + // Draw text ---------------------------------------------------- + p->translate( r, 0 ); + p->setPen( QPen( cg.text() ) ); + QListViewItem::paintCell( p, cg, column, width - r, align ); +} + +/*! + Draws the focus rectangle \a r using the color group \a cg on the + painter \a p. +*/ +void QCheckListItem::paintFocus( QPainter *p, const QColorGroup & cg, + const QRect & r ) +{ + bool intersect = TRUE; + QListView *lv = listView(); + if ( lv && lv->header()->mapToActual( 0 ) != 0 ) { + int xdepth = lv->treeStepSize() * ( depth() + ( lv->rootIsDecorated() ? 1 : 0) ) + lv->itemMargin(); + int p = lv->header()->cellPos( lv->header()->mapToActual( 0 ) ); + xdepth += p; + intersect = r.intersects( QRect( p, r.y(), xdepth - p + 1, r.height() ) ); + } + bool parentControl = FALSE; + if ( parent() && parent()->rtti() == 1 && + ((QCheckListItem*) parent())->type() == RadioButtonController ) + parentControl = TRUE; + if ( myType != RadioButtonController && intersect && + (lv->rootIsDecorated() || myType == RadioButton || + (myType == CheckBox && parentControl) ) ) { + QRect rect; + int boxsize = lv->style().pixelMetric(QStyle::PM_CheckListButtonSize, lv); + if ( lv->columnAlignment(0) == AlignCenter ) { + QFontMetrics fm( lv->font() ); + int bx = (lv->columnWidth(0) - (boxsize + fm.width(text())))/2 + boxsize; + if ( bx < 0 ) bx = 0; + rect.setRect( r.x() + bx + 5, r.y(), r.width() - bx - 5, + r.height() ); + } else + rect.setRect( r.x() + boxsize + 5, r.y(), r.width() - boxsize - 5, + r.height() ); + QListViewItem::paintFocus(p, cg, rect); + } else { + QListViewItem::paintFocus(p, cg, r); + } +} + +/*! + \reimp +*/ +QSize QListView::sizeHint() const +{ + if ( cachedSizeHint().isValid() ) + return cachedSizeHint(); + + constPolish(); + + if ( !isVisible() && (!d->drawables || d->drawables->isEmpty()) ) + // force the column widths to sanity, if possible + buildDrawableList(); + + QSize s( d->h->sizeHint() ); + if ( verticalScrollBar()->isVisible() ) + s.setWidth( s.width() + style().pixelMetric(QStyle::PM_ScrollBarExtent) ); + s += QSize(frameWidth()*2,frameWidth()*2); + QListViewItem * l = d->r; + while( l && !l->height() ) + l = l->childItem ? l->childItem : l->siblingItem; + + if ( l && l->height() ) + s.setHeight( s.height() + 10 * l->height() ); + else + s.setHeight( s.height() + 140 ); + + if ( s.width() > s.height() * 3 ) + s.setHeight( s.width() / 3 ); + else if ( s.width() *3 < s.height() ) + s.setHeight( s.width() * 3 ); + + setCachedSizeHint( s ); + + return s; +} + + +/*! + \reimp +*/ + +QSize QListView::minimumSizeHint() const +{ + return QScrollView::minimumSizeHint(); +} + + +/*! + Sets \a item to be open if \a open is TRUE and \a item is + expandable, and to be closed if \a open is FALSE. Repaints + accordingly. + + \sa QListViewItem::setOpen() QListViewItem::setExpandable() +*/ + +void QListView::setOpen( QListViewItem * item, bool open ) +{ + if ( !item || + item->isOpen() == open || + (open && !item->childCount() && !item->isExpandable()) ) + return; + + QListViewItem* nextParent = 0; + if ( open ) + nextParent = item->itemBelow(); + + item->setOpen( open ); + + if ( open ) { + QListViewItem* lastChild = item; + QListViewItem* tmp; + while ( TRUE ) { + tmp = lastChild->itemBelow(); + if ( !tmp || tmp == nextParent ) + break; + lastChild = tmp; + } + ensureItemVisible( lastChild ); + ensureItemVisible( item ); + } + if ( d->drawables ) + d->drawables->clear(); + buildDrawableList(); + + QListViewPrivate::DrawableItem * c = d->drawables->first(); + + while( c && c->i && c->i != item ) + c = d->drawables->next(); + + if ( c && c->i == item ) { + d->dirtyItemTimer->start( 0, TRUE ); + if ( !d->dirtyItems ) + d->dirtyItems = new QPtrDict<void>(); + while( c && c->i ) { + d->dirtyItems->insert( (void *)(c->i), (void *)(c->i) ); + c = d->drawables->next(); + } + } +} + + +/*! + Identical to \a{item}->isOpen(). Provided for completeness. + + \sa setOpen() +*/ + +bool QListView::isOpen( const QListViewItem * item ) const +{ + return item->isOpen(); +} + + +/*! + \property QListView::rootIsDecorated + \brief whether the list view shows open/close signs on root items + + Open/close signs are small <b>+</b> or <b>-</b> symbols in windows + style, or arrows in Motif style. The default is FALSE. +*/ + +void QListView::setRootIsDecorated( bool enable ) +{ + if ( enable != (bool)d->rootIsExpandable ) { + d->rootIsExpandable = enable; + if ( isVisible() ) + triggerUpdate(); + } +} + +bool QListView::rootIsDecorated() const +{ + return d->rootIsExpandable; +} + + +/*! + Ensures that item \a i is visible, scrolling the list view + vertically if necessary and opening (expanding) any parent items + if this is required to show the item. + + \sa itemRect() QScrollView::ensureVisible() +*/ + +void QListView::ensureItemVisible( const QListViewItem * i ) +{ + if ( !i || !i->isVisible() ) + return; + + QListViewItem *parent = i->parent(); + while ( parent ) { + if ( !parent->isOpen() ) + parent->setOpen( TRUE ); + parent = parent->parent(); + } + + if ( d->r->maybeTotalHeight < 0 ) + updateGeometries(); + int y = itemPos( i ); + int h = i->height(); + if ( isVisible() && y + h > contentsY() + visibleHeight() ) + setContentsPos( contentsX(), y - visibleHeight() + h ); + else if ( !isVisible() || y < contentsY() ) + setContentsPos( contentsX(), y ); +} + + +/*! + \fn QString QCheckListItem::text( int n ) const + + \reimp +*/ + +/*! + Returns the QHeader object that manages this list view's columns. + Please don't modify the header behind the list view's back. + + You may safely call QHeader::setClickEnabled(), + QHeader::setResizeEnabled(), QHeader::setMovingEnabled(), + QHeader::hide() and all the const QHeader functions. +*/ + +QHeader * QListView::header() const +{ + return d->h; +} + + +/*! + \property QListView::childCount + \brief the number of parentless (top-level) QListViewItem objects in this QListView + + Holds the current number of parentless (top-level) QListViewItem + objects in this QListView. + + \sa QListViewItem::childCount() +*/ + +int QListView::childCount() const +{ + if ( d->r ) + return d->r->childCount(); + return 0; +} + + +/* + Moves this item to just after \a olderSibling. \a olderSibling and + this object must have the same parent. + + If you need to move an item in the hierarchy use takeItem() and + insertItem(). +*/ + +void QListViewItem::moveToJustAfter( QListViewItem * olderSibling ) +{ + if ( parentItem && olderSibling && + olderSibling->parentItem == parentItem && olderSibling != this ) { + if ( parentItem->childItem == this ) { + parentItem->childItem = siblingItem; + } else { + QListViewItem * i = parentItem->childItem; + while( i && i->siblingItem != this ) + i = i->siblingItem; + if ( i ) + i->siblingItem = siblingItem; + } + siblingItem = olderSibling->siblingItem; + olderSibling->siblingItem = this; + parentItem->lsc = Unsorted; + } +} + +/*! + Move the item to be after item \a after, which must be one of the + item's siblings. To move an item in the hierarchy, use takeItem() + and insertItem(). + + Note that this function will have no effect if sorting is enabled + in the list view. +*/ + +void QListViewItem::moveItem( QListViewItem *after ) +{ + if ( !after || after == this ) + return; + if ( parent() != after->parent() ) { + if ( parentItem ) + parentItem->takeItem( this ); + if ( after->parentItem ) { + int tmpLsc = after->parentItem->lsc; + after->parentItem->insertItem( this ); + after->parentItem->lsc = tmpLsc; + } + } + moveToJustAfter( after ); + QListView *lv = listView(); + if ( lv ) + lv->triggerUpdate(); +} + +/* + Recursively sorts items, from the root to this item. + (enforceSortOrder() won't work the other way around, as + documented.) +*/ +void QListViewItem::enforceSortOrderBackToRoot() +{ + if ( parentItem ) { + parentItem->enforceSortOrderBackToRoot(); + parentItem->enforceSortOrder(); + } +} + +/*! + \reimp +*/ +void QListView::showEvent( QShowEvent * ) +{ + if ( d->drawables ) + d->drawables->clear(); + delete d->dirtyItems; + d->dirtyItems = 0; + d->dirtyItemTimer->stop(); + d->fullRepaintOnComlumnChange = TRUE; + + updateGeometries(); +} + + +/*! + Returns the y coordinate of this item in the list view's + coordinate system. This function is normally much slower than + QListView::itemAt(), but it works for all items whereas + QListView::itemAt() normally only works for items on the screen. + + \sa QListView::itemAt() QListView::itemRect() QListView::itemPos() +*/ + +int QListViewItem::itemPos() const +{ + QPtrStack<QListViewItem> s; + QListViewItem * i = (QListViewItem *)this; + while( i ) { + s.push( i ); + i = i->parentItem; + } + + int a = 0; + QListViewItem * p = 0; + while( s.count() ) { + i = s.pop(); + if ( p ) { + if ( !p->configured ) { + p->configured = TRUE; + p->setup(); // ### virtual non-const function called in const + } + a += p->height(); + QListViewItem * s = p->firstChild(); + while( s && s != i ) { + a += s->totalHeight(); + s = s->nextSibling(); + } + } + p = i; + } + return a; +} + + +/*! + \fn void QListView::removeItem( QListViewItem * ) + \obsolete + + This function has been renamed takeItem(). +*/ + +/*! + Removes item \a i from the list view; \a i must be a top-level + item. The warnings regarding QListViewItem::takeItem() apply to + this function, too. + + \sa insertItem() +*/ +void QListView::takeItem( QListViewItem * i ) +{ + if ( d->r ) + d->r->takeItem( i ); +} + + +void QListView::openFocusItem() +{ + d->autoopenTimer->stop(); + if ( d->focusItem && !d->focusItem->isOpen() ) { + d->focusItem->setOpen( TRUE ); + d->focusItem->repaint(); + } +} + +static const int autoopenTime = 750; + +#ifndef QT_NO_DRAGANDDROP + +/*! \reimp */ + +void QListView::contentsDragEnterEvent( QDragEnterEvent *e ) +{ + d->oldFocusItem = d->focusItem; + QListViewItem *i = d->focusItem; + d->focusItem = itemAt( contentsToViewport( e->pos() ) ); + if ( i ) + i->repaint(); + if ( d->focusItem ) { + d->autoopenTimer->start( autoopenTime ); + d->focusItem->dragEntered(); + d->focusItem->repaint(); + } + if ( i && i->dropEnabled() && i->acceptDrop( e ) || acceptDrops() ) + e->accept(); + else + e->ignore(); +} + +/*! \reimp */ + +void QListView::contentsDragMoveEvent( QDragMoveEvent *e ) +{ + QListViewItem *i = d->focusItem; + d->focusItem = itemAt( contentsToViewport( e->pos() ) ); + if ( i ) { + if ( i != d->focusItem ) + i->dragLeft(); + i->repaint(); + } + if ( d->focusItem ) { + if ( i != d->focusItem ) { + d->focusItem->dragEntered(); + d->autoopenTimer->stop(); + d->autoopenTimer->start( autoopenTime ); + } + d->focusItem->repaint(); + } else { + d->autoopenTimer->stop(); + } + if ( i && i->dropEnabled() && i->acceptDrop( e ) || acceptDrops() ) + e->accept(); + else + e->ignore(); +} + +/*! \reimp */ + +void QListView::contentsDragLeaveEvent( QDragLeaveEvent * ) +{ + d->autoopenTimer->stop(); + + if ( d->focusItem ) + d->focusItem->dragLeft(); + + setCurrentItem( d->oldFocusItem ); + d->oldFocusItem = 0; +} + +/*! \reimp */ + +void QListView::contentsDropEvent( QDropEvent *e ) +{ + d->autoopenTimer->stop(); + + setCurrentItem( d->oldFocusItem ); + QListViewItem *i = itemAt( contentsToViewport( e->pos() ) ); + if ( i && i->dropEnabled() && i->acceptDrop( e ) ) { + i->dropped( e ); + e->accept(); + } else if ( acceptDrops() ) { + emit dropped( e ); + e->accept(); + } +} + +/*! + If the user presses the mouse on an item and starts moving the + mouse, and the item allow dragging (see + QListViewItem::setDragEnabled()), this function is called to get a + drag object and a drag is started unless dragObject() returns 0. + + By default this function returns 0. You should reimplement it and + create a QDragObject depending on the selected items. +*/ + +QDragObject *QListView::dragObject() +{ + return 0; +} + +/*! + Starts a drag. +*/ + +void QListView::startDrag() +{ + if ( !d->startDragItem ) + return; + + d->startDragItem = 0; + d->buttonDown = FALSE; + + QDragObject *drag = dragObject(); + if ( !drag ) + return; + + drag->drag(); +} + +#endif // QT_NO_DRAGANDDROP + +/*! + \property QListView::defaultRenameAction + \brief What action to perform when the editor loses focus during renaming + + If this property is \c Accept, and the user renames an item and + the editor loses focus (without the user pressing Enter), the + item will still be renamed. If the property's value is \c Reject, + the item will not be renamed unless the user presses Enter. The + default is \c Reject. +*/ + +void QListView::setDefaultRenameAction( RenameAction a ) +{ + d->defRenameAction = a; +} + +QListView::RenameAction QListView::defaultRenameAction() const +{ + return d->defRenameAction; +} + +/*! + Returns TRUE if an item is being renamed; otherwise returns FALSE. +*/ + +bool QListView::isRenaming() const +{ + return currentItem() && currentItem()->renameBox; +} + +/********************************************************************** + * + * Class QListViewItemIterator + * + **********************************************************************/ + + +/*! + \class QListViewItemIterator + \brief The QListViewItemIterator class provides an iterator for collections of QListViewItems. + + \ingroup advanced + + Construct an instance of a QListViewItemIterator, with either a + QListView* or a QListViewItem* as argument, to operate on the tree + of QListViewItems, starting from the argument. + + A QListViewItemIterator iterates over all the items from its + starting point. This means that it always makes the first child of + the current item the new current item. If there is no child, the + next sibling becomes the new current item; and if there is no next + sibling, the next sibling of the parent becomes current. + + The following example creates a list of all the items that have + been selected by the user, storing pointers to the items in a + QPtrList: + \code + QPtrList<QListViewItem> lst; + QListViewItemIterator it( myListView ); + while ( it.current() ) { + if ( it.current()->isSelected() ) + lst.append( it.current() ); + ++it; + } + \endcode + + An alternative approach is to use an \c IteratorFlag: + \code + QPtrList<QListViewItem> lst; + QListViewItemIterator it( myListView, QListViewItemIterator::Selected ); + while ( it.current() ) { + lst.append( it.current() ); + ++it; + } + \endcode + + A QListViewItemIterator provides a convenient and easy way to + traverse a hierarchical QListView. + + Multiple QListViewItemIterators can operate on the tree of + QListViewItems. A QListView knows about all iterators operating on + its QListViewItems. So when a QListViewItem gets removed all + iterators that point to this item are updated and point to the + following item if possible, otherwise to a valid item before the + current one or to 0. Note however that deleting the parent item of + an item that an iterator points to is not safe. + + \sa QListView, QListViewItem +*/ + +/*! + \enum QListViewItemIterator::IteratorFlag + + These flags can be passed to a QListViewItemIterator constructor + (OR-ed together if more than one is used), so that the iterator + will only iterate over items that match the given flags. + + \value Visible + \value Invisible + \value Selected + \value Unselected + \value Selectable + \value NotSelectable + \value DragEnabled + \value DragDisabled + \value DropEnabled + \value DropDisabled + \value Expandable + \value NotExpandable + \value Checked + \value NotChecked +*/ + +/*! + Constructs an empty iterator. +*/ + +QListViewItemIterator::QListViewItemIterator() + : curr( 0 ), listView( 0 ) +{ + init( 0 ); +} + +/*! + Constructs an iterator for the QListView that contains the \a + item. The current iterator item is set to point to the \a item. +*/ + +QListViewItemIterator::QListViewItemIterator( QListViewItem *item ) + : curr( item ), listView( 0 ) +{ + init( 0 ); + + if ( item ) { + item->enforceSortOrderBackToRoot(); + listView = item->listView(); + } + addToListView(); +} + +/*! + Constructs an iterator for the QListView that contains the \a item + using the flags \a iteratorFlags. The current iterator item is set + to point to \a item or the next matching item if \a item doesn't + match the flags. + + \sa QListViewItemIterator::IteratorFlag +*/ + +QListViewItemIterator::QListViewItemIterator( QListViewItem *item, int iteratorFlags ) + : curr( item ), listView( 0 ) +{ + init( iteratorFlags ); + + // go to next matching item if the current don't match + if ( curr && !matchesFlags( curr ) ) + ++( *this ); + + if ( curr ) { + curr->enforceSortOrderBackToRoot(); + listView = curr->listView(); + } + addToListView(); +} + + +/*! + Constructs an iterator for the same QListView as \a it. The + current iterator item is set to point on the current item of \a + it. +*/ + +QListViewItemIterator::QListViewItemIterator( const QListViewItemIterator& it ) + : curr( it.curr ), listView( it.listView ) +{ + init(it.d() ? it.d()->flags : 0); + + addToListView(); +} + +/*! + Constructs an iterator for the QListView \a lv. The current + iterator item is set to point on the first child (QListViewItem) + of \a lv. +*/ + +QListViewItemIterator::QListViewItemIterator( QListView *lv ) + : curr( lv->firstChild() ), listView( lv ) +{ + init( 0 ); + + addToListView(); +} + +/*! + Constructs an iterator for the QListView \a lv with the flags \a + iteratorFlags. The current iterator item is set to point on the + first child (QListViewItem) of \a lv that matches the flags. + + \sa QListViewItemIterator::IteratorFlag +*/ + +QListViewItemIterator::QListViewItemIterator( QListView *lv, int iteratorFlags ) + : curr ( lv->firstChild() ), listView( lv ) +{ + init( iteratorFlags ); + + addToListView(); + if ( !matchesFlags( curr ) ) + ++( *this ); +} + + + +/*! + Assignment. Makes a copy of \a it and returns a reference to its + iterator. +*/ + +QListViewItemIterator &QListViewItemIterator::operator=( const QListViewItemIterator &it ) +{ + if ( listView ) { + if ( listView->d->iterators->removeRef( this ) ) { + if ( listView->d->iterators->count() == 0 ) { + delete listView->d->iterators; + listView->d->iterators = 0; + } + } + } + + listView = it.listView; + addToListView(); + curr = it.curr; + + // sets flags to be the same as the input iterators flags + if ( d() && it.d() ) + d()->flags = it.d()->flags; + + // go to next matching item if the current don't match + if ( curr && !matchesFlags( curr ) ) + ++( *this ); + + return *this; +} + +/*! + Destroys the iterator. +*/ + +QListViewItemIterator::~QListViewItemIterator() +{ + if ( listView ) { + if ( listView->d->iterators->removeRef( this ) ) { + if ( listView->d->iterators->count() == 0 ) { + delete listView->d->iterators; + listView->d->iterators = 0; + } + } + } + // removs the d-ptr from the dict ( autodelete on), and deletes the + // ptrdict if it becomes empty + if ( qt_iteratorprivate_dict ) { + qt_iteratorprivate_dict->remove( this ); + if ( qt_iteratorprivate_dict->isEmpty() ) { + delete qt_iteratorprivate_dict; + qt_iteratorprivate_dict = 0; + } + } +} + +/*! + Prefix ++. Makes the next item the new current item and returns + it. Returns 0 if the current item is the last item or the + QListView is 0. +*/ + +QListViewItemIterator &QListViewItemIterator::operator++() +{ + do { + if ( !curr ) + return *this; + + QListViewItem *item = curr->firstChild(); + if ( !item ) { + while ( (item = curr->nextSibling()) == 0 ) { + curr = curr->parent(); + if ( curr == 0 ) + break; + } + } + curr = item; + // if the next one doesn't match the flags we try one more ahead + } while ( curr && !matchesFlags( curr ) ); + return *this; +} + +/*! + \overload + + Postfix ++. Makes the next item the new current item and returns + the item that \e was the current item. +*/ + +const QListViewItemIterator QListViewItemIterator::operator++( int ) +{ + QListViewItemIterator oldValue = *this; + ++( *this ); + return oldValue; +} + +/*! + Sets the current item to the item \a j positions after the current + item. If that item is beyond the last item, the current item is + set to 0. Returns the current item. +*/ + +QListViewItemIterator &QListViewItemIterator::operator+=( int j ) +{ + while ( curr && j-- ) + ++( *this ); + + return *this; +} + +/*! + Prefix --. Makes the previous item the new current item and + returns it. Returns 0 if the current item is the first item or the + QListView is 0. +*/ + +QListViewItemIterator &QListViewItemIterator::operator--() +{ + if ( !curr ) + return *this; + + if ( !curr->parent() ) { + // we are in the first depth + if ( curr->listView() ) { + if ( curr->listView()->firstChild() != curr ) { + // go the previous sibling + QListViewItem *i = curr->listView()->firstChild(); + while ( i && i->siblingItem != curr ) + i = i->siblingItem; + + curr = i; + + if ( i && i->firstChild() ) { + // go to the last child of this item + QListViewItemIterator it( curr->firstChild() ); + for ( ; it.current() && it.current()->parent(); ++it ) + curr = it.current(); + } + + if ( curr && !matchesFlags( curr ) ) + --( *this ); + + return *this; + } else { + //we are already the first child of the list view, so it's over + curr = 0; + return *this; + } + } else + return *this; + } else { + QListViewItem *parent = curr->parent(); + + if ( curr != parent->firstChild() ) { + // go to the previous sibling + QListViewItem *i = parent->firstChild(); + while ( i && i->siblingItem != curr ) + i = i->siblingItem; + + curr = i; + + if ( i && i->firstChild() ) { + // go to the last child of this item + QListViewItemIterator it( curr->firstChild() ); + for ( ; it.current() && it.current()->parent() != parent; ++it ) + curr = it.current(); + } + + if ( curr && !matchesFlags( curr ) ) + --( *this ); + + return *this; + } else { + // make our parent the current item + curr = parent; + + if ( curr && !matchesFlags( curr ) ) + --( *this ); + + return *this; + } + } +} + +/*! + \overload + + Postfix --. Makes the previous item the new current item and + returns the item that \e was the current item. +*/ + +const QListViewItemIterator QListViewItemIterator::operator--( int ) +{ + QListViewItemIterator oldValue = *this; + --( *this ); + return oldValue; +} + +/*! + Sets the current item to the item \a j positions before the + current item. If that item is before the first item, the current + item is set to 0. Returns the current item. +*/ + +QListViewItemIterator &QListViewItemIterator::operator-=( int j ) +{ + while ( curr && j-- ) + --( *this ); + + return *this; +} + +/*! + Dereference operator. Returns a reference to the current item. The + same as current(). +*/ + +QListViewItem* QListViewItemIterator::operator*() +{ + if ( curr != 0 && !matchesFlags( curr ) ) + qWarning( "QListViewItemIterator::operator*() curr out of sync" ); + return curr; +} + +/*! + Returns iterator's current item. +*/ + +QListViewItem *QListViewItemIterator::current() const +{ + if ( curr != 0 && !matchesFlags( curr ) ) + qWarning( "QListViewItemIterator::current() curr out of sync" ); + return curr; +} + +QListViewItemIteratorPrivate* QListViewItemIterator::d() const +{ + return qt_iteratorprivate_dict ? + qt_iteratorprivate_dict->find( (void *)this ) : 0; +} + +void QListViewItemIterator::init( int iteratorFlags ) +{ + // makes new global ptrdict if it doesn't exist + if ( !qt_iteratorprivate_dict ) { + qt_iteratorprivate_dict = new QPtrDict<QListViewItemIteratorPrivate>; + qt_iteratorprivate_dict->setAutoDelete( TRUE ); + } + + // sets flag, or inserts new QListViewItemIteratorPrivate with flag + if ( d() ) + d()->flags = iteratorFlags; + else + qt_iteratorprivate_dict->insert( this, new QListViewItemIteratorPrivate( iteratorFlags ) ); +} + + +/* + Adds this iterator to its QListView's list of iterators. +*/ + +void QListViewItemIterator::addToListView() +{ + if ( listView ) { + if ( !listView->d->iterators ) { + listView->d->iterators = new QPtrList<QListViewItemIterator>; + Q_CHECK_PTR( listView->d->iterators ); + } + listView->d->iterators->append( this ); + } +} + +/* + This function is called to notify the iterator that the current + item has been deleted, and sets the current item point to another + (valid) item or 0. +*/ + +void QListViewItemIterator::currentRemoved() +{ + if ( !curr ) return; + + if ( curr->parent() ) + curr = curr->parent(); + else if ( curr->nextSibling() ) + curr = curr->nextSibling(); + else if ( listView && listView->firstChild() && + listView->firstChild() != curr ) + curr = listView->firstChild(); + else + curr = 0; +} + +/* + returns TRUE if the item \a item matches all of the flags set for the iterator +*/ +bool QListViewItemIterator::matchesFlags( const QListViewItem *item ) const +{ + if ( !item ) + return FALSE; + + int flags = d() ? d()->flags : 0; + + if ( flags == 0 ) + return TRUE; + + if ( flags & Visible && !item->isVisible() ) + return FALSE; + if ( flags & Invisible && item->isVisible() ) + return FALSE; + if ( flags & Selected && !item->isSelected() ) + return FALSE; + if ( flags & Unselected && item->isSelected() ) + return FALSE; + if ( flags & Selectable && !item->isSelectable() ) + return FALSE; + if ( flags & NotSelectable && item->isSelectable() ) + return FALSE; + if ( flags & DragEnabled && !item->dragEnabled() ) + return FALSE; + if ( flags & DragDisabled && item->dragEnabled() ) + return FALSE; + if ( flags & DropEnabled && !item->dropEnabled() ) + return FALSE; + if ( flags & DropDisabled && item->dropEnabled() ) + return FALSE; + if ( flags & Expandable && !item->isExpandable() ) + return FALSE; + if ( flags & NotExpandable && item->isExpandable() ) + return FALSE; + if ( flags & Checked && !isChecked( item ) ) + return FALSE; + if ( flags & NotChecked && isChecked( item ) ) + return FALSE; + + return TRUE; +} + +/* + we want the iterator to check QCheckListItems as well, so we provide this convenience function + that checks if the rtti() is 1 which means QCheckListItem and if isOn is TRUE, returns FALSE otherwise. +*/ +bool QListViewItemIterator::isChecked( const QListViewItem *item ) const +{ + if ( item->rtti() == 1 ) + return ((const QCheckListItem*)item)->isOn(); + else return FALSE; +} + +void QListView::handleItemChange( QListViewItem *old, bool shift, bool control ) +{ + if ( d->selectionMode == Single ) { + // nothing + } else if ( d->selectionMode == Extended ) { + if ( shift ) { + selectRange( d->selectAnchor ? d->selectAnchor : old, + d->focusItem, FALSE, TRUE, (d->selectAnchor && !control) ? TRUE : FALSE ); + } else if ( !control ) { + bool block = signalsBlocked(); + blockSignals( TRUE ); + selectAll( FALSE ); + blockSignals( block ); + setSelected( d->focusItem, TRUE ); + } + } else if ( d->selectionMode == Multi ) { + if ( shift ) + selectRange( old, d->focusItem, TRUE, FALSE ); + } +} + +void QListView::startRename() +{ + if ( !currentItem() ) + return; + currentItem()->startRename( d->pressedColumn ); + d->buttonDown = FALSE; +} + +/* unselects items from to, including children, returns TRUE if any items were unselected */ +bool QListView::clearRange( QListViewItem *from, QListViewItem *to, bool includeFirst ) +{ + if ( !from || !to ) + return FALSE; + + // Swap + if ( from->itemPos() > to->itemPos() ) { + QListViewItem *temp = from; + from = to; + to = temp; + } + + // Start on second? + if ( !includeFirst ) { + QListViewItem *below = (from == to) ? from : from->itemBelow(); + if ( below ) + from = below; + } + + // Clear items <from, to> + bool changed = FALSE; + + QListViewItemIterator it( from ); + while ( it.current() ) { + if ( it.current()->isSelected() ) { + it.current()->setSelected( FALSE ); + changed = TRUE; + } + if ( it.current() == to ) + break; + ++it; + } + + // NOTE! This function does _not_ emit + // any signals about selection changed + return changed; +} + +void QListView::selectRange( QListViewItem *from, QListViewItem *to, bool invert, bool includeFirst, bool clearSel ) +{ + if ( !from || !to ) + return; + if ( from == to && !includeFirst ) + return; + bool swap = FALSE; + if ( to == from->itemAbove() ) + swap = TRUE; + if ( !swap && from != to && from != to->itemAbove() ) { + QListViewItemIterator it( from ); + bool found = FALSE; + for ( ; it.current(); ++it ) { + if ( it.current() == to ) { + found = TRUE; + break; + } + } + if ( !found ) + swap = TRUE; + } + if ( swap ) { + QListViewItem *i = from; + from = to; + to = i; + if ( !includeFirst ) + to = to->itemAbove(); + } else { + if ( !includeFirst ) + from = from->itemBelow(); + } + + bool changed = FALSE; + if ( clearSel ) { + QListViewItemIterator it( firstChild() ); + for ( ; it.current(); ++it ) { + if ( it.current()->selected ) { + it.current()->setSelected( FALSE ); + changed = TRUE; + } + } + it = QListViewItemIterator( to ); + for ( ; it.current(); ++it ) { + if ( it.current()->selected ) { + it.current()->setSelected( FALSE ); + changed = TRUE; + } + } + } + + for ( QListViewItem *i = from; i; i = i->itemBelow() ) { + if ( !invert ) { + if ( !i->selected && i->isSelectable() ) { + i->setSelected( TRUE ); + changed = TRUE; + } + } else { + bool sel = !i->selected; + if ( (bool)i->selected != sel && sel && i->isSelectable() || !sel ) { + i->setSelected( sel ); + changed = TRUE; + } + } + if ( i == to ) + break; + } + if ( changed ) { + d->useDoubleBuffer = TRUE; + triggerUpdate(); + emit selectionChanged(); + } +} + +/* clears selection from anchor to old, selects from anchor to new, does not emit selectionChanged on change */ +bool QListView::selectRange( QListViewItem *newItem, QListViewItem *oldItem, QListViewItem *anchorItem ) +{ + if ( !newItem || !oldItem || !anchorItem ) + return FALSE; + + int anchorPos = anchorItem ? anchorItem->itemPos() : 0, + oldPos = oldItem ? oldItem->itemPos() : 0, + newPos = newItem->itemPos(); + QListViewItem *top=0, *bottom=0; + if ( anchorPos > newPos ) { + top = newItem; + bottom = anchorItem; + } else { + top = anchorItem; + bottom = newItem; + } + + // removes the parts of the old selection that will no longer be selected + bool changed = FALSE; + int topPos = top ? top->itemPos() : 0, + bottomPos = bottom ? bottom->itemPos() : 0; + if ( !(oldPos > topPos && oldPos < bottomPos) ) { + if ( oldPos < topPos ) + changed = clearRange( oldItem, top ); + else + changed = clearRange( bottom, oldItem ); + } + + // selects the new (not already selected) items + QListViewItemIterator lit( top ); + for ( ; lit.current(); ++lit ) { + if ( (bool)lit.current()->selected != d->select ) { + lit.current()->setSelected( d->select ); + changed = TRUE; + } + // Include bottom, then break + if ( lit.current() == bottom ) + break; + } + + return changed; +} + + +/*! + Finds the first list view item in column \a column, that matches + \a text and returns the item, or returns 0 of no such item could + be found. + The search starts from the current item if the current item exists, + otherwise it starts from the first list view item. After reaching + the last item the search continues from the first item. + Pass OR-ed together \l Qt::StringComparisonMode values + in the \a compare flag, to control how the matching is performed. + The default comparison mode is case-sensitive, exact match. +*/ + +QListViewItem *QListView::findItem( const QString& text, int column, + ComparisonFlags compare ) const +{ + if (text.isEmpty() && !(compare & ExactMatch)) + return 0; + + if ( compare == CaseSensitive || compare == 0 ) + compare |= ExactMatch; + + QString itmtxt; + QString comtxt = text; + if ( !(compare & CaseSensitive) ) + comtxt = comtxt.lower(); + + QListViewItemIterator it( d->focusItem ? d->focusItem : firstChild() ); + QListViewItem *sentinel = 0; + QListViewItem *item; + QListViewItem *beginsWithItem = 0; + QListViewItem *endsWithItem = 0; + QListViewItem *containsItem = 0; + + for ( int pass = 0; pass < 2; pass++ ) { + while ( (item = it.current()) != sentinel ) { + itmtxt = item->text( column ); + if ( !(compare & CaseSensitive) ) + itmtxt = itmtxt.lower(); + + if ( compare & ExactMatch && itmtxt == comtxt ) + return item; + if ( compare & BeginsWith && !beginsWithItem && itmtxt.startsWith( comtxt ) ) + beginsWithItem = containsItem = item; + if ( compare & EndsWith && !endsWithItem && itmtxt.endsWith( comtxt ) ) + endsWithItem = containsItem = item; + if ( compare & Contains && !containsItem && itmtxt.contains( comtxt ) ) + containsItem = item; + ++it; + } + + it = QListViewItemIterator( firstChild() ); + sentinel = d->focusItem ? d->focusItem : firstChild(); + } + + // Obey the priorities + if ( beginsWithItem ) + return beginsWithItem; + else if ( endsWithItem ) + return endsWithItem; + else if ( containsItem ) + return containsItem; + return 0; +} + +/*! \reimp */ +void QListView::windowActivationChange( bool oldActive ) +{ + if ( oldActive && d->scrollTimer ) + d->scrollTimer->stop(); + if ( palette().active() != palette().inactive() ) + viewport()->update(); + QScrollView::windowActivationChange( oldActive ); +} + +/*! + Hides the column specified at \a column. This is a convenience + function that calls setColumnWidth( \a column, 0 ). + + Note: The user may still be able to resize the hidden column using + the header handles. To prevent this, call setResizeEnabled(FALSE, + \a column) on the list views header. + + \sa setColumnWidth() +*/ + +void QListView::hideColumn( int column ) +{ + setColumnWidth( column, 0 ); +} + +/*! Adjusts the column \a col to its preferred width */ + +void QListView::adjustColumn( int col ) +{ + if ( col < 0 || col > (int)d->column.count() - 1 || d->h->isStretchEnabled( col ) ) + return; + + int oldw = d->h->sectionSize( col ); + + int w = d->h->sectionSizeHint( col, fontMetrics() ).width(); + if ( d->h->iconSet( col ) ) + w += d->h->iconSet( col )->pixmap().width(); + w = QMAX( w, 20 ); + QFontMetrics fm( fontMetrics() ); + QListViewItem* item = firstChild(); + int rootDepth = rootIsDecorated() ? treeStepSize() : 0; + while ( item ) { + int iw = item->width( fm, this, col ); + if ( 0 == col ) + iw += itemMargin() + rootDepth + item->depth()*treeStepSize() - 1; + w = QMAX( w, iw ); + item = item->itemBelow(); + } + w = QMAX( w, QApplication::globalStrut().width() ); + + d->h->adjustHeaderSize( oldw - w ); + if (oldw != w) { + d->fullRepaintOnComlumnChange = TRUE; + d->h->resizeSection( col, w ); + emit d->h->sizeChange( col, oldw, w); + } +} + +#endif // QT_NO_LISTVIEW diff --git a/src/widgets/qlistview.h b/src/widgets/qlistview.h new file mode 100644 index 0000000..e5dc0e4 --- /dev/null +++ b/src/widgets/qlistview.h @@ -0,0 +1,605 @@ +/**************************************************************************** +** +** Definition of QListView widget class +** +** Created : 970809 +** +** 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. +** +**********************************************************************/ + +#ifndef QLISTVIEW_H +#define QLISTVIEW_H + +#ifndef QT_H +#include "qscrollview.h" +#endif // QT_H + +#ifndef QT_NO_LISTVIEW + + +class QPixmap; +class QFont; +class QHeader; +class QIconSet; + +class QListView; +struct QListViewPrivate; +struct QCheckListItemPrivate; +class QListViewItemIterator; +struct QListViewItemIteratorPrivate; +class QDragObject; +class QMimeSource; +class QLineEdit; +class QListViewToolTip; + +class Q_EXPORT QListViewItem : public Qt +{ + friend class QListViewItemIterator; + friend class QListViewToolTip; + +public: + QListViewItem( QListView * parent ); + QListViewItem( QListViewItem * parent ); + QListViewItem( QListView * parent, QListViewItem * after ); + QListViewItem( QListViewItem * parent, QListViewItem * after ); + + QListViewItem( QListView * parent, + QString, QString = QString::null, + QString = QString::null, QString = QString::null, + QString = QString::null, QString = QString::null, + QString = QString::null, QString = QString::null ); + QListViewItem( QListViewItem * parent, + QString, QString = QString::null, + QString = QString::null, QString = QString::null, + QString = QString::null, QString = QString::null, + QString = QString::null, QString = QString::null ); + + QListViewItem( QListView * parent, QListViewItem * after, + QString, QString = QString::null, + QString = QString::null, QString = QString::null, + QString = QString::null, QString = QString::null, + QString = QString::null, QString = QString::null ); + QListViewItem( QListViewItem * parent, QListViewItem * after, + QString, QString = QString::null, + QString = QString::null, QString = QString::null, + QString = QString::null, QString = QString::null, + QString = QString::null, QString = QString::null ); + virtual ~QListViewItem(); + + virtual void insertItem( QListViewItem * ); + virtual void takeItem( QListViewItem * ); + virtual void removeItem( QListViewItem *item ) { takeItem( item ); } //obsolete, use takeItem instead + + int height() const; + virtual void invalidateHeight(); + int totalHeight() const; + virtual int width( const QFontMetrics&, + const QListView*, int column) const; + void widthChanged(int column=-1) const; + int depth() const; + + virtual void setText( int, const QString &); + virtual QString text( int ) const; + + virtual void setPixmap( int, const QPixmap & ); + virtual const QPixmap * pixmap( int ) const; + + virtual QString key( int, bool ) const; + virtual int compare( QListViewItem *i, int col, bool ) const; + virtual void sortChildItems( int, bool ); + + int childCount() const { return nChildren; } + + bool isOpen() const { return open; } + virtual void setOpen( bool ); + virtual void setup(); + + virtual void setSelected( bool ); + bool isSelected() const { return selected; } + + virtual void paintCell( QPainter *, const QColorGroup & cg, + int column, int width, int alignment ); + virtual void paintBranches( QPainter * p, const QColorGroup & cg, + int w, int y, int h ); + virtual void paintFocus( QPainter *, const QColorGroup & cg, + const QRect & r ); + + QListViewItem * firstChild() const; + QListViewItem * nextSibling() const { return siblingItem; } + QListViewItem * parent() const; + + QListViewItem * itemAbove(); + QListViewItem * itemBelow(); + + int itemPos() const; + + QListView *listView() const; + + virtual void setSelectable( bool enable ); + bool isSelectable() const { return selectable && enabled; } + + virtual void setExpandable( bool ); + bool isExpandable() const { return expandable; } + + void repaint() const; + + virtual void sort(); + void moveItem( QListViewItem *after ); + + virtual void setDragEnabled( bool allow ); + virtual void setDropEnabled( bool allow ); + bool dragEnabled() const; + bool dropEnabled() const; + virtual bool acceptDrop( const QMimeSource *mime ) const; + + void setVisible( bool b ); + bool isVisible() const; + + virtual void setRenameEnabled( int col, bool b ); + bool renameEnabled( int col ) const; + virtual void startRename( int col ); + + virtual void setEnabled( bool b ); + bool isEnabled() const; + + virtual int rtti() const; + // ### Qt 4: make const or better use an enum + static int RTTI; + + virtual void setMultiLinesEnabled( bool b ); + bool multiLinesEnabled() const; + +protected: + virtual void enforceSortOrder() const; + virtual void setHeight( int ); + virtual void activate(); + + bool activatedPos( QPoint & ); +#ifndef QT_NO_DRAGANDDROP + virtual void dropped( QDropEvent *e ); +#endif + virtual void dragEntered(); + virtual void dragLeft(); + virtual void okRename( int col ); + virtual void cancelRename( int col ); + + void ignoreDoubleClick(); + +private: + void init(); + void moveToJustAfter( QListViewItem * ); + void enforceSortOrderBackToRoot(); + void removeRenameBox(); + + int ownHeight; + int maybeTotalHeight; + int nChildren; + + uint lsc: 14; + uint lso: 1; + uint open : 1; + uint selected : 1; + uint selectable: 1; + uint configured: 1; + uint expandable: 1; + uint is_root: 1; + uint allow_drag : 1; + uint allow_drop : 1; + uint visible : 1; + uint enabled : 1; + uint mlenabled : 1; + + QListViewItem * parentItem; + QListViewItem * siblingItem; + QListViewItem * childItem; + QLineEdit *renameBox; + int renameCol; + + void * columns; + + friend class QListView; +}; + +class QCheckListItem; + +class Q_EXPORT QListView: public QScrollView +{ + friend class QListViewItemIterator; + friend class QListViewItem; + friend class QCheckListItem; + friend class QListViewToolTip; + + Q_OBJECT + Q_ENUMS( SelectionMode ResizeMode RenameAction ) + Q_PROPERTY( int columns READ columns ) + Q_PROPERTY( bool multiSelection READ isMultiSelection WRITE setMultiSelection DESIGNABLE false ) + Q_PROPERTY( SelectionMode selectionMode READ selectionMode WRITE setSelectionMode ) + Q_PROPERTY( int childCount READ childCount ) + Q_PROPERTY( bool allColumnsShowFocus READ allColumnsShowFocus WRITE setAllColumnsShowFocus ) + Q_PROPERTY( bool showSortIndicator READ showSortIndicator WRITE setShowSortIndicator ) + Q_PROPERTY( int itemMargin READ itemMargin WRITE setItemMargin ) + Q_PROPERTY( bool rootIsDecorated READ rootIsDecorated WRITE setRootIsDecorated ) + Q_PROPERTY( bool showToolTips READ showToolTips WRITE setShowToolTips ) + Q_PROPERTY( ResizeMode resizeMode READ resizeMode WRITE setResizeMode ) + Q_PROPERTY( int treeStepSize READ treeStepSize WRITE setTreeStepSize ) + Q_PROPERTY( RenameAction defaultRenameAction READ defaultRenameAction WRITE setDefaultRenameAction ) + +public: + QListView( QWidget* parent=0, const char* name=0, WFlags f = 0 ); + ~QListView(); + + int treeStepSize() const; + virtual void setTreeStepSize( int ); + + virtual void insertItem( QListViewItem * ); + virtual void takeItem( QListViewItem * ); + virtual void removeItem( QListViewItem *item ) { takeItem( item ); } // obsolete, use takeItem instead + + QHeader * header() const; + + virtual int addColumn( const QString &label, int size = -1); + virtual int addColumn( const QIconSet& iconset, const QString &label, int size = -1); + virtual void removeColumn( int index ); + virtual void setColumnText( int column, const QString &label ); + virtual void setColumnText( int column, const QIconSet& iconset, const QString &label ); + QString columnText( int column ) const; + virtual void setColumnWidth( int column, int width ); + int columnWidth( int column ) const; + enum WidthMode { Manual, Maximum }; + virtual void setColumnWidthMode( int column, WidthMode ); + WidthMode columnWidthMode( int column ) const; + int columns() const; + + virtual void setColumnAlignment( int, int ); + int columnAlignment( int ) const; + + void show(); + + QListViewItem * itemAt( const QPoint & screenPos ) const; + QRect itemRect( const QListViewItem * ) const; + int itemPos( const QListViewItem * ); + + void ensureItemVisible( const QListViewItem * ); + + void repaintItem( const QListViewItem * ) const; + + virtual void setMultiSelection( bool enable ); + bool isMultiSelection() const; + + enum SelectionMode { Single, Multi, Extended, NoSelection }; + void setSelectionMode( SelectionMode mode ); + SelectionMode selectionMode() const; + + virtual void clearSelection(); + virtual void setSelected( QListViewItem *, bool ); + void setSelectionAnchor( QListViewItem * ); + bool isSelected( const QListViewItem * ) const; + QListViewItem * selectedItem() const; + virtual void setOpen( QListViewItem *, bool ); + bool isOpen( const QListViewItem * ) const; + + virtual void setCurrentItem( QListViewItem * ); + QListViewItem * currentItem() const; + + QListViewItem * firstChild() const; + QListViewItem * lastItem() const; + + int childCount() const; + + virtual void setAllColumnsShowFocus( bool ); + bool allColumnsShowFocus() const; + + virtual void setItemMargin( int ); + int itemMargin() const; + + virtual void setRootIsDecorated( bool ); + bool rootIsDecorated() const; + + virtual void setSorting( int column, bool ascending = TRUE ); + int sortColumn() const; + void setSortColumn( int column ); + SortOrder sortOrder() const; + void setSortOrder( SortOrder order ); + virtual void sort(); + + virtual void setFont( const QFont & ); + virtual void setPalette( const QPalette & ); + + bool eventFilter( QObject * o, QEvent * ); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + virtual void setShowSortIndicator( bool show ); + bool showSortIndicator() const; + virtual void setShowToolTips( bool b ); + bool showToolTips() const; + + enum ResizeMode { NoColumn, AllColumns, LastColumn }; + virtual void setResizeMode( ResizeMode m ); + ResizeMode resizeMode() const; + + QListViewItem * findItem( const QString& text, int column, ComparisonFlags compare = ExactMatch | CaseSensitive ) const; + + enum RenameAction { Accept, Reject }; + virtual void setDefaultRenameAction( RenameAction a ); + RenameAction defaultRenameAction() const; + bool isRenaming() const; + + void hideColumn( int column ); + +public slots: + virtual void clear(); + virtual void invertSelection(); + virtual void selectAll( bool select ); + void triggerUpdate(); + void setContentsPos( int x, int y ); + void adjustColumn( int col ); + +signals: + void selectionChanged(); + void selectionChanged( QListViewItem * ); + void currentChanged( QListViewItem * ); + void clicked( QListViewItem * ); + void clicked( QListViewItem *, const QPoint &, int ); + void pressed( QListViewItem * ); + void pressed( QListViewItem *, const QPoint &, int ); + + void doubleClicked( QListViewItem * ); + void doubleClicked( QListViewItem *, const QPoint&, int ); + void returnPressed( QListViewItem * ); + void spacePressed( QListViewItem * ); + void rightButtonClicked( QListViewItem *, const QPoint&, int ); + void rightButtonPressed( QListViewItem *, const QPoint&, int ); + void mouseButtonPressed( int, QListViewItem *, const QPoint& , int ); + void mouseButtonClicked( int, QListViewItem *, const QPoint&, int ); + + void contextMenuRequested( QListViewItem *, const QPoint &, int ); + + void onItem( QListViewItem *item ); + void onViewport(); + + void expanded( QListViewItem *item ); + void collapsed( QListViewItem *item ); +#ifndef QT_NO_DRAGANDDROP + void dropped( QDropEvent *e ); +#endif + void itemRenamed( QListViewItem *item, int col, const QString & ); + void itemRenamed( QListViewItem *item, int col ); + +protected: + void contentsMousePressEvent( QMouseEvent * e ); + void contentsMouseReleaseEvent( QMouseEvent * e ); + void contentsMouseMoveEvent( QMouseEvent * e ); + void contentsMouseDoubleClickEvent( QMouseEvent * e ); + void contentsContextMenuEvent( QContextMenuEvent * e ); +#ifndef QT_NO_DRAGANDDROP + void contentsDragEnterEvent( QDragEnterEvent *e ); + void contentsDragMoveEvent( QDragMoveEvent *e ); + void contentsDragLeaveEvent( QDragLeaveEvent *e ); + void contentsDropEvent( QDropEvent *e ); + virtual QDragObject *dragObject(); + virtual void startDrag(); +#endif + + void focusInEvent( QFocusEvent * e ); + void focusOutEvent( QFocusEvent * e ); + + void keyPressEvent( QKeyEvent *e ); + + void resizeEvent( QResizeEvent *e ); + void viewportResizeEvent( QResizeEvent *e ); + + void showEvent( QShowEvent * ); + + void drawContentsOffset( QPainter *, int ox, int oy, + int cx, int cy, int cw, int ch ); + + virtual void paintEmptyArea( QPainter *, const QRect & ); + void styleChange( QStyle& ); + void windowActivationChange( bool ); + +protected slots: + void updateContents(); + void doAutoScroll(); + +private slots: + void changeSortColumn( int ); + void handleIndexChange(); + void updateDirtyItems(); + void makeVisible(); + void handleSizeChange( int, int, int ); + void startRename(); + void openFocusItem(); + +private: + void contentsMousePressEventEx( QMouseEvent * e ); + void contentsMouseReleaseEventEx( QMouseEvent * e ); + void init(); + void updateGeometries(); + void buildDrawableList() const; + void reconfigureItems(); + void widthChanged(const QListViewItem*, int c); + void handleItemChange( QListViewItem *old, bool shift, bool control ); + void selectRange( QListViewItem *from, QListViewItem *to, bool invert, bool includeFirst, bool clearSel = FALSE ); + bool selectRange( QListViewItem *newItem, QListViewItem *oldItem, QListViewItem *anchorItem ); + bool clearRange( QListViewItem *from, QListViewItem *to, bool includeFirst = TRUE ); + void doAutoScroll( const QPoint &cursorPos ); + + QListViewPrivate * d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QListView( const QListView & ); + QListView &operator=( const QListView & ); +#endif +}; + + +class Q_EXPORT QCheckListItem : public QListViewItem +{ +public: + enum Type { RadioButton, + CheckBox, + Controller, + RadioButtonController=Controller, + CheckBoxController }; + // ### should be integrated with qbutton in ver4 perhaps + enum ToggleState { Off, NoChange, On }; + + QCheckListItem( QCheckListItem *parent, const QString &text, + Type = RadioButtonController ); + QCheckListItem( QCheckListItem *parent, QListViewItem *after, + const QString &text, Type = RadioButtonController ); + QCheckListItem( QListViewItem *parent, const QString &text, + Type = RadioButtonController ); + QCheckListItem( QListViewItem *parent, QListViewItem *after, + const QString &text, Type = RadioButtonController ); + QCheckListItem( QListView *parent, const QString &text, + Type = RadioButtonController ); + QCheckListItem( QListView *parent, QListViewItem *after, + const QString &text, Type = RadioButtonController ); + QCheckListItem( QListViewItem *parent, const QString &text, + const QPixmap & ); + QCheckListItem( QListView *parent, const QString &text, + const QPixmap & ); + ~QCheckListItem(); + + void paintCell( QPainter *, const QColorGroup & cg, + int column, int width, int alignment ); + virtual void paintFocus( QPainter *, const QColorGroup & cg, + const QRect & r ); + int width( const QFontMetrics&, const QListView*, int column) const; + void setup(); + + virtual void setOn( bool ); // ### should be replaced by setChecked in ver4 + bool isOn() const { return on; } + Type type() const { return myType; } + QString text() const { return QListViewItem::text( 0 ); } + QString text( int n ) const { return QListViewItem::text( n ); } + + void setTristate( bool ); + bool isTristate() const; + ToggleState state() const; + void setState( ToggleState s); + + int rtti() const; + static int RTTI; + +protected: + void activate(); + void turnOffChild(); + virtual void stateChange( bool ); + +private: + void init(); + ToggleState internalState() const; + void setStoredState( ToggleState newState, void *key ); + ToggleState storedState( void *key ) const; + void stateChange( ToggleState s ); + void restoreState( void *key, int depth = 0 ); + void updateController( bool update = TRUE , bool store = FALSE ); + void updateStoredState( void *key ); + void setState( ToggleState s, bool update, bool store ); + void setCurrentState( ToggleState s ); + + Type myType; + bool on; // ### remove in ver4 + QCheckListItemPrivate *d; +}; + +class Q_EXPORT QListViewItemIterator +{ + friend struct QListViewPrivate; + friend class QListView; + friend class QListViewItem; + +public: + enum IteratorFlag { + Visible = 0x00000001, + Invisible = 0x00000002, + Selected = 0x00000004, + Unselected = 0x00000008, + Selectable = 0x00000010, + NotSelectable = 0x00000020, + DragEnabled = 0x00000040, + DragDisabled = 0x00000080, + DropEnabled = 0x00000100, + DropDisabled = 0x00000200, + Expandable = 0x00000400, + NotExpandable = 0x00000800, + Checked = 0x00001000, + NotChecked = 0x00002000 + }; + + QListViewItemIterator(); + QListViewItemIterator( QListViewItem *item ); + QListViewItemIterator( QListViewItem *item, int iteratorFlags ); + + QListViewItemIterator( const QListViewItemIterator &it ); + QListViewItemIterator( QListView *lv ); + QListViewItemIterator( QListView *lv, int iteratorFlags ); + + QListViewItemIterator &operator=( const QListViewItemIterator &it ); + + ~QListViewItemIterator(); + + QListViewItemIterator &operator++(); + const QListViewItemIterator operator++( int ); + QListViewItemIterator &operator+=( int j ); + + QListViewItemIterator &operator--(); + const QListViewItemIterator operator--( int ); + QListViewItemIterator &operator-=( int j ); + + QListViewItem* operator*(); + QListViewItem *current() const; + +protected: + QListViewItem *curr; + QListView *listView; + +private: + QListViewItemIteratorPrivate* d() const; + void init( int flags ); + void addToListView(); + void currentRemoved(); + bool matchesFlags( const QListViewItem* ) const; + bool testPair( QListViewItemIterator::IteratorFlag, QListViewItemIterator::IteratorFlag, bool ) const; + bool isChecked( const QListViewItem* ) const; +}; + +#endif // QT_NO_LISTVIEW + +#endif // QLISTVIEW_H diff --git a/src/widgets/qmainwindow.cpp b/src/widgets/qmainwindow.cpp new file mode 100644 index 0000000..7ad2781 --- /dev/null +++ b/src/widgets/qmainwindow.cpp @@ -0,0 +1,2609 @@ +/**************************************************************************** +** +** Implementation of QMainWindow class +** +** Created : 980312 +** +** 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 "qmainwindow.h" +#ifndef QT_NO_MAINWINDOW + +#include "qtimer.h" +#include "qlayout.h" +#include "qobjectlist.h" +#include "qintdict.h" +#include "qapplication.h" +#include "qptrlist.h" +#include "qmap.h" +#include "qcursor.h" +#include "qpainter.h" +#include "qmenubar.h" +#include "qpopupmenu.h" +#include "qtoolbar.h" +#include "qstatusbar.h" +#include "qscrollview.h" +#include "qtooltip.h" +#include "qdatetime.h" +#include "qwhatsthis.h" +#include "qbitmap.h" +#include "qdockarea.h" +#include "qstringlist.h" +#include "qstyle.h" +#ifdef Q_WS_MACX +# include "qt_mac.h" +#endif + +class QHideDock; +class QMainWindowLayout; + +class QMainWindowPrivate +{ +public: + QMainWindowPrivate() + : mb(0), sb(0), ttg(0), mc(0), tll(0), mwl(0), ubp( FALSE ), utl( FALSE ), + justify( FALSE ), movable( TRUE ), opaque( FALSE ), dockMenu( TRUE ) + { + docks.insert( Qt::DockTop, TRUE ); + docks.insert( Qt::DockBottom, TRUE ); + docks.insert( Qt::DockLeft, TRUE ); + docks.insert( Qt::DockRight, TRUE ); + docks.insert( Qt::DockMinimized, FALSE ); + docks.insert( Qt::DockTornOff, TRUE ); + } + + ~QMainWindowPrivate() + { + } + +#ifndef QT_NO_MENUBAR + QMenuBar * mb; +#else + QWidget * mb; +#endif + QStatusBar * sb; + QToolTipGroup * ttg; + + QWidget * mc; + + QBoxLayout * tll; + QMainWindowLayout * mwl; + + uint ubp :1; + uint utl :1; + uint justify :1; + uint movable :1; + uint opaque :1; + uint dockMenu :1; + + QDockArea *topDock, *bottomDock, *leftDock, *rightDock; + + QPtrList<QDockWindow> dockWindows; + QMap<Qt::Dock, bool> docks; + QStringList disabledDocks; + QHideDock *hideDock; + + QGuardedPtr<QPopupMenu> rmbMenu, tbMenu, dwMenu; + QMap<QDockWindow*, bool> appropriate; + QMap<QPopupMenu*, QMainWindow::DockWindows> dockWindowModes; + +}; + + +/* QMainWindowLayout, respects widthForHeight layouts (like the left + and right docks are) +*/ + +class QMainWindowLayout : public QLayout +{ + Q_OBJECT + +public: + QMainWindowLayout( QMainWindow *mw, QLayout* parent = 0 ); + ~QMainWindowLayout() {} + + void addItem( QLayoutItem * ); + void setLeftDock( QDockArea *l ); + void setRightDock( QDockArea *r ); + void setCentralWidget( QWidget *w ); + bool hasHeightForWidth() const { return FALSE; } + QSize sizeHint() const; + QSize minimumSize() const; + QLayoutIterator iterator(); + QSizePolicy::ExpandData expanding() const { return QSizePolicy::BothDirections; } + void invalidate() {} + +protected: + void setGeometry( const QRect &r ) { + QLayout::setGeometry( r ); + layoutItems( r ); + } + +private: + int layoutItems( const QRect&, bool testonly = FALSE ); + int extraPixels() const; + + QDockArea *left, *right; + QWidget *central; + QMainWindow *mainWindow; + +}; + +QSize QMainWindowLayout::sizeHint() const +{ + int w = 0; + int h = 0; + + if ( left ) { + w += left->sizeHint().width(); + h = QMAX( h, left->sizeHint().height() ); + } + if ( right ) { + w += right->sizeHint().width(); + h = QMAX( h, right->sizeHint().height() ); + } + if ( central ) { + w += central->sizeHint().width(); + int diff = extraPixels(); + h = QMAX( h, central->sizeHint().height() + diff ); + } + return QSize( w, h ); +} + +QSize QMainWindowLayout::minimumSize() const +{ + int w = 0; + int h = 0; + + if ( left ) { + QSize ms = left->minimumSizeHint().expandedTo( left->minimumSize() ); + w += ms.width(); + h = QMAX( h, ms.height() ); + } + if ( right ) { + QSize ms = right->minimumSizeHint().expandedTo( right->minimumSize() ); + w += ms.width(); + h = QMAX( h, ms.height() ); + } + if ( central ) { + QSize min = central->minimumSize().isNull() ? + central->minimumSizeHint() : central->minimumSize(); + w += min.width(); + int diff = extraPixels(); + h = QMAX( h, min.height() + diff ); + } + return QSize( w, h ); +} + +QMainWindowLayout::QMainWindowLayout( QMainWindow *mw, QLayout* parent ) + : QLayout( parent ), left( 0 ), right( 0 ), central( 0 ) +{ + mainWindow = mw; +} + +void QMainWindowLayout::setLeftDock( QDockArea *l ) +{ + left = l; +} + +void QMainWindowLayout::setRightDock( QDockArea *r ) +{ + right = r; +} + +void QMainWindowLayout::setCentralWidget( QWidget *w ) +{ + central = w; +} + +int QMainWindowLayout::layoutItems( const QRect &r, bool testonly ) +{ + if ( !left && !central && !right ) + return 0; + + int wl = 0, wr = 0; + if ( left ) + wl = ( (QDockAreaLayout*)left->QWidget::layout() )->widthForHeight( r.height() ); + if ( right ) + wr = ( (QDockAreaLayout*)right->QWidget::layout() )->widthForHeight( r.height() ); + int w = r.width() - wr - wl; + if ( w < 0 ) + w = 0; + + int diff = extraPixels(); + if ( !testonly ) { + QRect g( geometry() ); + if ( left ) + left->setGeometry( QRect( g.x(), g.y() + diff, wl, r.height() - diff ) ); + if ( right ) + right->setGeometry( QRect( g.x() + g.width() - wr, g.y() + diff, wr, r.height() - diff ) ); + if ( central ) + central->setGeometry( g.x() + wl, g.y() + diff, w, r.height() - diff ); + } + + w = wl + wr; + if ( central ) + w += central->minimumSize().width(); + return w; +} + +int QMainWindowLayout::extraPixels() const +{ + if ( mainWindow->d->topDock->isEmpty() && + !(mainWindow->d->leftDock->isEmpty() && + mainWindow->d->rightDock->isEmpty()) ) { + return 2; + } else { + return 0; + } +} + +void QMainWindowLayout::addItem( QLayoutItem * /* item */ ) +{ +} + + +QLayoutIterator QMainWindowLayout::iterator() +{ + return 0; +} + + +/* + QHideToolTip and QHideDock - minimized dock +*/ + +#ifndef QT_NO_TOOLTIP +class QHideToolTip : public QToolTip +{ +public: + QHideToolTip( QWidget *parent ) : QToolTip( parent ) {} + ~QHideToolTip() {} + + virtual void maybeTip( const QPoint &pos ); +}; +#endif + + +class QHideDock : public QWidget +{ + Q_OBJECT + +public: + QHideDock( QMainWindow *parent ) : QWidget( parent, "qt_hide_dock" ) { + hide(); + setFixedHeight( style().pixelMetric( QStyle::PM_DockWindowHandleExtent, + this ) + 3 ); + pressedHandle = -1; + pressed = FALSE; + setMouseTracking( TRUE ); + win = parent; +#ifndef QT_NO_TOOLTIP + tip = new QHideToolTip( this ); +#endif + } + ~QHideDock() + { +#ifndef QT_NO_TOOLTIP + delete tip; +#endif + } + +protected: + void paintEvent( QPaintEvent *e ) { + if ( !children() || children()->isEmpty() ) + return; + QPainter p( this ); + p.setClipRegion( e->rect() ); + p.fillRect( e->rect(), colorGroup().brush( QColorGroup::Background ) ); + int x = 0; + int i = -1; + QObjectListIt it( *children() ); + QObject *o; + while ( ( o = it.current() ) ) { + ++it; + ++i; + QDockWindow *dw = ::qt_cast<QDockWindow*>(o); + if ( !dw || !dw->isVisible() ) + continue; + + QStyle::SFlags flags = QStyle::Style_Default; + if ( i == pressedHandle ) + flags |= QStyle::Style_On; + + style().drawPrimitive( QStyle::PE_DockWindowHandle, &p, + QRect( x, 0, 30, 10 ), colorGroup(), + flags ); + x += 30; + } + } + + void mousePressEvent( QMouseEvent *e ) { + pressed = TRUE; + if ( !children() || children()->isEmpty() ) + return; + mouseMoveEvent( e ); + pressedHandle = -1; + + if ( e->button() == RightButton && win->isDockMenuEnabled() ) { + // ### TODO: HideDock menu + } else { + mouseMoveEvent( e ); + } + } + + void mouseMoveEvent( QMouseEvent *e ) { + if ( !children() || children()->isEmpty() ) + return; + if ( !pressed ) + return; + int x = 0; + int i = -1; + if ( e->y() >= 0 && e->y() <= height() ) { + QObjectListIt it( *children() ); + QObject *o; + while ( ( o = it.current() ) ) { + ++it; + ++i; + QDockWindow *dw = ::qt_cast<QDockWindow*>(o); + if ( !dw || !dw->isVisible() ) + continue; + + if ( e->x() >= x && e->x() <= x + 30 ) { + int old = pressedHandle; + pressedHandle = i; + if ( pressedHandle != old ) + repaint( TRUE ); + return; + } + x += 30; + } + } + int old = pressedHandle; + pressedHandle = -1; + if ( old != -1 ) + repaint( TRUE ); + } + + void mouseReleaseEvent( QMouseEvent *e ) { + pressed = FALSE; + if ( pressedHandle == -1 ) + return; + if ( !children() || children()->isEmpty() ) + return; + if ( e->button() == LeftButton ) { + if ( e->y() >= 0 && e->y() <= height() ) { + QObject *o = ( (QObjectList*)children() )->at( pressedHandle ); + QDockWindow *dw = ::qt_cast<QDockWindow*>(o); + if ( dw ) { + dw->show(); + dw->dock(); + } + } + } + pressedHandle = -1; + repaint( FALSE ); + } + + bool eventFilter( QObject *o, QEvent *e ) { + if ( o == this || !o->isWidgetType() ) + return QWidget::eventFilter( o, e ); + if ( e->type() == QEvent::Hide || + e->type() == QEvent::Show || + e->type() == QEvent::ShowToParent ) + updateState(); + return QWidget::eventFilter( o, e ); + } + + void updateState() { + bool visible = TRUE; + if ( !children() || children()->isEmpty() ) { + visible = FALSE; + } else { + QObjectListIt it( *children() ); + QObject *o; + while ( ( o = it.current() ) ) { + ++it; + QDockWindow *dw = ::qt_cast<QDockWindow*>(o); + if ( !dw ) + continue; + if ( dw->isHidden() ) { + visible = FALSE; + continue; + } + if ( !dw->isVisible() ) + continue; + visible = TRUE; + break; + } + } + + if ( visible ) + show(); + else + hide(); + win->triggerLayout( FALSE ); + update(); + } + + void childEvent( QChildEvent *e ) { + QWidget::childEvent( e ); + if ( e->type() == QEvent::ChildInserted ) + e->child()->installEventFilter( this ); + else + e->child()->removeEventFilter( this ); + updateState(); + } + +private: + QMainWindow *win; + int pressedHandle; + bool pressed; +#ifndef QT_NO_TOOLTIP + QHideToolTip *tip; + friend class QHideToolTip; +#endif +}; + +#ifndef QT_NO_TOOLTIP +void QHideToolTip::maybeTip( const QPoint &pos ) +{ + if ( !parentWidget() ) + return; + QHideDock *dock = (QHideDock*)parentWidget(); + + if ( !dock->children() || dock->children()->isEmpty() ) + return; + QObjectListIt it( *dock->children() ); + QObject *o; + int x = 0; + while ( ( o = it.current() ) ) { + ++it; + QDockWindow *dw = ::qt_cast<QDockWindow*>(o); + if ( !dw || !dw->isVisible() ) + continue; + + if ( pos.x() >= x && pos.x() <= x + 30 ) { + QDockWindow *dw = (QDockWindow*)o; + if ( !dw->caption().isEmpty() ) + tip( QRect( x, 0, 30, dock->height() ), dw->caption() ); + return; + } + x += 30; + } +} +#endif + +/*! + \class QMainWindow qmainwindow.h + \brief The QMainWindow class provides a main application window, + with a menu bar, dock windows (e.g. for toolbars), and a status + bar. + + \ingroup application + \mainclass + + Main windows are most often used to provide menus, toolbars and a + status bar around a large central widget, such as a text edit, + drawing canvas or QWorkspace (for MDI applications). QMainWindow + is usually subclassed since this makes it easier to encapsulate + the central widget, menus and toolbars as well as the window's + state. Subclassing makes it possible to create the slots that are + called when the user clicks menu items or toolbar buttons. You can + also create main windows using \link designer-manual.book Qt + Designer\endlink. We'll briefly review adding menu items and + toolbar buttons then describe the facilities of QMainWindow + itself. + + \code + QMainWindow *mw = new QMainWindow; + QTextEdit *edit = new QTextEdit( mw, "editor" ); + edit->setFocus(); + mw->setCaption( "Main Window" ); + mw->setCentralWidget( edit ); + mw->show(); + \endcode + + QMainWindows may be created in their own right as shown above. + The central widget is set with setCentralWidget(). Popup menus can + be added to the default menu bar, widgets can be added to the + status bar, toolbars and dock windows can be added to any of the + dock areas. + + \quotefile application/main.cpp + \skipto ApplicationWindow + \printuntil show + + In the extract above ApplicationWindow is a subclass of + QMainWindow that we must write for ourselves; this is the usual + approach to using QMainWindow. (The source for the extracts in + this description are taken from \l application/main.cpp, \l + application/application.cpp, \l action/main.cpp, and \l + action/application.cpp ) + + When subclassing we add the menu items and toolbars in the + subclass's constructor. If we've created a QMainWindow instance + directly we can add menu items and toolbars just as easily by + passing the QMainWindow instance as the parent instead of the \e + this pointer. + + \quotefile application/application.cpp + \skipto help = new + \printuntil about + + Here we've added a new menu with one menu item. The menu has been + inserted into the menu bar that QMainWindow provides by default + and which is accessible through the menuBar() function. The slot + will be called when the menu item is clicked. + + \quotefile application/application.cpp + \skipto fileTools + \printuntil setLabel + \skipto QToolButton + \printuntil open file + + This extract shows the creation of a toolbar with one toolbar + button. QMainWindow supplies four dock areas for toolbars. When a + toolbar is created as a child of a QMainWindow (or derived class) + instance it will be placed in a dock area (the \c Top dock area by + default). The slot will be called when the toolbar button is + clicked. Any dock window can be added to a dock area either using + addDockWindow(), or by creating a dock window with the QMainWindow + as the parent. + + \quotefile application/application.cpp + \skipto editor + \printuntil statusBar + + Having created the menus and toolbar we create an instance of the + large central widget, give it the focus and set it as the main + window's central widget. In the example we've also set the status + bar, accessed via the statusBar() function, to an initial message + which will be displayed for two seconds. Note that you can add + additional widgets to the status bar, for example labels, to show + further status information. See the QStatusBar documentation for + details, particularly the addWidget() function. + + Often we want to synchronize a toolbar button with a menu item. + For example, if the user clicks a 'bold' toolbar button we want + the 'bold' menu item to be checked. This synchronization can be + achieved automatically by creating actions and adding the actions + to the toolbar and menu. + + \quotefile action/application.cpp + \skipto QAction * fileOpen + \printline + \skipto fileOpenAction + \printuntil choose + + Here we create an action with an icon which will be used in any + menu and toolbar that the action is added to. We've also given the + action a menu name, '\&Open', and a keyboard shortcut. The + connection that we have made will be used when the user clicks + either the menu item \e or the toolbar button. + + \quotefile action/application.cpp + \skipto QPopupMenu * file + \printuntil menuBar + \skipto fileOpen + \printline + + The extract above shows the creation of a popup menu. We add the + menu to the QMainWindow's menu bar and add our action. + + \quotefile action/application.cpp + \skipto QToolBar * fileTool + \printuntil OpenAction + + Here we create a new toolbar as a child of the QMainWindow and add + our action to the toolbar. + + We'll now explore the functionality offered by QMainWindow. + + The main window will take care of the dock areas, and the geometry + of the central widget, but all other aspects of the central widget + are left to you. QMainWindow automatically detects the creation of + a menu bar or status bar if you specify the QMainWindow as parent, + or you can use the provided menuBar() and statusBar() functions. + The functions menuBar() and statusBar() create a suitable widget + if one doesn't exist, and update the window's layout to make + space. + + QMainWindow provides a QToolTipGroup connected to the status bar. + The function toolTipGroup() provides access to the default + QToolTipGroup. It isn't possible to set a different tool tip + group. + + New dock windows and toolbars can be added to a QMainWindow using + addDockWindow(). Dock windows can be moved using moveDockWindow() + and removed with removeDockWindow(). QMainWindow allows default + dock window (toolbar) docking in all its dock areas (\c Top, \c + Left, \c Right, \c Bottom). You can use setDockEnabled() to + enable and disable docking areas for dock windows. When adding or + moving dock windows you can specify their 'edge' (dock area). The + currently available edges are: \c Top, \c Left, \c Right, \c + Bottom, \c Minimized (effectively a 'hidden' dock area) and \c + TornOff (floating). See \l Qt::Dock for an explanation of these + areas. Note that the *ToolBar functions are included for backward + compatibility; all new code should use the *DockWindow functions. + QToolbar is a subclass of QDockWindow so all functions that work + with dock windows work on toolbars in the same way. + + \target dwm + If the user clicks the close button, then the dock window is + hidden. A dock window can be hidden or unhidden by the user by + right clicking a dock area and clicking the name of the relevant + dock window on the pop up dock window menu. This menu lists the + names of every dock window; visible dock windows have a tick + beside their names. The dock window menu is created automatically + as required by createDockWindowMenu(). Since it may not always be + appropriate for a dock window to appear on this menu the + setAppropriate() function is used to inform the main window + whether or not the dock window menu should include a particular + dock window. Double clicking a dock window handle (usually on the + left-hand side of the dock window) undocks (floats) the dock + window. Double clicking a floating dock window's titlebar will + dock the floating dock window. (See also + \l{QMainWindow::DockWindows}.) + + Some functions change the appearance of a QMainWindow globally: + \list + \i QDockWindow::setHorizontalStretchable() and + QDockWindow::setVerticalStretchable() are used to make specific dock + windows or toolbars stretchable. + \i setUsesBigPixmaps() is used to set whether tool buttons should + draw small or large pixmaps (see QIconSet for more information). + \i setUsesTextLabel() is used to set whether tool buttons + should display a textual label in addition to pixmaps + (see QToolButton for more information). + \endlist + + The user can drag dock windows into any enabled docking area. Dock + windows can also be dragged \e within a docking area, for example + to rearrange the order of some toolbars. Dock windows can also be + dragged outside any docking area (undocked or 'floated'). Being + able to drag dock windows can be enabled (the default) and + disabled using setDockWindowsMovable(). + + The \c Minimized edge is a hidden dock area. If this dock area is + enabled the user can hide (minimize) a dock window or show (restore) + a minimized dock window by clicking the dock window handle. If the + user hovers the mouse cursor over one of the handles, the caption of + the dock window is displayed in a tool tip (see + QDockWindow::caption() or QToolBar::label()), so if you enable the + \c Minimized dock area, it is best to specify a meaningful caption + or label for each dock window. To minimize a dock window + programmatically use moveDockWindow() with an edge of \c Minimized. + + Dock windows are moved transparently by default, i.e. during the + drag an outline rectangle is drawn on the screen representing the + position of the dock window as it moves. If you want the dock + window to be shown normally whilst it is moved use + setOpaqueMoving(). + + The location of a dock window, i.e. its dock area and position + within the dock area, can be determined by calling getLocation(). + Movable dock windows can be lined up to minimize wasted space with + lineUpDockWindows(). Pointers to the dock areas are available from + topDock(), leftDock(), rightDock() and bottomDock(). A customize + menu item is added to the pop up dock window menu if + isCustomizable() returns TRUE; it returns FALSE by default. + Reimplement isCustomizable() and customize() if you want to offer + this extra menu item, for example, to allow the user to change + settings relating to the main window and its toolbars and dock + windows. + + The main window's menu bar is fixed (at the top) by default. If + you want a movable menu bar, create a QMenuBar as a stretchable + widget inside its own movable dock window and restrict this dock + window to only live within the \c Top or \c Bottom dock: + + \code + QToolBar *tb = new QToolBar( this ); + addDockWindow( tb, tr( "Menubar" ), Top, FALSE ); + QMenuBar *mb = new QMenuBar( tb ); + mb->setFrameStyle( QFrame::NoFrame ); + tb->setStretchableWidget( mb ); + setDockEnabled( tb, Left, FALSE ); + setDockEnabled( tb, Right, FALSE ); + \endcode + + An application with multiple dock windows can choose to save the + current dock window layout in order to restore it later, e.g. in + the next session. You can do this by using the streaming operators + for QMainWindow. + + To save the layout and positions of all the dock windows do this: + + \code + QFile file( filename ); + if ( file.open( IO_WriteOnly ) ) { + QTextStream stream( &file ); + stream << *mainWindow; + file.close(); + } + \endcode + + To restore the dock window positions and sizes (normally when the + application is next started), do following: + + \code + QFile file( filename ); + if ( file.open( IO_ReadOnly ) ) { + QTextStream stream( &file ); + stream >> *mainWindow; + file.close(); + } + \endcode + + The QSettings class can be used in conjunction with the streaming + operators to store the application's settings. + + QMainWindow's management of dock windows and toolbars is done + transparently behind-the-scenes by QDockArea. + + For multi-document interfaces (MDI), use a QWorkspace as the + central widget. + + Adding dock windows, e.g. toolbars, to QMainWindow's dock areas is + straightforward. If the supplied dock areas are not sufficient for + your application we suggest that you create a QWidget subclass and + add your own dock areas (see \l QDockArea) to the subclass since + QMainWindow provides functionality specific to the standard dock + areas it provides. + + <img src=qmainwindow-m.png> <img src=qmainwindow-w.png> + + \sa QToolBar QDockWindow QStatusBar QAction QMenuBar QPopupMenu QToolTipGroup QDialog +*/ + +/*! \enum Qt::ToolBarDock + \internal +*/ + +/*! + \enum Qt::Dock + + Each dock window can be in one of the following positions: + + \value DockTop above the central widget, below the menu bar. + + \value DockBottom below the central widget, above the status bar. + + \value DockLeft to the left of the central widget. + + \value DockRight to the right of the central widget. + + \value DockMinimized the dock window is not shown (this is + effectively a 'hidden' dock area); the handles of all minimized + dock windows are drawn in one row below the menu bar. + + \value DockTornOff the dock window floats as its own top level + window which always stays on top of the main window. + + \value DockUnmanaged not managed by a QMainWindow. +*/ + +/*! + \enum QMainWindow::DockWindows + + Right-clicking a dock area will pop-up the dock window menu + (createDockWindowMenu() is called automatically). When called in + code you can specify what items should appear on the menu with + this enum. + + \value OnlyToolBars The menu will list all the toolbars, but not + any other dock windows. + + \value NoToolBars The menu will list dock windows but not + toolbars. + + \value AllDockWindows The menu will list all toolbars and other + dock windows. (This is the default.) +*/ + +/*! + \obsolete + \fn void QMainWindow::addToolBar( QDockWindow *, Dock = Top, bool newLine = FALSE ); +*/ + +/*! + \obsolete + \overload void QMainWindow::addToolBar( QDockWindow *, const QString &label, Dock = Top, bool newLine = FALSE ); +*/ + +/*! + \obsolete + \fn void QMainWindow::moveToolBar( QDockWindow *, Dock = Top ); +*/ + +/*! + \obsolete + \overload void QMainWindow::moveToolBar( QDockWindow *, Dock, bool nl, int index, int extraOffset = -1 ); +*/ + +/*! + \obsolete + \fn void QMainWindow::removeToolBar( QDockWindow * ); +*/ + +/*! + \obsolete + \fn void QMainWindow::lineUpToolBars( bool keepNewLines = FALSE ); +*/ + +/*! + \obsolete + \fn void QMainWindow::toolBarPositionChanged( QToolBar * ); +*/ + +/*! + \obsolete + \fn bool QMainWindow::toolBarsMovable() const +*/ + +/*! + \obsolete + \fn void QMainWindow::setToolBarsMovable( bool ) +*/ + +/*! + Constructs an empty main window. The \a parent, \a name and widget + flags \a f, are passed on to the QWidget constructor. + + By default, the widget flags are set to \c WType_TopLevel rather + than 0 as they are with QWidget. If you don't want your + QMainWindow to be a top level widget then you will need to set \a + f to 0. +*/ + +QMainWindow::QMainWindow( QWidget * parent, const char * name, WFlags f ) + : QWidget( parent, name, f ) +{ + d = new QMainWindowPrivate; +#ifdef Q_WS_MACX + d->opaque = TRUE; +#else + d->opaque = FALSE; +#endif + installEventFilter( this ); + d->topDock = new QDockArea( Horizontal, QDockArea::Normal, this, "qt_top_dock" ); + d->topDock->installEventFilter( this ); + d->bottomDock = new QDockArea( Horizontal, QDockArea::Reverse, this, "qt_bottom_dock" ); + d->bottomDock->installEventFilter( this ); + d->leftDock = new QDockArea( Vertical, QDockArea::Normal, this, "qt_left_dock" ); + d->leftDock->installEventFilter( this ); + d->rightDock = new QDockArea( Vertical, QDockArea::Reverse, this, "qt_right_dock" ); + d->rightDock->installEventFilter( this ); + d->hideDock = new QHideDock( this ); +} + + +/*! + Destroys the object and frees any allocated resources. +*/ + +QMainWindow::~QMainWindow() +{ + delete layout(); + delete d; +} + +#ifndef QT_NO_MENUBAR +/*! + Sets this main window to use the menu bar \a newMenuBar. + + The existing menu bar (if any) is deleted along with its contents. + + \sa menuBar() +*/ + +void QMainWindow::setMenuBar( QMenuBar * newMenuBar ) +{ + if ( !newMenuBar ) + return; + if ( d->mb ) + delete d->mb; + d->mb = newMenuBar; + d->mb->installEventFilter( this ); + triggerLayout(); +} + + +/*! + Returns the menu bar for this window. + + If there isn't one, then menuBar() creates an empty menu bar. + + \sa statusBar() +*/ + +QMenuBar * QMainWindow::menuBar() const +{ + if ( d->mb ) + return d->mb; + + QObjectList * l + = ((QObject*)this)->queryList( "QMenuBar", 0, FALSE, FALSE ); + QMenuBar * b; + if ( l && l->count() ) { + b = (QMenuBar *)l->first(); + } else { + b = new QMenuBar( (QMainWindow *)this, "automatic menu bar" ); + b->show(); + } + delete l; + d->mb = b; + d->mb->installEventFilter( this ); + ((QMainWindow *)this)->triggerLayout(); + return b; +} +#endif // QT_NO_MENUBAR + +/*! + Sets this main window to use the status bar \a newStatusBar. + + The existing status bar (if any) is deleted along with its + contents. + + Note that \a newStatusBar \e must be a child of this main window, + and that it is not automatically displayed. If you call this + function after show(), you will probably also need to call + newStatusBar->show(). + + \sa setMenuBar() statusBar() +*/ + +void QMainWindow::setStatusBar( QStatusBar * newStatusBar ) +{ + if ( !newStatusBar || newStatusBar == d->sb ) + return; + if ( d->sb ) + delete d->sb; + d->sb = newStatusBar; +#ifndef QT_NO_TOOLTIP + // ### this code can cause unnecessary creation of a tool tip group + connect( toolTipGroup(), SIGNAL(showTip(const QString&)), + d->sb, SLOT(message(const QString&)) ); + connect( toolTipGroup(), SIGNAL(removeTip()), + d->sb, SLOT(clear()) ); +#endif + d->sb->installEventFilter( this ); + triggerLayout(); +} + + +/*! + Returns this main window's status bar. If there isn't one, + statusBar() creates an empty status bar, and if necessary a tool + tip group too. + + \sa menuBar() toolTipGroup() +*/ + +QStatusBar * QMainWindow::statusBar() const +{ + if ( d->sb ) + return d->sb; + + QObjectList * l + = ((QObject*)this)->queryList( "QStatusBar", 0, FALSE, FALSE ); + QStatusBar * s; + if ( l && l->count() ) { + s = (QStatusBar *)l->first(); + } else { + s = new QStatusBar( (QMainWindow *)this, "automatic status bar" ); + s->show(); + } + delete l; + ((QMainWindow *)this)->setStatusBar( s ); + ((QMainWindow *)this)->triggerLayout( TRUE ); + return s; +} + + +#ifndef QT_NO_TOOLTIP +/*! + Sets this main window to use the tool tip group \a + newToolTipGroup. + + The existing tool tip group (if any) is deleted along with its + contents. All the tool tips connected to it lose the ability to + display the group texts. + + \sa menuBar() toolTipGroup() +*/ + +void QMainWindow::setToolTipGroup( QToolTipGroup * newToolTipGroup ) +{ + if ( !newToolTipGroup || newToolTipGroup == d->ttg ) + return; + if ( d->ttg ) + delete d->ttg; + d->ttg = newToolTipGroup; + + connect( toolTipGroup(), SIGNAL(showTip(const QString&)), + statusBar(), SLOT(message(const QString&)) ); + connect( toolTipGroup(), SIGNAL(removeTip()), + statusBar(), SLOT(clear()) ); +} + + +/*! + Returns this main window's tool tip group. If there isn't one, + toolTipGroup() creates an empty tool tip group. + + \sa menuBar() statusBar() +*/ + +QToolTipGroup * QMainWindow::toolTipGroup() const +{ + if ( d->ttg ) + return d->ttg; + + QToolTipGroup * t = new QToolTipGroup( (QMainWindow*)this, + "automatic tool tip group" ); + ((QMainWindowPrivate*)d)->ttg = t; + return t; +} +#endif + + +/*! + If \a enable is TRUE then users can dock windows in the \a dock + area. If \a enable is FALSE users cannot dock windows in the \a + dock dock area. + + Users can dock (drag) dock windows into any enabled dock area. +*/ + +void QMainWindow::setDockEnabled( Dock dock, bool enable ) +{ + d->docks.replace( dock, enable ); +} + + +/*! + Returns TRUE if the \a dock dock area is enabled, i.e. it can + accept user dragged dock windows; otherwise returns FALSE. + + \sa setDockEnabled() +*/ + +bool QMainWindow::isDockEnabled( Dock dock ) const +{ + return d->docks[ dock ]; +} + +/*! + \overload + + Returns TRUE if dock area \a area is enabled, i.e. it can accept + user dragged dock windows; otherwise returns FALSE. + + \sa setDockEnabled() +*/ + +bool QMainWindow::isDockEnabled( QDockArea *area ) const +{ + + if ( area == d->leftDock ) + return d->docks[ DockLeft ]; + if ( area == d->rightDock ) + return d->docks[ DockRight ]; + if ( area == d->topDock ) + return d->docks[ DockTop ]; + if ( area == d->bottomDock ) + return d->docks[ DockBottom ]; + return FALSE; +} + +/*! + \overload + + If \a enable is TRUE then users can dock the \a dw dock window in + the \a dock area. If \a enable is FALSE users cannot dock the \a + dw dock window in the \a dock area. + + In general users can dock (drag) dock windows into any enabled + dock area. Using this function particular dock areas can be + enabled (or disabled) as docking points for particular dock + windows. +*/ + + +void QMainWindow::setDockEnabled( QDockWindow *dw, Dock dock, bool enable ) +{ + if ( d->dockWindows.find( dw ) == -1 ) { + d->dockWindows.append( dw ); + connect( dw, SIGNAL( placeChanged(QDockWindow::Place) ), + this, SLOT( slotPlaceChanged() ) ); + } + QString s; + s.sprintf( "%p_%d", (void*)dw, (int)dock ); + if ( enable ) + d->disabledDocks.remove( s ); + else if ( d->disabledDocks.find( s ) == d->disabledDocks.end() ) + d->disabledDocks << s; + switch ( dock ) { + case DockTop: + topDock()->setAcceptDockWindow( dw, enable ); + break; + case DockLeft: + leftDock()->setAcceptDockWindow( dw, enable ); + break; + case DockRight: + rightDock()->setAcceptDockWindow( dw, enable ); + break; + case DockBottom: + bottomDock()->setAcceptDockWindow( dw, enable ); + break; + default: + break; + } +} + +/*! + \overload + + Returns TRUE if dock area \a area is enabled for the dock window + \a dw; otherwise returns FALSE. + + \sa setDockEnabled() +*/ + +bool QMainWindow::isDockEnabled( QDockWindow *dw, QDockArea *area ) const +{ + if ( !isDockEnabled( area ) ) + return FALSE; + Dock dock; + if ( area == d->leftDock ) + dock = DockLeft; + else if ( area == d->rightDock ) + dock = DockRight; + else if ( area == d->topDock ) + dock = DockTop; + else if ( area == d->bottomDock ) + dock = DockBottom; + else + return FALSE; + return isDockEnabled( dw, dock ); +} + +/*! + \overload + + Returns TRUE if dock area \a dock is enabled for the dock window + \a tb; otherwise returns FALSE. + + \sa setDockEnabled() +*/ + +bool QMainWindow::isDockEnabled( QDockWindow *tb, Dock dock ) const +{ + if ( !isDockEnabled( dock ) ) + return FALSE; + QString s; + s.sprintf( "%p_%d", (void*)tb, (int)dock ); + return d->disabledDocks.find( s ) == d->disabledDocks.end(); +} + + + +/*! + Adds \a dockWindow to the \a edge dock area. + + If \a newLine is FALSE (the default) then the \a dockWindow is + added at the end of the \a edge. For vertical edges the end is at + the bottom, for horizontal edges (including \c Minimized) the end + is at the right. If \a newLine is TRUE a new line of dock windows + is started with \a dockWindow as the first (left-most and + top-most) dock window. + + If \a dockWindow is managed by another main window, it is first + removed from that window. +*/ + +void QMainWindow::addDockWindow( QDockWindow *dockWindow, + Dock edge, bool newLine ) +{ +#ifdef Q_WS_MAC + if(isTopLevel() && edge == DockTop) + ChangeWindowAttributes((WindowPtr)handle(), kWindowToolbarButtonAttribute, 0); +#endif + moveDockWindow( dockWindow, edge ); + dockWindow->setNewLine( newLine ); + if ( d->dockWindows.find( dockWindow ) == -1 ) { + d->dockWindows.append( dockWindow ); + connect( dockWindow, SIGNAL( placeChanged(QDockWindow::Place) ), + this, SLOT( slotPlaceChanged() ) ); + dockWindow->installEventFilter( this ); + } + dockWindow->setOpaqueMoving( d->opaque ); +} + + +/*! + \overload + + Adds \a dockWindow to the dock area with label \a label. + + If \a newLine is FALSE (the default) the \a dockWindow is added at + the end of the \a edge. For vertical edges the end is at the + bottom, for horizontal edges (including \c Minimized) the end is + at the right. If \a newLine is TRUE a new line of dock windows is + started with \a dockWindow as the first (left-most and top-most) + dock window. + + If \a dockWindow is managed by another main window, it is first + removed from that window. +*/ + +void QMainWindow::addDockWindow( QDockWindow * dockWindow, const QString &label, + Dock edge, bool newLine ) +{ + addDockWindow( dockWindow, edge, newLine ); +#ifndef QT_NO_TOOLBAR + QToolBar *tb = ::qt_cast<QToolBar*>(dockWindow); + if ( tb ) + tb->setLabel( label ); +#endif +} + +/*! + Moves \a dockWindow to the end of the \a edge. + + For vertical edges the end is at the bottom, for horizontal edges + (including \c Minimized) the end is at the right. + + If \a dockWindow is managed by another main window, it is first + removed from that window. +*/ + +void QMainWindow::moveDockWindow( QDockWindow * dockWindow, Dock edge ) +{ + Orientation oo = dockWindow->orientation(); + switch ( edge ) { + case DockTop: + if ( dockWindow->area() != d->topDock ) + dockWindow->removeFromDock( FALSE ); + d->topDock->moveDockWindow( dockWindow ); + emit dockWindowPositionChanged( dockWindow ); + break; + case DockBottom: + if ( dockWindow->area() != d->bottomDock ) + dockWindow->removeFromDock( FALSE ); + d->bottomDock->moveDockWindow( dockWindow ); + emit dockWindowPositionChanged( dockWindow ); + break; + case DockRight: + if ( dockWindow->area() != d->rightDock ) + dockWindow->removeFromDock( FALSE ); + d->rightDock->moveDockWindow( dockWindow ); + emit dockWindowPositionChanged( dockWindow ); + break; + case DockLeft: + if ( dockWindow->area() != d->leftDock ) + dockWindow->removeFromDock( FALSE ); + d->leftDock->moveDockWindow( dockWindow ); + emit dockWindowPositionChanged( dockWindow ); + break; + case DockTornOff: + dockWindow->undock(); + break; + case DockMinimized: + dockWindow->undock( d->hideDock ); + break; + case DockUnmanaged: + break; + } + + if ( oo != dockWindow->orientation() ) + dockWindow->setOrientation( dockWindow->orientation() ); +} + +/*! + \overload + + Moves \a dockWindow to position \a index within the \a edge dock + area. + + Any dock windows with positions \a index or higher have their + position number incremented and any of these on the same line are + moved right (down for vertical dock areas) to make room. + + If \a nl is TRUE, a new dock window line is created below the line + in which the moved dock window appears and the moved dock window, + with any others with higher positions on the same line, is moved + to this new line. + + The \a extraOffset is the space to put between the left side of + the dock area (top side for vertical dock areas) and the dock + window. (This is mostly used for restoring dock windows to the + positions the user has dragged them to.) + + If \a dockWindow is managed by another main window, it is first + removed from that window. +*/ + +void QMainWindow::moveDockWindow( QDockWindow * dockWindow, Dock edge, bool nl, int index, int extraOffset ) +{ + Orientation oo = dockWindow->orientation(); + + dockWindow->setNewLine( nl ); + dockWindow->setOffset( extraOffset ); + switch ( edge ) { + case DockTop: + if ( dockWindow->area() != d->topDock ) + dockWindow->removeFromDock( FALSE ); + d->topDock->moveDockWindow( dockWindow, index ); + break; + case DockBottom: + if ( dockWindow->area() != d->bottomDock ) + dockWindow->removeFromDock( FALSE ); + d->bottomDock->moveDockWindow( dockWindow, index ); + break; + case DockRight: + if ( dockWindow->area() != d->rightDock ) + dockWindow->removeFromDock( FALSE ); + d->rightDock->moveDockWindow( dockWindow, index ); + break; + case DockLeft: + if ( dockWindow->area() != d->leftDock ) + dockWindow->removeFromDock( FALSE ); + d->leftDock->moveDockWindow( dockWindow, index ); + break; + case DockTornOff: + dockWindow->undock(); + break; + case DockMinimized: + dockWindow->undock( d->hideDock ); + break; + case DockUnmanaged: + break; + } + + if ( oo != dockWindow->orientation() ) + dockWindow->setOrientation( dockWindow->orientation() ); +} + +/*! + Removes \a dockWindow from the main window's docking area, + provided \a dockWindow is non-null and managed by this main + window. +*/ + +void QMainWindow::removeDockWindow( QDockWindow * dockWindow ) +{ +#ifdef Q_WS_MAC + if(isTopLevel() && dockWindow->area() == topDock() && !dockWindows( DockTop ).count()) + ChangeWindowAttributes((WindowPtr)handle(), 0, kWindowToolbarButtonAttribute); +#endif + + dockWindow->hide(); + d->dockWindows.removeRef( dockWindow ); + disconnect( dockWindow, SIGNAL( placeChanged(QDockWindow::Place) ), + this, SLOT( slotPlaceChanged() ) ); + dockWindow->removeEventFilter( this ); +} + +/*! + Sets up the geometry management of the window. It is called + automatically when needed, so you shouldn't need to call it. +*/ + +void QMainWindow::setUpLayout() +{ +#ifndef QT_NO_MENUBAR + if ( !d->mb ) { + // slightly evil hack here. reconsider this + QObjectList * l + = ((QObject*)this)->queryList( "QMenuBar", 0, FALSE, FALSE ); + if ( l && l->count() ) + d->mb = menuBar(); + delete l; + } +#endif + if ( !d->sb ) { + // as above. + QObjectList * l + = ((QObject*)this)->queryList( "QStatusBar", 0, FALSE, FALSE ); + if ( l && l->count() ) + d->sb = statusBar(); + delete l; + } + + if (!d->tll) { + d->tll = new QBoxLayout( this, QBoxLayout::Down ); + d->tll->setResizeMode( minimumSize().isNull() ? QLayout::Minimum : QLayout::FreeResize ); + } else { + d->tll->setMenuBar( 0 ); + QLayoutIterator it = d->tll->iterator(); + QLayoutItem *item; + while ( (item = it.takeCurrent()) ) + delete item; + } + +#ifndef QT_NO_MENUBAR + if ( d->mb && d->mb->isVisibleTo( this ) ) { + d->tll->setMenuBar( d->mb ); + if (style().styleHint(QStyle::SH_MainWindow_SpaceBelowMenuBar, this)) + d->tll->addSpacing( d->movable ? 1 : 2 ); + } +#endif + + d->tll->addWidget( d->hideDock ); + if(d->topDock->parentWidget() == this) + d->tll->addWidget( d->topDock ); + + QMainWindowLayout *mwl = new QMainWindowLayout( this, d->tll ); + d->tll->setStretchFactor( mwl, 1 ); + + if(d->leftDock->parentWidget() == this) + mwl->setLeftDock( d->leftDock ); + if ( centralWidget() ) + mwl->setCentralWidget( centralWidget() ); + if(d->rightDock->parentWidget() == this) + mwl->setRightDock( d->rightDock ); + d->mwl = mwl; + + if(d->bottomDock->parentWidget() == this) + d->tll->addWidget( d->bottomDock ); + + if ( d->sb && d->sb->parentWidget() == this) { + d->tll->addWidget( d->sb, 0 ); + // make the sb stay on top of tool bars if there isn't enough space + d->sb->raise(); + } +} + +/*! \reimp */ +void QMainWindow::show() +{ + if ( !d->tll ) + setUpLayout(); + + // show all floating dock windows not explicitly hidden + if (!isVisible()) { + QPtrListIterator<QDockWindow> it(d->dockWindows); + while ( it.current() ) { + QDockWindow *dw = it.current(); + ++it; + if ( dw->isTopLevel() && !dw->isVisible() && !dw->testWState(WState_ForceHide) ) + dw->show(); + } + } + + // show us last so we get focus + QWidget::show(); +} + + +/*! \reimp +*/ +void QMainWindow::hide() +{ + if ( isVisible() ) { + QPtrListIterator<QDockWindow> it(d->dockWindows); + while ( it.current() ) { + QDockWindow *dw = it.current(); + ++it; + if ( dw->isTopLevel() && dw->isVisible() ) { + dw->hide(); // implicit hide, so clear forcehide + ((QMainWindow*)dw)->clearWState(WState_ForceHide); + } + } + } + + QWidget::hide(); +} + + +/*! \reimp */ +QSize QMainWindow::sizeHint() const +{ + QMainWindow* that = (QMainWindow*) this; + // Workaround: because d->tll get's deleted in + // totalSizeHint->polish->sendPostedEvents->childEvent->triggerLayout + // [eg. canvas example on Qt/Embedded] + QApplication::sendPostedEvents( that, QEvent::ChildInserted ); + if ( !that->d->tll ) + that->setUpLayout(); + return that->d->tll->totalSizeHint(); +} + +/*! \reimp */ +QSize QMainWindow::minimumSizeHint() const +{ + if ( !d->tll ) { + QMainWindow* that = (QMainWindow*) this; + that->setUpLayout(); + } + return d->tll->totalMinimumSize(); +} + +/*! + Sets the central widget for this main window to \a w. + + The central widget is surrounded by the left, top, right and + bottom dock areas. The menu bar is above the top dock area. + + \sa centralWidget() +*/ + +void QMainWindow::setCentralWidget( QWidget * w ) +{ + if ( d->mc ) + d->mc->removeEventFilter( this ); + d->mc = w; + if ( d->mc ) + d->mc->installEventFilter( this ); + triggerLayout(); +} + + +/*! + Returns a pointer to the main window's central widget. + + The central widget is surrounded by the left, top, right and + bottom dock areas. The menu bar is above the top dock area. + + \sa setCentralWidget() +*/ + +QWidget * QMainWindow::centralWidget() const +{ + return d->mc; +} + + +/*! \reimp */ + +void QMainWindow::paintEvent( QPaintEvent * ) +{ + if (d->mb && + style().styleHint(QStyle::SH_MainWindow_SpaceBelowMenuBar, this)) { + QPainter p( this ); + int y = d->mb->height() + 1; + style().drawPrimitive(QStyle::PE_Separator, &p, QRect(0, y, width(), 1), + colorGroup(), QStyle::Style_Sunken); + } +} + + +bool QMainWindow::dockMainWindow( QObject *dock ) +{ + while ( dock ) { + if ( dock->parent() && dock->parent() == this ) + return TRUE; + if ( ::qt_cast<QMainWindow*>(dock->parent()) ) + return FALSE; + dock = dock->parent(); + } + return FALSE; +} + +/*! + \reimp +*/ + +bool QMainWindow::eventFilter( QObject* o, QEvent *e ) +{ + if ( e->type() == QEvent::Show && o == this ) { + if ( !d->tll ) + setUpLayout(); + d->tll->activate(); + } else if ( e->type() == QEvent::ContextMenu && d->dockMenu && + ( ::qt_cast<QDockArea*>(o) && dockMainWindow( o ) || o == d->hideDock || o == d->mb ) ) { + if ( showDockMenu( ( (QMouseEvent*)e )->globalPos() ) ) { + ( (QContextMenuEvent*)e )->accept(); + return TRUE; + } + } + + return QWidget::eventFilter( o, e ); +} + + +/*! + Monitors events, recieved in \a e, to ensure the layout is updated. +*/ +void QMainWindow::childEvent( QChildEvent* e) +{ + if ( e->type() == QEvent::ChildRemoved ) { + if ( e->child() == 0 || + !e->child()->isWidgetType() || + ((QWidget*)e->child())->testWFlags( WType_TopLevel ) ) { + // nothing + } else if ( e->child() == d->sb ) { + d->sb = 0; + triggerLayout(); + } else if ( e->child() == d->mb ) { + d->mb = 0; + triggerLayout(); + } else if ( e->child() == d->mc ) { + d->mc = 0; + d->mwl->setCentralWidget( 0 ); + triggerLayout(); + } else if ( ::qt_cast<QDockWindow*>(e->child()) ) { + removeDockWindow( (QDockWindow *)(e->child()) ); + d->appropriate.remove( (QDockWindow*)e->child() ); + triggerLayout(); + } + } else if ( e->type() == QEvent::ChildInserted && !d->sb ) { + d->sb = ::qt_cast<QStatusBar*>(e->child()); + if ( d->sb ) { + if ( d->tll ) { + if ( !d->tll->findWidget( d->sb ) ) + d->tll->addWidget( d->sb ); + } else { + triggerLayout(); + } + } + } +} + +/*! + \reimp +*/ + +bool QMainWindow::event( QEvent * e ) +{ + if ( e->type() == QEvent::ChildRemoved && ( (QChildEvent*)e )->child() == d->mc ) { + d->mc->removeEventFilter( this ); + d->mc = 0; + d->mwl->setCentralWidget( 0 ); + } + + return QWidget::event( e ); +} + + +/*! + \property QMainWindow::usesBigPixmaps + \brief whether big pixmaps are enabled + + If FALSE (the default), the tool buttons will use small pixmaps; + otherwise big pixmaps will be used. + + Tool buttons and other widgets that wish to respond to this + setting are responsible for reading the correct state on startup, + and for connecting to the main window's widget's + pixmapSizeChanged() signal. +*/ + +bool QMainWindow::usesBigPixmaps() const +{ + return d->ubp; +} + +void QMainWindow::setUsesBigPixmaps( bool enable ) +{ + if ( enable == (bool)d->ubp ) + return; + + d->ubp = enable; + emit pixmapSizeChanged( enable ); + + QObjectList *l = queryList( "QLayout" ); + if ( !l || !l->first() ) { + delete l; + return; + } + for ( QLayout *lay = (QLayout*)l->first(); lay; lay = (QLayout*)l->next() ) + lay->activate(); + delete l; +} + +/*! + \property QMainWindow::usesTextLabel + \brief whether text labels for toolbar buttons are enabled + + If disabled (the default), the tool buttons will not use text + labels. If enabled, text labels will be used. + + Tool buttons and other widgets that wish to respond to this + setting are responsible for reading the correct state on startup, + and for connecting to the main window's widget's + usesTextLabelChanged() signal. + + \sa QToolButton::setUsesTextLabel() +*/ + +bool QMainWindow::usesTextLabel() const +{ + return d->utl; +} + + +void QMainWindow::setUsesTextLabel( bool enable ) +{ + if ( enable == (bool)d->utl ) + return; + + d->utl = enable; + emit usesTextLabelChanged( enable ); + + QObjectList *l = queryList( "QLayout" ); + if ( !l || !l->first() ) { + delete l; + return; + } + for ( QLayout *lay = (QLayout*)l->first(); lay; lay = (QLayout*)l->next() ) + lay->activate(); + delete l; +} + + +/*! + \fn void QMainWindow::pixmapSizeChanged( bool ) + + This signal is emitted whenever the setUsesBigPixmaps() is called + with a value different to the current setting. All widgets that + should respond to such changes, e.g. toolbar buttons, must connect + to this signal. +*/ + +/*! + \fn void QMainWindow::usesTextLabelChanged( bool ) + + This signal is emitted whenever the setUsesTextLabel() is called + with a value different to the current setting. All widgets that + should respond to such changes, e.g. toolbar buttons, must connect + to this signal. +*/ + +/*! + \fn void QMainWindow::dockWindowPositionChanged( QDockWindow *dockWindow ) + + This signal is emitted when the \a dockWindow has changed its + position. A change in position occurs when a dock window is moved + within its dock area or moved to another dock area (including the + \c Minimized and \c TearOff dock areas). + + \sa getLocation() +*/ + +void QMainWindow::setRightJustification( bool enable ) +{ + if ( enable == (bool)d->justify ) + return; + d->justify = enable; + triggerLayout( TRUE ); +} + + +/*! + \obsolete + \property QMainWindow::rightJustification + \brief whether the main window right-justifies its dock windows + + If disabled (the default), stretchable dock windows are expanded, + and non-stretchable dock windows are given the minimum space they + need. Since most dock windows are not stretchable, this usually + results in an unjustified right edge (or unjustified bottom edge + for a vertical dock area). If enabled, the main window will + right-justify its dock windows. + + \sa QDockWindow::setVerticalStretchable(), QDockWindow::setHorizontalStretchable() +*/ + +bool QMainWindow::rightJustification() const +{ + return d->justify; +} + +/*! \internal + */ + +void QMainWindow::triggerLayout( bool deleteLayout ) +{ + if ( deleteLayout || !d->tll ) + setUpLayout(); + QApplication::postEvent( this, new QEvent( QEvent::LayoutHint ) ); +} + +/*! + Enters 'What's This?' mode and returns immediately. + + This is the same as QWhatsThis::enterWhatsThisMode(), but + implemented as a main window object's slot. This way it can easily + be used for popup menus, for example: + + \code + QPopupMenu * help = new QPopupMenu( this ); + help->insertItem( "What's &This", this , SLOT(whatsThis()), SHIFT+Key_F1); + \endcode + + \sa QWhatsThis::enterWhatsThisMode() +*/ +void QMainWindow::whatsThis() +{ +#ifndef QT_NO_WHATSTHIS + QWhatsThis::enterWhatsThisMode(); +#endif +} + + +/*! + \reimp +*/ + +void QMainWindow::styleChange( QStyle& old ) +{ + QWidget::styleChange( old ); +} + +/*! + Finds the location of the dock window \a dw. + + If the \a dw dock window is found in the main window the function + returns TRUE and populates the \a dock variable with the dw's dock + area and the \a index with the dw's position within the dock area. + It also sets \a nl to TRUE if the \a dw begins a new line + (otherwise FALSE), and \a extraOffset with the dock window's offset. + + If the \a dw dock window is not found then the function returns + FALSE and the state of \a dock, \a index, \a nl and \a extraOffset + is undefined. + + If you want to save and restore dock window positions then use + operator>>() and operator<<(). + + \sa operator>>() operator<<() +*/ + +bool QMainWindow::getLocation( QDockWindow *dw, Dock &dock, int &index, bool &nl, int &extraOffset ) const +{ + dock = DockTornOff; + if ( d->topDock->hasDockWindow( dw, &index ) ) + dock = DockTop; + else if ( d->bottomDock->hasDockWindow( dw, &index ) ) + dock = DockBottom; + else if ( d->leftDock->hasDockWindow( dw, &index ) ) + dock = DockLeft; + else if ( d->rightDock->hasDockWindow( dw, &index ) ) + dock = DockRight; + else if ( dw->parentWidget() == d->hideDock ) { + index = 0; + dock = DockMinimized; + } else { + index = 0; + } + nl = dw->newLine(); + extraOffset = dw->offset(); + return TRUE; +} + +#ifndef QT_NO_TOOLBAR +/*! + Returns a list of all the toolbars which are in the \a dock dock + area, regardless of their state. + + For example, the \c TornOff dock area may contain closed toolbars + but these are returned along with the visible toolbars. + + \sa dockWindows() +*/ + +QPtrList<QToolBar> QMainWindow::toolBars( Dock dock ) const +{ + QPtrList<QDockWindow> lst = dockWindows( dock ); + QPtrList<QToolBar> tbl; + for ( QDockWindow *w = lst.first(); w; w = lst.next() ) { + QToolBar *tb = ::qt_cast<QToolBar*>(w); + if ( tb ) + tbl.append( tb ); + } + return tbl; +} +#endif + +/*! + Returns a list of all the dock windows which are in the \a dock + dock area, regardless of their state. + + For example, the \c DockTornOff dock area may contain closed dock + windows but these are returned along with the visible dock + windows. +*/ + +QPtrList<QDockWindow> QMainWindow::dockWindows( Dock dock ) const +{ + QPtrList<QDockWindow> lst; + switch ( dock ) { + case DockTop: + return d->topDock->dockWindowList(); + case DockBottom: + return d->bottomDock->dockWindowList(); + case DockLeft: + return d->leftDock->dockWindowList(); + case DockRight: + return d->rightDock->dockWindowList(); + case DockTornOff: { + for ( QDockWindow *w = d->dockWindows.first(); w; w = d->dockWindows.next() ) { + if ( !w->area() && w->place() == QDockWindow::OutsideDock ) + lst.append( w ); + } + } + return lst; + case DockMinimized: { + if ( d->hideDock->children() ) { + QObjectListIt it( *d->hideDock->children() ); + QObject *o; + while ( ( o = it.current() ) ) { + ++it; + QDockWindow *dw = ::qt_cast<QDockWindow*>(o); + if ( !dw ) + continue; + lst.append( dw ); + } + } + } + return lst; + default: + break; + } + return lst; +} + +/*! + \overload + + Returns the list of dock windows which belong to this main window, + regardless of which dock area they are in or what their state is, + (e.g. irrespective of whether they are visible or not). +*/ + +QPtrList<QDockWindow> QMainWindow::dockWindows() const +{ + return d->dockWindows; +} + +void QMainWindow::setDockWindowsMovable( bool enable ) +{ + d->movable = enable; + QObjectList *l = queryList( "QDockWindow" ); + if ( l ) { + for ( QObject *o = l->first(); o; o = l->next() ) + ( (QDockWindow*)o )->setMovingEnabled( enable ); + } + delete l; +} + +/*! + \property QMainWindow::dockWindowsMovable + \brief whether the dock windows are movable + + If TRUE (the default), the user will be able to move movable dock + windows from one QMainWindow dock area to another, including the + \c TearOff area (i.e. where the dock window floats freely as a + window in its own right), and the \c Minimized area (where only + the dock window's handle is shown below the menu bar). Moveable + dock windows can also be moved within QMainWindow dock areas, i.e. + to rearrange them within a dock area. + + If FALSE the user will not be able to move any dock windows. + + By default dock windows are moved transparently (i.e. only an + outline rectangle is shown during the drag), but this setting can + be changed with setOpaqueMoving(). + + \sa setDockEnabled(), setOpaqueMoving() +*/ + +bool QMainWindow::dockWindowsMovable() const +{ + return d->movable; +} + +void QMainWindow::setOpaqueMoving( bool b ) +{ + d->opaque = b; + QObjectList *l = queryList( "QDockWindow" ); + if ( l ) { + for ( QObject *o = l->first(); o; o = l->next() ) + ( (QDockWindow*)o )->setOpaqueMoving( b ); + } + delete l; +} + +/*! + \property QMainWindow::opaqueMoving + \brief whether dock windows are moved opaquely + + If TRUE the dock windows of the main window are shown opaquely + (i.e. it shows the toolbar as it looks when docked) whilst it is + being moved. If FALSE (the default) they are shown transparently, + (i.e. as an outline rectangle). + + \warning Opaque moving of toolbars and dockwindows is known to + have several problems. We recommend avoiding the use of this + feature for the time being. We intend fixing the problems in a + future release. +*/ + +bool QMainWindow::opaqueMoving() const +{ + return d->opaque; +} + +/*! + This function will line up dock windows within the visible dock + areas (\c Top, \c Left, \c Right and \c Bottom) as compactly as + possible. + + If \a keepNewLines is TRUE, all dock windows stay on their + original lines. If \a keepNewLines is FALSE then newlines may be + removed to achieve the most compact layout possible. + + The method only works if dockWindowsMovable() returns TRUE. +*/ + +void QMainWindow::lineUpDockWindows( bool keepNewLines ) +{ + if ( !dockWindowsMovable() ) + return; + d->topDock->lineUp( keepNewLines ); + d->leftDock->lineUp( keepNewLines ); + d->rightDock->lineUp( keepNewLines ); + d->bottomDock->lineUp( keepNewLines ); +} + +/*! + Returns TRUE, if the dock window menu is enabled; otherwise + returns FALSE. + + The menu lists the (appropriate()) dock windows (which may be + shown or hidden), and has a "Line Up Dock Windows" menu item. It + will also have a "Customize" menu item if isCustomizable() returns + TRUE. + + \sa setDockEnabled(), lineUpDockWindows() appropriate() + setAppropriate() +*/ + +bool QMainWindow::isDockMenuEnabled() const +{ + return d->dockMenu; +} + +/*! + If \a b is TRUE, then right clicking on a dock window or dock area + will pop up the dock window menu. If \a b is FALSE, right clicking + a dock window or dock area will not pop up the menu. + + The menu lists the (appropriate()) dock windows (which may be + shown or hidden), and has a "Line Up Dock Windows" item. It will + also have a "Customize" menu item if isCustomizable() returns + TRUE. + + \sa lineUpDockWindows(), isDockMenuEnabled() +*/ + +void QMainWindow::setDockMenuEnabled( bool b ) +{ + d->dockMenu = b; +} + +/*! + Creates the dock window menu which contains all toolbars (if \a + dockWindows is \c OnlyToolBars ), all dock windows (if \a + dockWindows is \c NoToolBars) or all toolbars and dock windows (if + \a dockWindows is \c AllDockWindows - the default). + + This function is called internally when necessary, e.g. when the + user right clicks a dock area (providing isDockMenuEnabled() + returns TRUE). +\omit +### Qt 4.0 + You can reimplement this function if you wish to customize the + behaviour. +\endomit + + The menu items representing the toolbars and dock windows are + checkable. The visible dock windows are checked and the hidden + dock windows are unchecked. The user can click a menu item to + change its state (show or hide the dock window). + + The list and the state are always kept up-to-date. + + Toolbars and dock windows which are not appropriate in the current + context (see setAppropriate()) are not listed in the menu. + + The menu also has a menu item for lining up the dock windows. + + If isCustomizable() returns TRUE, a Customize menu item is added + to the menu, which if clicked will call customize(). The + isCustomizable() function we provide returns FALSE and customize() + does nothing, so they must be reimplemented in a subclass to be + useful. +*/ + +QPopupMenu *QMainWindow::createDockWindowMenu( DockWindows dockWindows ) const +{ + QObjectList *l = queryList( "QDockWindow" ); + + if ( !l || l->isEmpty() ) + return 0; + + delete l; + + QPopupMenu *menu = new QPopupMenu( (QMainWindow*)this, "qt_customize_menu" ); + menu->setCheckable( TRUE ); + d->dockWindowModes.replace( menu, dockWindows ); + connect( menu, SIGNAL( aboutToShow() ), this, SLOT( menuAboutToShow() ) ); + return menu; +} + +/*! + This slot is called from the aboutToShow() signal of the default + dock menu of the mainwindow. The default implementation + initializes the menu with all dock windows and toolbars in this + slot. +\omit +### Qt 4.0 + If you want to do small adjustments to the menu, you can do it in + this slot; or you can reimplement createDockWindowMenu(). +\endomit +*/ + +void QMainWindow::menuAboutToShow() +{ + QPopupMenu *menu = (QPopupMenu*)sender(); + QMap<QPopupMenu*, DockWindows>::Iterator it = d->dockWindowModes.find( menu ); + if ( it == d->dockWindowModes.end() ) + return; + menu->clear(); + + DockWindows dockWindows = *it; + + QObjectList *l = queryList( "QDockWindow" ); + + bool empty = TRUE; + if ( l && !l->isEmpty() ) { + + QObject *o = 0; + if ( dockWindows == AllDockWindows || dockWindows == NoToolBars ) { + for ( o = l->first(); o; o = l->next() ) { + QDockWindow *dw = (QDockWindow*)o; + if ( !appropriate( dw ) || ::qt_cast<QToolBar*>(dw) || !dockMainWindow( dw ) ) + continue; + QString label = dw->caption(); + if ( !label.isEmpty() ) { + int id = menu->insertItem( label, dw, SLOT( toggleVisible() ) ); + menu->setItemChecked( id, dw->isVisible() ); + empty = FALSE; + } + } + if ( !empty ) + menu->insertSeparator(); + } + + empty = TRUE; + +#ifndef QT_NO_TOOLBAR + if ( dockWindows == AllDockWindows || dockWindows == OnlyToolBars ) { + for ( o = l->first(); o; o = l->next() ) { + QToolBar *tb = ::qt_cast<QToolBar*>(o); + if ( !tb || !appropriate(tb) || !dockMainWindow(tb) ) + continue; + QString label = tb->label(); + if ( !label.isEmpty() ) { + int id = menu->insertItem( label, tb, SLOT( toggleVisible() ) ); + menu->setItemChecked( id, tb->isVisible() ); + empty = FALSE; + } + } + } +#endif + + } + + delete l; + + if ( !empty ) + menu->insertSeparator(); + + if ( dockWindowsMovable() ) + menu->insertItem( tr( "Line up" ), this, SLOT( doLineUp() ) ); + if ( isCustomizable() ) + menu->insertItem( tr( "Customize..." ), this, SLOT( customize() ) ); +} + +/*! + Shows the dock menu at the position \a globalPos. The menu lists + the dock windows so that they can be shown (or hidden), lined up, + and possibly customized. Returns TRUE if the menu is shown; + otherwise returns FALSE. + + If you want a custom menu, reimplement this function. You can + create the menu from scratch or call createDockWindowMenu() and + modify the result. +\omit +### Qt 4.0 + The default implementation uses the dock window menu which gets + created by createDockWindowMenu(). You can reimplement + createDockWindowMenu() if you want to use your own specialized + popup menu. +\endomit +*/ + +bool QMainWindow::showDockMenu( const QPoint &globalPos ) +{ + if ( !d->dockMenu ) + return FALSE; + if ( !d->rmbMenu ) + d->rmbMenu = createDockWindowMenu(); + if ( !d->rmbMenu ) + return FALSE; + + d->rmbMenu->exec( globalPos ); + return TRUE; +} + +void QMainWindow::slotPlaceChanged() +{ + QObject* obj = (QObject*)sender(); + QDockWindow *dw = ::qt_cast<QDockWindow*>(obj); + if ( dw ) + emit dockWindowPositionChanged( dw ); +#ifndef QT_NO_TOOLBAR + QToolBar *tb = ::qt_cast<QToolBar*>(obj); + if ( tb ) + emit toolBarPositionChanged( tb ); +#endif +} + +/*! + \internal + For internal use of QDockWindow only. + */ + +QDockArea *QMainWindow::dockingArea( const QPoint &p ) +{ + int mh = d->mb ? d->mb->height() : 0; + int sh = d->sb ? d->sb->height() : 0; + if ( p.x() >= -5 && p.x() <= 100 && p.y() > mh && p.y() - height() - sh ) + return d->leftDock; + if ( p.x() >= width() - 100 && p.x() <= width() + 5 && p.y() > mh && p.y() - height() - sh ) + return d->rightDock; + if ( p.y() >= -5 && p.y() < mh + 100 && p.x() >= 0 && p.x() <= width() ) + return d->topDock; + if ( p.y() >= height() - sh - 100 && p.y() <= height() + 5 && p.x() >= 0 && p.x() <= width() ) + return d->bottomDock; + return 0; +} + +/*! + Returns TRUE if \a dw is a dock window known to the main window; + otherwise returns FALSE. +*/ + +bool QMainWindow::hasDockWindow( QDockWindow *dw ) +{ + return d->dockWindows.findRef( dw ) != -1; +} + +/*! + Returns the \c Left dock area + + \sa rightDock() topDock() bottomDock() +*/ + +QDockArea *QMainWindow::leftDock() const +{ + return d->leftDock; +} + +/*! + Returns the \c Right dock area + + \sa leftDock() topDock() bottomDock() +*/ + +QDockArea *QMainWindow::rightDock() const +{ + return d->rightDock; +} + +/*! + Returns the \c Top dock area + + \sa bottomDock() leftDock() rightDock() +*/ + +QDockArea *QMainWindow::topDock() const +{ + return d->topDock; +} + +/*! + Returns a pointer the \c Bottom dock area + + \sa topDock() leftDock() rightDock() +*/ + +QDockArea *QMainWindow::bottomDock() const +{ + return d->bottomDock; +} + +/*! + This function is called when the user clicks the Customize menu + item on the dock window menu. + + The customize menu item will only appear if isCustomizable() + returns TRUE (it returns FALSE by default). + + The function is intended, for example, to provide the user with a + means of telling the application that they wish to customize the + main window, dock windows or dock areas. + + The default implementation does nothing and the Customize menu + item is not shown on the right-click menu by default. If you want + the item to appear then reimplement isCustomizable() to return + TRUE, and reimplement this function to do whatever you want. + + \sa isCustomizable() +*/ + +void QMainWindow::customize() +{ +} + +/*! + Returns TRUE if the dock area dock window menu includes the + Customize menu item (which calls customize() when clicked). + Returns FALSE by default, i.e. the popup menu will not contain a + Customize menu item. You will need to reimplement this function + and set it to return TRUE if you wish the user to be able to see + the dock window menu. + + \sa customize() +*/ + +bool QMainWindow::isCustomizable() const +{ + return FALSE; +} + +/*! + Returns TRUE if it is appropriate to include a menu item for the + \a dw dock window in the dock window menu; otherwise returns + FALSE. + + The user is able to change the state (show or hide) a dock window + that has a menu item by clicking the item. + + Call setAppropriate() to indicate whether or not a particular dock + window should appear on the popup menu. + + \sa setAppropriate() +*/ + +bool QMainWindow::appropriate( QDockWindow *dw ) const +{ + QMap<QDockWindow*, bool>::ConstIterator it = d->appropriate.find( dw ); + if ( it == d->appropriate.end() ) + return TRUE; + return *it; +} + +/*! + Use this function to control whether or not the \a dw dock + window's caption should appear as a menu item on the dock window + menu that lists the dock windows. + + If \a a is TRUE then the \a dw will appear as a menu item on the + dock window menu. The user is able to change the state (show or + hide) a dock window that has a menu item by clicking the item; + depending on the state of your application, this may or may not be + appropriate. If \a a is FALSE the \a dw will not appear on the + popup menu. + + \sa showDockMenu() isCustomizable() customize() +*/ + +void QMainWindow::setAppropriate( QDockWindow *dw, bool a ) +{ + d->appropriate.replace( dw, a ); +} + +#ifndef QT_NO_TEXTSTREAM +static void saveDockArea( QTextStream &ts, QDockArea *a ) +{ + QPtrList<QDockWindow> l = a->dockWindowList(); + for ( QDockWindow *dw = l.first(); dw; dw = l.next() ) { + ts << QString( dw->caption() ); + ts << ","; + } + ts << endl; + ts << *a; +} + +/*! + \relates QMainWindow + + Writes the layout (sizes and positions) of the dock windows in the + dock areas of the QMainWindow \a mainWindow, including \c + Minimized and \c TornOff dock windows, to the text stream \a ts. + + This can be used, for example, in conjunction with QSettings to + save the user's layout when the \mainWindow receives a closeEvent. + + \sa operator>>() closeEvent() +*/ + +QTextStream &operator<<( QTextStream &ts, const QMainWindow &mainWindow ) +{ + QPtrList<QDockWindow> l = mainWindow.dockWindows( Qt::DockMinimized ); + QDockWindow *dw = 0; + for ( dw = l.first(); dw; dw = l.next() ) { + ts << dw->caption(); + ts << ","; + } + ts << endl; + + l = mainWindow.dockWindows( Qt::DockTornOff ); + for ( dw = l.first(); dw; dw = l.next() ) { + ts << dw->caption(); + ts << ","; + } + ts << endl; + for ( dw = l.first(); dw; dw = l.next() ) { + ts << "[" << dw->caption() << "," + << (int)dw->geometry().x() << "," + << (int)dw->geometry().y() << "," + << (int)dw->geometry().width() << "," + << (int)dw->geometry().height() << "," + << (int)dw->isVisible() << "]"; + } + ts << endl; + + saveDockArea( ts, mainWindow.topDock() ); + saveDockArea( ts, mainWindow.bottomDock() ); + saveDockArea( ts, mainWindow.rightDock() ); + saveDockArea( ts, mainWindow.leftDock() ); + return ts; +} + +static void loadDockArea( const QStringList &names, QDockArea *a, Qt::Dock d, QPtrList<QDockWindow> &l, QMainWindow *mw, QTextStream &ts ) +{ + for ( QStringList::ConstIterator it = names.begin(); it != names.end(); ++it ) { + for ( QDockWindow *dw = l.first(); dw; dw = l.next() ) { + if ( dw->caption() == *it ) { + mw->addDockWindow( dw, d ); + break; + } + } + } + if ( a ) { + ts >> *a; + } else if ( d == Qt::DockTornOff ) { + QString s = ts.readLine(); + enum State { Pre, Name, X, Y, Width, Height, Visible, Post }; + int state = Pre; + QString name, x, y, w, h, visible; + QChar c; + for ( int i = 0; i < (int)s.length(); ++i ) { + c = s[ i ]; + if ( state == Pre && c == '[' ) { + state++; + continue; + } + if ( c == ',' && + ( state == Name || state == X || state == Y || state == Width || state == Height ) ) { + state++; + continue; + } + if ( state == Visible && c == ']' ) { + for ( QDockWindow *dw = l.first(); dw; dw = l.next() ) { + if ( QString( dw->caption() ) == name ) { + if ( !::qt_cast<QToolBar*>(dw) ) + dw->setGeometry( x.toInt(), y.toInt(), w.toInt(), h.toInt() ); + else + dw->setGeometry( x.toInt(), y.toInt(), dw->width(), dw->height() ); + if ( !(bool)visible.toInt() ) + dw->hide(); + else + dw->show(); + break; + } + } + + name = x = y = w = h = visible = ""; + + state = Pre; + continue; + } + if ( state == Name ) + name += c; + else if ( state == X ) + x += c; + else if ( state == Y ) + y += c; + else if ( state == Width ) + w += c; + else if ( state == Height ) + h += c; + else if ( state == Visible ) + visible += c; + } + } +} + +/*! + \relates QMainWindow + + Reads the layout (sizes and positions) of the dock windows in the + dock areas of the QMainWindow \a mainWindow from the text stream, + \a ts, including \c Minimized and \c TornOff dock windows. + Restores the dock windows and dock areas to these sizes and + positions. The layout information must be in the format produced + by operator<<(). + + This can be used, for example, in conjunction with QSettings to + restore the user's layout. + + \sa operator<<() +*/ + +QTextStream &operator>>( QTextStream &ts, QMainWindow &mainWindow ) +{ + QPtrList<QDockWindow> l = mainWindow.dockWindows(); + + QString s = ts.readLine(); + QStringList names = QStringList::split( ',', s ); + loadDockArea( names, 0, Qt::DockMinimized, l, &mainWindow, ts ); + + s = ts.readLine(); + names = QStringList::split( ',', s ); + loadDockArea( names, 0, Qt::DockTornOff, l, &mainWindow, ts ); + + int i = 0; + QDockArea *areas[] = { mainWindow.topDock(), mainWindow.bottomDock(), mainWindow.rightDock(), mainWindow.leftDock() }; + for ( int d = (int)Qt::DockTop; d != (int)Qt::DockMinimized; ++d, ++i ) { + s = ts.readLine(); + names = QStringList::split( ',', s ); + loadDockArea( names, areas[ i ], (Qt::Dock)d, l, &mainWindow, ts ); + } + return ts; +} +#endif + +#include "qmainwindow.moc" + +#endif diff --git a/src/widgets/qmainwindow.h b/src/widgets/qmainwindow.h new file mode 100644 index 0000000..1d18c7e --- /dev/null +++ b/src/widgets/qmainwindow.h @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Definition of QMainWindow class +** +** Created : 980316 +** +** 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. +** +**********************************************************************/ + +#ifndef QMAINWINDOW_H +#define QMAINWINDOW_H + +#ifndef QT_H +#include "qwidget.h" +#include "qtoolbar.h" +#include "qptrlist.h" +#include "qtextstream.h" +#endif // QT_H + +#ifndef QT_NO_MAINWINDOW + +class QMenuBar; +class QStatusBar; +class QToolTipGroup; +class QMainWindowPrivate; +class QMainWindowLayout; +class QPopupMenu; + +class Q_EXPORT QMainWindow: public QWidget +{ + Q_OBJECT + Q_PROPERTY( bool rightJustification READ rightJustification WRITE setRightJustification DESIGNABLE false ) + Q_PROPERTY( bool usesBigPixmaps READ usesBigPixmaps WRITE setUsesBigPixmaps ) + Q_PROPERTY( bool usesTextLabel READ usesTextLabel WRITE setUsesTextLabel ) + Q_PROPERTY( bool dockWindowsMovable READ dockWindowsMovable WRITE setDockWindowsMovable ) + Q_PROPERTY( bool opaqueMoving READ opaqueMoving WRITE setOpaqueMoving ) + +public: + QMainWindow( QWidget* parent=0, const char* name=0, WFlags f = WType_TopLevel ); + ~QMainWindow(); + +#ifndef QT_NO_MENUBAR + QMenuBar * menuBar() const; +#endif + QStatusBar * statusBar() const; +#ifndef QT_NO_TOOLTIP + QToolTipGroup * toolTipGroup() const; +#endif + + virtual void setCentralWidget( QWidget * ); + QWidget * centralWidget() const; + + virtual void setDockEnabled( Dock dock, bool enable ); + bool isDockEnabled( Dock dock ) const; + bool isDockEnabled( QDockArea *area ) const; + virtual void setDockEnabled( QDockWindow *tb, Dock dock, bool enable ); + bool isDockEnabled( QDockWindow *tb, Dock dock ) const; + bool isDockEnabled( QDockWindow *tb, QDockArea *area ) const; + + virtual void addDockWindow( QDockWindow *, Dock = DockTop, bool newLine = FALSE ); + virtual void addDockWindow( QDockWindow *, const QString &label, + Dock = DockTop, bool newLine = FALSE ); + virtual void moveDockWindow( QDockWindow *, Dock = DockTop ); + virtual void moveDockWindow( QDockWindow *, Dock, bool nl, int index, int extraOffset = -1 ); + virtual void removeDockWindow( QDockWindow * ); + + void show(); + void hide(); + QSize sizeHint() const; + QSize minimumSizeHint() const; + + bool rightJustification() const; + bool usesBigPixmaps() const; + bool usesTextLabel() const; + bool dockWindowsMovable() const; + bool opaqueMoving() const; + + bool eventFilter( QObject*, QEvent* ); + + bool getLocation( QDockWindow *tb, Dock &dock, int &index, bool &nl, int &extraOffset ) const; + + QPtrList<QDockWindow> dockWindows( Dock dock ) const; + QPtrList<QDockWindow> dockWindows() const; + void lineUpDockWindows( bool keepNewLines = FALSE ); + + bool isDockMenuEnabled() const; + + // compatibility stuff + bool hasDockWindow( QDockWindow *dw ); +#ifndef QT_NO_TOOLBAR + void addToolBar( QDockWindow *, Dock = DockTop, bool newLine = FALSE ); + void addToolBar( QDockWindow *, const QString &label, + Dock = DockTop, bool newLine = FALSE ); + void moveToolBar( QDockWindow *, Dock = DockTop ); + void moveToolBar( QDockWindow *, Dock, bool nl, int index, int extraOffset = -1 ); + void removeToolBar( QDockWindow * ); + + bool toolBarsMovable() const; + QPtrList<QToolBar> toolBars( Dock dock ) const; + void lineUpToolBars( bool keepNewLines = FALSE ); +#endif + + virtual QDockArea *dockingArea( const QPoint &p ); + QDockArea *leftDock() const; + QDockArea *rightDock() const; + QDockArea *topDock() const; + QDockArea *bottomDock() const; + + virtual bool isCustomizable() const; + + bool appropriate( QDockWindow *dw ) const; + + enum DockWindows { OnlyToolBars, NoToolBars, AllDockWindows }; + QPopupMenu *createDockWindowMenu( DockWindows dockWindows = AllDockWindows ) const; + +public slots: + virtual void setRightJustification( bool ); + virtual void setUsesBigPixmaps( bool ); + virtual void setUsesTextLabel( bool ); + virtual void setDockWindowsMovable( bool ); + virtual void setOpaqueMoving( bool ); + virtual void setDockMenuEnabled( bool ); + virtual void whatsThis(); + virtual void setAppropriate( QDockWindow *dw, bool a ); + virtual void customize(); + + // compatibility stuff + void setToolBarsMovable( bool ); + +signals: + void pixmapSizeChanged( bool ); + void usesTextLabelChanged( bool ); + void dockWindowPositionChanged( QDockWindow * ); + +#ifndef QT_NO_TOOLBAR + // compatibility stuff + void toolBarPositionChanged( QToolBar * ); +#endif + +protected slots: + virtual void setUpLayout(); + virtual bool showDockMenu( const QPoint &globalPos ); + void menuAboutToShow(); + +protected: + void paintEvent( QPaintEvent * ); + void childEvent( QChildEvent * ); + bool event( QEvent * ); + void styleChange( QStyle& ); + +private slots: + void slotPlaceChanged(); + void doLineUp() { lineUpDockWindows( TRUE ); } + +private: + QMainWindowPrivate * d; + void triggerLayout( bool deleteLayout = TRUE); + bool dockMainWindow( QObject *dock ); + +#ifndef QT_NO_MENUBAR + virtual void setMenuBar( QMenuBar * ); +#endif + virtual void setStatusBar( QStatusBar * ); +#ifndef QT_NO_TOOLTIP + virtual void setToolTipGroup( QToolTipGroup * ); +#endif + + friend class QDockWindow; + friend class QMenuBar; + friend class QHideDock; + friend class QToolBar; + friend class QMainWindowLayout; +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QMainWindow( const QMainWindow & ); + QMainWindow& operator=( const QMainWindow & ); +#endif +}; + +#ifndef QT_NO_TOOLBAR +inline void QMainWindow::addToolBar( QDockWindow *w, ToolBarDock dock, bool newLine ) +{ + addDockWindow( w, dock, newLine ); +} + +inline void QMainWindow::addToolBar( QDockWindow *w, const QString &label, + ToolBarDock dock, bool newLine ) +{ + addDockWindow( w, label, dock, newLine ); +} + +inline void QMainWindow::moveToolBar( QDockWindow *w, ToolBarDock dock ) +{ + moveDockWindow( w, dock ); +} + +inline void QMainWindow::moveToolBar( QDockWindow *w, ToolBarDock dock, bool nl, int index, int extraOffset ) +{ + moveDockWindow( w, dock, nl, index, extraOffset ); +} + +inline void QMainWindow::removeToolBar( QDockWindow *w ) +{ + removeDockWindow( w ); +} + +inline bool QMainWindow::toolBarsMovable() const +{ + return dockWindowsMovable(); +} + +inline void QMainWindow::lineUpToolBars( bool keepNewLines ) +{ + lineUpDockWindows( keepNewLines ); +} + +inline void QMainWindow::setToolBarsMovable( bool b ) +{ + setDockWindowsMovable( b ); +} +#endif + +#ifndef QT_NO_TEXTSTREAM +Q_EXPORT QTextStream &operator<<( QTextStream &, const QMainWindow & ); +Q_EXPORT QTextStream &operator>>( QTextStream &, QMainWindow & ); +#endif + +#endif // QT_NO_MAINWINDOW + +#endif // QMAINWINDOW_H diff --git a/src/widgets/qmenubar.cpp b/src/widgets/qmenubar.cpp new file mode 100644 index 0000000..45e0ffb --- /dev/null +++ b/src/widgets/qmenubar.cpp @@ -0,0 +1,1683 @@ +/**************************************************************************** +** +** Implementation of QMenuBar class +** +** Created : 941209 +** +** 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. +** +**********************************************************************/ + +// qmainwindow.h before qmenubar.h because of GCC-2.7.* compatibility +// ### could be reorganised by discarding INCLUDE_MENUITEM_DEF and put +// the relevant declarations in a private header? +#include "qmainwindow.h" +#ifndef QT_NO_MENUBAR +#include "qmenubar.h" +#include "qpopupmenu.h" +#include "qaccel.h" +#include "qpainter.h" +#include "qdrawutil.h" +#include "qapplication.h" +#include "qguardedptr.h" +#include "qlayout.h" +#include "qcleanuphandler.h" +#include "../kernel/qinternal_p.h" +#include "qstyle.h" +#include "qtimer.h" +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif + +class QMenuDataData { + // attention: also defined in qmenudata.cpp +public: + QMenuDataData(); + QGuardedPtr<QWidget> aWidget; + int aInt; +}; + +#if defined(QT_ACCESSIBILITY_SUPPORT) +static bool inMenu = FALSE; +#endif + +#if defined(Q_WS_X11) +extern int qt_xfocusout_grab_counter; // defined in qapplication_x11.cpp +#endif + +/*! + \class QMenuBar qmenubar.h + \brief The QMenuBar class provides a horizontal menu bar. + + \ingroup application + \mainclass + + A menu bar consists of a list of pull-down menu items. You add + menu items with \link QMenuData::insertItem() + insertItem()\endlink. For example, asuming that \c menubar is a + pointer to a QMenuBar and \c filemenu is a pointer to a + QPopupMenu, the following statement inserts the menu into the menu + bar: + \code + menubar->insertItem( "&File", filemenu ); + \endcode + The ampersand in the menu item's text sets Alt+F as a shortcut for + this menu. (You can use "\&\&" to get a real ampersand in the menu + bar.) + + Items are either enabled or disabled. You toggle their state with + setItemEnabled(). + + There is no need to lay out a menu bar. It automatically sets its + own geometry to the top of the parent widget and changes it + appropriately whenever the parent is resized. + + \important insertItem removeItem clear insertSeparator setItemEnabled isItemEnabled setItemVisible isItemVisible + + Example of creating a menu bar with menu items (from \l menu/menu.cpp): + \quotefile menu/menu.cpp + \skipto file = new QPopupMenu + \printline + \skipto Key_O + \printline + \printline + \skipto new QMenuBar + \printline + \skipto insertItem + \printline + + In most main window style applications you would use the menuBar() + provided in QMainWindow, adding \l{QPopupMenu}s to the menu bar + and adding \l{QAction}s to the popup menus. + + Example (from \l action/application.cpp): + \quotefile action/application.cpp + \skipto file = new QPopupMenu + \printuntil fileNewAction + + Menu items can have text and pixmaps (or iconsets), see the + various \link QMenuData::insertItem() insertItem()\endlink + overloads, as well as separators, see \link + QMenuData::insertSeparator() insertSeparator()\endlink. You can + also add custom menu items that are derived from + \l{QCustomMenuItem}. + + Menu items may be removed with removeItem() and enabled or + disabled with \link QMenuData::setItemEnabled() + setItemEnabled()\endlink. + + <img src=qmenubar-m.png> <img src=qmenubar-w.png> + + \section1 QMenuBar on Qt/Mac + + QMenuBar on Qt/Mac is a wrapper for using the system-wide menubar. + If you have multiple menubars in one dialog the outermost menubar + (normally inside a widget with widget flag \c WType_TopLevel) will + be used for the system-wide menubar. + + Note that arbitrary Qt widgets \e cannot be inserted into a + QMenuBar on the Mac because Qt uses Mac's native menus which don't + support this functionality. This limitation does not apply to + stand-alone QPopupMenus. + + Qt/Mac also provides a menubar merging feature to make QMenuBar + conform more closely to accepted Mac OS X menubar layout. The + merging functionality is based on string matching the title of a + QPopupMenu entry. These strings are translated (using + QObject::tr()) in the "QMenuBar" context. If an entry is moved its + slots will still fire as if it was in the original place. The + table below outlines the strings looked for and where the entry is + placed if matched: + + \table + \header \i String matches \i Placement \i Notes + \row \i about.* + \i Application Menu | About <application name> + \i If this entry is not found no About item will appear in + the Application Menu + \row \i config, options, setup, settings or preferences + \i Application Menu | Preferences + \i If this entry is not found the Settings item will be disabled + \row \i quit or exit + \i Application Menu | Quit <application name> + \i If this entry is not found a default Quit item will be + created to call QApplication::quit() + \endtable + + \link menu-example.html menu/menu.cpp\endlink is an example of + QMenuBar and QPopupMenu use. + + \sa QPopupMenu QAccel QAction \link http://developer.apple.com/techpubs/macosx/Carbon/HumanInterfaceToolbox/Aqua/aqua.html Aqua Style Guidelines \endlink \link guibooks.html#fowler GUI Design Handbook: Menu Bar \endlink +*/ + + +/*! + \enum QMenuBar::Separator + + This enum type is used to decide whether QMenuBar should draw a + separator line at its bottom. + + \value Never In many applications there is already a separator, + and having two looks wrong. + + \value InWindowsStyle In some other applications a separator looks + good in Windows style, but nowhere else. +*/ + +/*! + \fn void QMenuBar::activated( int id ) + + This signal is emitted when a menu item is selected; \a id is the + id of the selected item. + + Normally you will 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 QMenuBar::highlighted( int id ) + + This signal is emitted when a menu item is highlighted; \a id is + the id of the highlighted item. + + Normally, you will 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 activated(), QMenuData::insertItem() +*/ + + +// Motif style parameters + +static const int motifBarHMargin = 2; // menu bar hor margin to item +static const int motifBarVMargin = 1; // menu bar ver margin to item +static const int motifItemFrame = 2; // menu item frame width +static const int motifItemHMargin = 5; // menu item hor text margin +static const int motifItemVMargin = 4; // menu item ver text margin + +// The others are 0 +static const int gtkItemHMargin = 8; +static const int gtkItemVMargin = 8; + +/* + ++----------------------------- +| BarFrame +| +------------------------- +| | V BarMargin +| | +--------------------- +| | H | ItemFrame +| | | +----------------- +| | | | \ +| | | | ^ T E X T ^ | ItemVMargin +| | | | | | / +| | | ItemHMargin +| | +| + +*/ + + +/***************************************************************************** + QMenuBar member functions + *****************************************************************************/ + + +/*! + Constructs a menu bar called \a name with parent \a parent. +*/ +QMenuBar::QMenuBar( QWidget *parent, const char *name ) + : QFrame( parent, name, WNoAutoErase ) +{ +#if defined( Q_WS_MAC ) && !defined(QMAC_QMENUBAR_NO_NATIVE) + mac_eaten_menubar = FALSE; + mac_d = 0; + macCreateNativeMenubar(); +#endif + isMenuBar = TRUE; +#ifndef QT_NO_ACCEL + autoaccel = 0; +#endif + irects = 0; + rightSide = 0; // Right of here is rigth-aligned content + mseparator = 0; + waitforalt = 0; + popupvisible = 0; + hasmouse = 0; + defaultup = 0; + toggleclose = 0; + pendingDelayedContentsChanges = 0; + pendingDelayedStateChanges = 0; + if ( parent ) { + // filter parent events for resizing + parent->installEventFilter( this ); + + // filter top-level-widget events for accelerators + QWidget *tlw = topLevelWidget(); + if ( tlw != parent ) + tlw->installEventFilter( this ); + } + installEventFilter( this ); + + setBackgroundMode( PaletteButton ); + setFrameStyle( QFrame::MenuBarPanel | QFrame::Raised ); + + QFontMetrics fm = fontMetrics(); + + int h; + int gs = style().styleHint(QStyle::SH_GUIStyle); + if (gs == GtkStyle) { + h = fm.height() + gtkItemVMargin; + } else { + h = 2*motifBarVMargin + fm.height() + motifItemVMargin + 2*frameWidth() + 2*motifItemFrame; + } + + setGeometry( 0, 0, width(), h ); + + setMouseTracking( style().styleHint(QStyle::SH_MenuBar_MouseTracking) ); +} + + + +/*! \reimp */ + +void QMenuBar::styleChange( QStyle& old ) +{ + setMouseTracking( style().styleHint(QStyle::SH_MenuBar_MouseTracking) ); + QFrame::styleChange( old ); +} + + + +/*! + Destroys the menu bar. +*/ + +QMenuBar::~QMenuBar() +{ +#ifndef QT_NO_ACCEL + delete autoaccel; +#endif +#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE) + macRemoveNativeMenubar(); +#endif + if ( irects ) // Avoid purify complaint. + delete [] irects; +} + +/*! + \internal + + Repaints the menu item with id \a id; does nothing if there is no + such menu item. +*/ +void QMenuBar::updateItem( int id ) +{ + int i = indexOf( id ); + if ( i >= 0 && irects ) + repaint( irects[i], FALSE ); +} + +#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE) +static bool fromFrameChange = FALSE; +#endif + +/*! + Recomputes the menu bar's display data according to the new + contents. + + You should never need to call this; it is called automatically by + QMenuData whenever it needs to be called. +*/ + +void QMenuBar::menuContentsChanged() +{ + // here the part that can't be delayed + QMenuData::menuContentsChanged(); + badSize = TRUE; // might change the size + if( pendingDelayedContentsChanges ) + return; + pendingDelayedContentsChanges = 1; + if( !pendingDelayedStateChanges )// if the timer hasn't been started yet + QTimer::singleShot( 0, this, SLOT(performDelayedChanges())); +} + +void QMenuBar::performDelayedContentsChanged() +{ + pendingDelayedContentsChanges = 0; + // here the part the can be delayed +#ifndef QT_NO_ACCEL + // if performDelayedStateChanged() will be called too, + // it will call setupAccelerators() too, no need to do it twice + if( !pendingDelayedStateChanges ) + setupAccelerators(); +#endif + calculateRects(); + if ( isVisible() ) { + update(); +#ifndef QT_NO_MAINWINDOW + QMainWindow *mw = ::qt_cast<QMainWindow*>(parent()); + if ( mw ) { + mw->triggerLayout(); + mw->update(); + } +#endif +#ifndef QT_NO_LAYOUT + if ( parentWidget() && parentWidget()->layout() ) + parentWidget()->layout()->activate(); +#endif + } +} + +/*! + Recomputes the menu bar's display data according to the new state. + + You should never need to call this; it is called automatically by + QMenuData whenever it needs to be called. +*/ + +void QMenuBar::menuStateChanged() +{ + if( pendingDelayedStateChanges ) + return; + pendingDelayedStateChanges = 1; + if( !pendingDelayedContentsChanges ) // if the timer hasn't been started yet + QTimer::singleShot( 0, this, SLOT(performDelayedChanges())); +} + +void QMenuBar::performDelayedStateChanged() +{ + pendingDelayedStateChanges = 0; + // here the part that can be delayed +#ifndef QT_NO_ACCEL + setupAccelerators(); // ### 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(); +} + + +void QMenuBar::performDelayedChanges() +{ +#if defined(Q_WS_MAC) && !defined(QMAC_MENUBAR_NO_NATIVE) + // I must do this here as the values change in the function below. + bool needMacUpdate = (pendingDelayedContentsChanges || pendingDelayedStateChanges); +#endif + if( pendingDelayedContentsChanges ) + performDelayedContentsChanged(); + if( pendingDelayedStateChanges ) + performDelayedStateChanged(); +#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE) + if(mac_eaten_menubar && needMacUpdate) { + macDirtyNativeMenubar(); + + bool all_hidden = TRUE; + if(irects) { + for(int i = 0; all_hidden && i < (int)mitems->count(); i++) + all_hidden = irects[i].isEmpty(); + } + if( all_hidden ) { + if( !isHidden()) + hide(); + } else { + if( !isShown() && !fromFrameChange ) + show(); + } + } +#endif +} + + +void QMenuBar::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 QMenuBar::menuDelPopup( QPopupMenu *popup ) +{ + popup->disconnect( SIGNAL(activatedRedirect(int)) ); + popup->disconnect( SIGNAL(highlightedRedirect(int)) ); + disconnect( popup, SIGNAL(destroyed(QObject*)), + this, SLOT(popupDestroyed(QObject*)) ); +} + +void QMenuBar::frameChanged() +{ +#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE) + fromFrameChange = TRUE; +#endif + menuContentsChanged(); +#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE) + fromFrameChange = FALSE; +#endif +} + +void QMenuBar::languageChange() +{ + menuContentsChanged(); +} + +/*! + \internal + + This function is used to adjust the menu bar's geometry to the + parent widget's geometry. Note that this is \e not part of the + public interface - the function is \c public only because + QObject::eventFilter() is. + + Resizes the menu bar to fit in the parent widget when the parent + receives a resize event. +*/ + +bool QMenuBar::eventFilter( QObject *object, QEvent *event ) +{ + if ( object == parent() && object +#ifndef QT_NO_TOOLBAR + && !::qt_cast<QToolBar*>(object) +#endif + && event->type() == QEvent::Resize ) { + QResizeEvent *e = (QResizeEvent *)event; + int w = e->size().width(); + setGeometry( 0, y(), w, heightForWidth(w) ); + return FALSE; + } + + if ( !isVisible() || !object->isWidgetType() ) + return FALSE; + + if ( object == this && event->type() == QEvent::LanguageChange ) { + badSize = TRUE; + calculateRects(); + return FALSE; + } else if ( event->type() == QEvent::MouseButtonPress || + event->type() == QEvent::MouseButtonRelease ) { + waitforalt = 0; + return FALSE; + } else if ( waitforalt && event->type() == QEvent::FocusOut ) { + // some window systems/managers use alt/meta as accelerator keys + // for switching between windows/desktops/etc. If the focus + // widget gets unfocused, then we need to stop waiting for alt + // NOTE: this event came from the real focus widget, so we don't + // need to touch the event filters + waitforalt = 0; + // although the comment above said not to remove the event filter, it is + // incorrect. We need to remove our self fom the focused widget as normally + // this happens in the key release but it does not happen in this case + QWidget * f = ((QWidget *)object)->focusWidget(); + if (f) + f->removeEventFilter( this ); + return FALSE; + } else if ( !( event->type() == QEvent::Accel || + event->type() == QEvent::AccelOverride || + event->type() == QEvent::KeyPress || + event->type() == QEvent::KeyRelease ) || + !style().styleHint(QStyle::SH_MenuBar_AltKeyNavigation, this) ) { + return FALSE; + } + + QKeyEvent * ke = (QKeyEvent *) event; +#ifndef QT_NO_ACCEL + // look for Alt press and Alt-anything press + if ( event->type() == QEvent::Accel ) { + QWidget * f = ((QWidget *)object)->focusWidget(); + // ### this thinks alt and meta are the same + if ( ke->key() == Key_Alt || ke->key() == Key_Meta ) { + // A new Alt press and we wait for release, eat + // this key and don't wait for Alt on this widget + if ( waitforalt ) { + waitforalt = 0; + if ( object->parent() ) + object->removeEventFilter( this ); + ke->accept(); + return TRUE; + // Menu has focus, send focus back + } else if ( hasFocus() ) { + setAltMode( FALSE ); + ke->accept(); + return TRUE; + // Start waiting for Alt release on focus widget + } else if ( ke->stateAfter() == AltButton ) { + waitforalt = 1; +#if defined(Q_WS_X11) + QMenuData::d->aInt = qt_xfocusout_grab_counter; +#endif + if ( f && f != object ) + f->installEventFilter( this ); + } + // Other modifiers kills focus on menubar + } else if ( ke->key() == Key_Control || ke->key() == Key_Shift) { + setAltMode( FALSE ); + // Got other key, no need to wait for Alt release + } else { + waitforalt = 0; + } + // ### ! block all accelerator events when the menu bar is active + if ( qApp && qApp->focusWidget() == this ) { + return TRUE; + } + + return FALSE; + } +#endif + // look for Alt release + if ( ((QWidget*)object)->focusWidget() == object || + (object->parent() == 0 && ((QWidget*)object)->focusWidget() == 0) ) { + if ( waitforalt && event->type() == QEvent::KeyRelease && + ( ke->key() == Key_Alt || ke->key() == Key_Meta ) +#if defined(Q_WS_X11) + && QMenuData::d->aInt == qt_xfocusout_grab_counter +#endif + ) { + setAltMode( TRUE ); + if ( object->parent() ) + object->removeEventFilter( this ); + QWidget * tlw = ((QWidget *)object)->topLevelWidget(); + if ( tlw ) { + // ### ! + // make sure to be the first event filter, so we can kill + // accelerator events before the accelerators get to them. + tlw->removeEventFilter( this ); + tlw->installEventFilter( this ); + } + return TRUE; + // Cancel if next keypress is NOT Alt/Meta, + } else if ( !hasFocus() && (event->type() == QEvent::AccelOverride ) && + !(((QKeyEvent *)event)->key() == Key_Alt || + ((QKeyEvent *)event)->key() == Key_Meta) ) { + if ( object->parent() ) + object->removeEventFilter( this ); + setAltMode( FALSE ); + } + } + + return FALSE; // don't stop event +} + + + +/*! + \internal + Receives signals from menu items. +*/ + +void QMenuBar::subActivated( int id ) +{ + emit activated( id ); +} + +/*! + \internal + Receives signals from menu items. +*/ + +void QMenuBar::subHighlighted( int id ) +{ + emit highlighted( id ); +} + +/*! + \internal + Receives signals from menu accelerator. +*/ +#ifndef QT_NO_ACCEL +void QMenuBar::accelActivated( int id ) +{ +#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE) + if(mac_eaten_menubar) + return; +#endif + if ( !isEnabled() ) // the menu bar is disabled + return; + setAltMode( TRUE ); + setActiveItem( indexOf( id ) ); +} +#endif + +/*! + \internal + This slot receives signals from menu accelerator when it is about to be + destroyed. +*/ +#ifndef QT_NO_ACCEL +void QMenuBar::accelDestroyed() +{ + autoaccel = 0; // don't delete it twice! +} +#endif + +void QMenuBar::popupDestroyed( QObject *o ) +{ + removePopup( (QPopupMenu*)o ); +} + +bool QMenuBar::tryMouseEvent( QPopupMenu *, QMouseEvent *e ) +{ + QPoint pos = mapFromGlobal( e->globalPos() ); + if ( !rect().contains( pos ) ) // outside + return FALSE; + int item = itemAtPos( pos ); + if ( item == -1 && (e->type() == QEvent::MouseButtonPress || + e->type() == QEvent::MouseButtonRelease) ) { + hidePopups(); + goodbye(); + return FALSE; + } + QMouseEvent ee( e->type(), pos, e->globalPos(), e->button(), e->state() ); + event( &ee ); + return TRUE; +} + + +void QMenuBar::tryKeyEvent( QPopupMenu *, QKeyEvent *e ) +{ + event( e ); +} + + +void QMenuBar::goodbye( bool cancelled ) +{ + mouseBtDn = FALSE; + popupvisible = 0; + setAltMode( cancelled && style().styleHint(QStyle::SH_MenuBar_AltKeyNavigation, this) ); +} + + +void QMenuBar::openActPopup() +{ +#if defined(QT_ACCESSIBILITY_SUPPORT) + if ( !inMenu ) { + QAccessible::updateAccessibility( this, 0, QAccessible::MenuStart ); + inMenu = TRUE; + } +#endif + + if ( actItem < 0 ) + return; + QPopupMenu *popup = mitems->at(actItem)->popup(); + if ( !popup || !popup->isEnabled() ) + return; + + QRect r = itemRect( actItem ); + bool reverse = QApplication::reverseLayout(); + const int yoffset = 1; //(style().styleHint( QStyle::SH_GUIStyle ) == QStyle::WindowsStyle) ? 4 : 1; ### this breaks designer mainwindow editing + QPoint pos = r.bottomLeft() + QPoint(0,yoffset); + if( reverse ) { + pos = r.bottomRight() + QPoint(0,yoffset); + pos.rx() -= popup->sizeHint().width(); + } + + int ph = popup->sizeHint().height(); + pos = mapToGlobal(pos); + int sh = QApplication::desktop()->height(); + if ( defaultup || (pos.y() + ph > sh) ) { + QPoint t = mapToGlobal( r.topLeft() ); + if( reverse ) { + t = mapToGlobal( r.topRight() ); + t.rx() -= popup->sizeHint().width(); + } + t.ry() -= (QCOORD)ph; + if ( !defaultup || t.y() >= 0 ) + pos = t; + } + + //avoid circularity + if ( popup->isVisible() ) + return; + + Q_ASSERT( popup->parentMenu == 0 ); + popup->parentMenu = this; // set parent menu + + popup->snapToMouse = FALSE; + popup->popup( pos ); + popup->snapToMouse = TRUE; +} + +/*! + \internal + Hides all popup menu items. +*/ + +void QMenuBar::hidePopups() +{ +#if defined(QT_ACCESSIBILITY_SUPPORT) + bool anyVisible = FALSE; +#endif + QMenuItemListIt it(*mitems); + register QMenuItem *mi; + while ( (mi=it.current()) ) { + ++it; + if ( mi->popup() && mi->popup()->isVisible() ) { +#if defined(QT_ACCESSIBILITY_SUPPORT) + anyVisible = TRUE; +#endif + mi->popup()->hide(); + } + } +#if defined(QT_ACCESSIBILITY_SUPPORT) + if ( !popupvisible && anyVisible && inMenu ) { + QAccessible::updateAccessibility( this, 0, QAccessible::MenuEnd ); + inMenu = FALSE; + } +#endif +} + + +/*! + Reimplements QWidget::show() in order to set up the correct + keyboard accelerators and to raise itself to the top of the widget + stack. +*/ + +void QMenuBar::show() +{ +#ifndef QT_NO_ACCEL + setupAccelerators(); +#endif + + if ( parentWidget() ) + resize( parentWidget()->width(), height() ); + + QApplication::sendPostedEvents( this, QEvent::Resize ); + performDelayedChanges(); + calculateRects(); + +#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE) + if(mac_eaten_menubar) { + //If all elements are invisible no reason for me to be visible either + bool all_hidden = TRUE; + if(irects) { + for(int i = 0; all_hidden && i < (int)mitems->count(); i++) + all_hidden = irects[i].isEmpty(); + } + if(all_hidden) + QWidget::hide(); + else + QWidget::show(); + } else { + QWidget::show(); + } +#else + QWidget::show(); +#endif + +#ifndef QT_NO_MAINWINDOW + QMainWindow *mw = ::qt_cast<QMainWindow*>(parent()); + if ( mw ) //### ugly workaround + mw->triggerLayout(); +#endif + raise(); +} + +/*! + Reimplements QWidget::hide() in order to deselect any selected + item, and calls setUpLayout() for the main window. +*/ + +void QMenuBar::hide() +{ + QWidget::hide(); + setAltMode( FALSE ); + hidePopups(); +#ifndef QT_NO_MAINWINDOW + QMainWindow *mw = ::qt_cast<QMainWindow*>(parent()); + if ( mw ) //### ugly workaround + mw->triggerLayout(); +#endif +} + +/*! + \internal + Needs to change the size of the menu bar when a new font is set. +*/ + +void QMenuBar::fontChange( const QFont & f ) +{ + badSize = TRUE; + updateGeometry(); + if ( isVisible() ) + calculateRects(); + QWidget::fontChange( f ); +} + + +/***************************************************************************** + Item geometry functions + *****************************************************************************/ + +/* + This function serves two different purposes. If the parameter is + negative, it updates the irects member for the current width and + resizes. Otherwise, it does the same calculations for the GIVEN + width and returns the height to which it WOULD have resized. A bit + tricky, but both operations require almost identical steps. +*/ +int QMenuBar::calculateRects( int max_width ) +{ + polish(); + bool update = ( max_width < 0 ); + + if ( update ) { + rightSide = 0; + if ( !badSize ) // size was not changed + return 0; + delete [] irects; + int i = mitems->count(); + if ( i == 0 ) { + irects = 0; + } else { + irects = new QRect[ i ]; + Q_CHECK_PTR( irects ); + } + max_width = width(); + } + QFontMetrics fm = fontMetrics(); + int max_height = 0; + int max_item_height = 0; + int nlitems = 0; // number on items on cur line + int gs = style().styleHint(QStyle::SH_GUIStyle); + bool reverse = QApplication::reverseLayout(); + int x = frameWidth(); + int y = frameWidth(); + if ( gs == MotifStyle ) { + x += motifBarHMargin; + y += motifBarVMargin; + } else if ( style().inherits("QWindowsXPStyle") && style().styleHint(QStyle::SH_TitleBar_NoBorder) ) { + ; + } else if ( gs == WindowsStyle ) { + x += 2; + y += 2; + } + if ( reverse ) + x = max_width - x; + + int i = 0; + int separator = -1; + const int itemSpacing = style().pixelMetric(QStyle::PM_MenuBarItemSpacing); + const int lastItem = reverse ? 0 : mitems->count() - 1; + + while ( i < (int)mitems->count() ) { // for each menu item... + QMenuItem *mi = mitems->at(i); + + int w=0, h=0; + if ( !mi->isVisible() +#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE) + || (mac_eaten_menubar && !mi->custom() && !mi->widget() ) +#endif + ) { + ; // empty rectangle + } else if ( mi->widget() ) { + if ( mi->widget()->parentWidget() != this ) { + mi->widget()->reparent( this, QPoint(0,0) ); + } + w = mi->widget()->sizeHint().expandedTo( QApplication::globalStrut() ).width()+2; + h = mi->widget()->sizeHint().expandedTo( QApplication::globalStrut() ).height()+2; + if ( i && separator < 0 ) + separator = i; + } else if ( mi->pixmap() ) { // pixmap item + w = QMAX( mi->pixmap()->width() + 4, QApplication::globalStrut().width() ); + h = QMAX( mi->pixmap()->height() + 4, QApplication::globalStrut().height() ); + } else if ( !mi->text().isNull() ) { // text item + QString s = mi->text(); + if ( gs == GtkStyle ) { + w = fm.boundingRect( s ).width() + 2*gtkItemHMargin; + } else { + w = fm.boundingRect( s ).width() + 2*motifItemHMargin; + } + w -= s.contains('&')*fm.width('&'); + w += s.contains("&&")*fm.width('&'); + w = QMAX( w, QApplication::globalStrut().width() ); + if (gs == GtkStyle ) { + h = QMAX( fm.height() + gtkItemVMargin, QApplication::globalStrut().height() ); + } else { + h = QMAX( fm.height() + motifItemVMargin, QApplication::globalStrut().height() ); + } + } else if ( mi->isSeparator() ) { // separator item + if ( style().styleHint(QStyle::SH_GUIStyle) == MotifStyle ) + separator = i; //### only motif? + } + if ( !mi->isSeparator() || mi->widget() ) { +#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE) + if ( !mac_eaten_menubar ) { +#endif + if ( gs == MotifStyle && mi->isVisible() ) { + w += 2*motifItemFrame; + h += 2*motifItemFrame; + } +#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE) + } +#endif + + if ( ( ( !reverse && x + w + frameWidth() - max_width > 0 ) || + ( reverse && x - w - itemSpacing - frameWidth() < 0 ) ) + && nlitems > 0 ) { + nlitems = 0; + x = frameWidth(); + y += h; + if ( gs == MotifStyle ) { + x += motifBarHMargin; + y += motifBarVMargin; + } + if ( reverse ) + x = max_width - x + itemSpacing; + if ( style().styleHint(QStyle::SH_GUIStyle) == MotifStyle ) + separator = -1; + } + if ( y + h + 2*frameWidth() > max_height ) + max_height = y + h + 2*frameWidth(); + if ( h > max_item_height ) + max_item_height = h; + } + + const bool isLast = (i == lastItem); + + if( reverse ) { + x -= w; + if (!isLast && !mi->isSeparator()) + x -= itemSpacing; + } + if ( update ) { + irects[i].setRect( x, y, w, h ); + } + if ( !reverse ) { + x += w; + if (!isLast && !mi->isSeparator()) + x += itemSpacing; + } + nlitems++; + i++; + } + if ( gs == WindowsStyle ) { + max_height += 2; + max_width += 2; + } + + if ( update ) { + if ( separator >= 0 ) { + int moveBy = reverse ? - x - frameWidth() : max_width - x - frameWidth(); + rightSide = x; + while( --i >= separator ) { + irects[i].moveBy( moveBy, 0 ); + } + } else { + rightSide = width()-frameWidth(); + } + if ( max_height != height() ) + resize( width(), max_height ); + for ( i = 0; i < (int)mitems->count(); i++ ) { + irects[i].setHeight( max_item_height ); + QMenuItem *mi = mitems->at(i); + if ( mi->widget() ) { + QRect r ( QPoint(0,0), mi->widget()->sizeHint() ); + r.moveCenter( irects[i].center() ); + mi->widget()->setGeometry( r ); + if( mi->widget()->isHidden() ) + mi->widget()->show(); + } + } + badSize = FALSE; + } + + return max_height; +} + +/*! + Returns the height that the menu would resize itself to if its + parent (and hence itself) resized to the given \a max_width. This + can be useful for simple layout tasks in which the height of the + menu bar is needed after items have been inserted. See \l + showimg/showimg.cpp for an example of the usage. +*/ +int QMenuBar::heightForWidth(int max_width) const +{ + // Okay to cast away const, as we are not updating. + if ( max_width < 0 ) max_width = 0; + return ((QMenuBar*)this)->calculateRects( max_width ); +} + +/*! + \internal + Return the bounding rectangle for the menu item at position \a index. +*/ + +QRect QMenuBar::itemRect( int index ) +{ + calculateRects(); + return irects ? irects[index] : QRect(0,0,0,0); +} + +/*! + \internal + Return the item at \a pos, or -1 if there is no item there or if + it is a separator item. +*/ + +int QMenuBar::itemAtPos( const QPoint &pos_ ) +{ + calculateRects(); + if ( !irects ) + return -1; + int i = 0; + QPoint pos = pos_; + // Fitts' Law for edges - compensate for the extra margin + // added in calculateRects() + const int margin = 2; + pos.setX( QMAX( margin, QMIN( width() - margin, pos.x()))); + pos.setY( QMAX( margin, QMIN( height() - margin, pos.y()))); + while ( i < (int)mitems->count() ) { + if ( !irects[i].isEmpty() && irects[i].contains( pos ) ) { + QMenuItem *mi = mitems->at(i); + return mi->isSeparator() ? -1 : i; + } + ++i; + } + return -1; // no match +} + + +/*! + \property QMenuBar::separator + \brief in which cases a menubar sparator is drawn + + \obsolete +*/ +void QMenuBar::setSeparator( Separator when ) +{ + mseparator = when; +} + +QMenuBar::Separator QMenuBar::separator() const +{ + return mseparator ? InWindowsStyle : Never; +} + +/***************************************************************************** + Event handlers + *****************************************************************************/ + +/*! + Called from QFrame::paintEvent(). Draws the menu bar contents + using painter \a p. +*/ + +void QMenuBar::drawContents( QPainter *p ) +{ + performDelayedChanges(); + QRegion reg( contentsRect() ); + QColorGroup g = colorGroup(); + bool e; + + // this shouldn't happen + if ( !irects ) + return; + + for ( int i=0; i<(int)mitems->count(); i++ ) { + QMenuItem *mi = mitems->at( i ); + if ( !mi->text().isNull() || mi->pixmap() ) { + QRect r = irects[i]; + if(r.isEmpty() || !mi->isVisible()) + continue; + e = mi->isEnabledAndVisible(); + if ( e ) + g = isEnabled() ? ( isActiveWindow() ? palette().active() : + palette().inactive() ) : palette().disabled(); + else + g = palette().disabled(); + reg = reg.subtract( r ); + QSharedDoubleBuffer buffer( p, r ); + buffer.painter()->setFont( p->font() ); + buffer.painter()->setPen( p->pen() ); + buffer.painter()->setBrush( p->brush() ); + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled() && e) + flags |= QStyle::Style_Enabled; + if ( i == actItem ) + flags |= QStyle::Style_Active; + if ( actItemDown ) + flags |= QStyle::Style_Down; + if (hasFocus() || hasmouse || popupvisible) + flags |= QStyle::Style_HasFocus; + style().drawControl(QStyle::CE_MenuBarItem, buffer.painter(), this, + r, g, flags, QStyleOption(mi)); + } + } + + p->save(); + p->setClipRegion(reg); + style().drawControl(QStyle::CE_MenuBarEmptyArea, p, this, contentsRect(), g); + p->restore(); + +#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE) + if ( !mac_eaten_menubar ) +#endif + { + Qt::GUIStyle gs = (Qt::GUIStyle) style().styleHint(QStyle::SH_GUIStyle); + if ( mseparator == InWindowsStyle && gs == WindowsStyle ) { + p->setPen( g.light() ); + p->drawLine( 0, height()-1, width()-1, height()-1 ); + p->setPen( g.dark() ); + p->drawLine( 0, height()-2, width()-1, height()-2 ); + } + } +} + + +/*! + \reimp +*/ +void QMenuBar::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() != LeftButton ) + return; + mouseBtDn = TRUE; // mouse button down + int item = itemAtPos( e->pos() ); + if ( item == actItem && popupvisible ) + toggleclose = 1; + if ( item >= 0 ) { + QFocusEvent::Reason oldReason = QFocusEvent::reason(); + QMenuItem *mi = findItem( idAt( item ) ); + // we know that a popup will open, so set the reason to avoid + // itemviews to redraw their selections + if ( mi && mi->popup() ) + QFocusEvent::setReason( QFocusEvent::Popup ); + setAltMode( TRUE ); + QFocusEvent::setReason( oldReason ); + } + setActiveItem( item, TRUE, FALSE ); +} + + +/*! + \reimp +*/ +void QMenuBar::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( e->button() != LeftButton ) + return; + if ( !mouseBtDn ) + return; + mouseBtDn = FALSE; // mouse button up + int item = itemAtPos( e->pos() ); + if ( item >= 0 && !mitems->at(item)->isEnabledAndVisible() || + actItem >= 0 && !mitems->at(actItem)->isEnabledAndVisible() ) { + hidePopups(); + setActiveItem( -1 ); + return; + } + bool showMenu = TRUE; + if ( toggleclose && + // pressing an item twice closes in windows, but not in motif :/ + style().styleHint(QStyle::SH_GUIStyle) == WindowsStyle && + actItem == item ) { + showMenu = FALSE; + setAltMode( FALSE ); + } + setActiveItem( item, showMenu, !hasMouseTracking() ); + toggleclose = 0; +} + + +/*! + \reimp +*/ +void QMenuBar::mouseMoveEvent( QMouseEvent *e ) +{ + int item = itemAtPos( e->pos() ); + if ( !mouseBtDn && !popupvisible) { + if ( item >= 0 ) { + if ( !hasmouse ) { + hasmouse = 1; + if ( actItem == item ) + actItem = -1; // trigger update + } + } + setActiveItem( item, FALSE, FALSE ); + return; + } + if ( item != actItem && item >= 0 && ( popupvisible || mouseBtDn ) ) + setActiveItem( item, TRUE, FALSE ); +} + + +/*! + \reimp +*/ +void QMenuBar::leaveEvent( QEvent * e ) +{ + hasmouse = 0; + int actId = idAt( actItem ); + if ( !hasFocus() && !popupvisible ) + actItem = -1; + updateItem( actId ); + QFrame::leaveEvent( e ); +} + + +/*! + \reimp +*/ +void QMenuBar::keyPressEvent( QKeyEvent *e ) +{ + if ( actItem < 0 ) + return; + + QMenuItem *mi = 0; + int dx = 0; + + if ( e->state() & Qt::ControlButton && + ( e->key() == Qt::Key_Tab || e->key() == Qt::Key_Backtab ) ) + { + e->ignore(); + return; + } + + switch ( e->key() ) { + case Key_Left: + dx = QApplication::reverseLayout() ? 1 : -1; + break; + + case Key_Right: + case Key_Tab: + dx = QApplication::reverseLayout() ? -1 : 1; + break; + + case Key_Up: + case Key_Down: + case Key_Enter: + case Key_Return: + if ( style().styleHint(QStyle::SH_MenuBar_AltKeyNavigation) ) + setActiveItem( actItem ); + break; + + case Key_Escape: + setAltMode( FALSE ); + break; + } + + if ( dx ) { // highlight next/prev + register int i = actItem; + int c = mitems->count(); + int n = c; + while ( n-- ) { + i = i + dx; + if ( i == c ) + i = 0; + else if ( i < 0 ) + i = c - 1; + mi = mitems->at( i ); + // ### fix windows-style traversal - currently broken due to + // QMenuBar's reliance on QPopupMenu + if ( /* (style() == WindowsStyle || */ mi->isEnabledAndVisible() /* ) */ + && !mi->isSeparator() ) + break; + } + setActiveItem( i, popupvisible ); + } else if ( ( !e->state() || (e->state()&(MetaButton|AltButton)) ) && e->text().length()==1 && !popupvisible ) { + QChar c = e->text()[0].upper(); + + QMenuItemListIt it(*mitems); + QMenuItem* first = 0; + QMenuItem* currentSelected = 0; + QMenuItem* firstAfterCurrent = 0; + + register QMenuItem *m; + int indx = 0; + int clashCount = 0; + while ( (m=it.current()) ) { + ++it; + QString s = m->text(); + if ( !s.isEmpty() ) { + int i = s.find( '&' ); + if ( i >= 0 ) + { + if ( s[i+1].upper() == c ) { + clashCount++; + if ( !first ) + first = m; + if ( indx == actItem ) + currentSelected = m; + else if ( !firstAfterCurrent && currentSelected ) + firstAfterCurrent = m; + } + } + } + indx++; + } + if ( 0 == clashCount ) { + return; + } else if ( 1 == clashCount ) { + indx = indexOf( first->id() ); + } else { + // 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)) + indx = indexOf( first->id() ); + else + indx = indexOf( firstAfterCurrent->id() ); + } + + setActiveItem( indx ); + } +} + + +/*! + \reimp +*/ +void QMenuBar::resizeEvent( QResizeEvent *e ) +{ + QFrame::resizeEvent( e ); + if ( badSize ) + return; + badSize = TRUE; + calculateRects(); +} + +/* + Sets actItem to \a i and calls repaint for the changed things. + + Takes care to optimize the repainting. Assumes that + calculateRects() has been called as appropriate. +*/ + +void QMenuBar::setActiveItem( int i, bool show, bool activate_first_item ) +{ + if ( i == actItem && (uint)show == popupvisible ) + return; + + QMenuItem* mi = 0; + if ( i >= 0 ) + mi = mitems->at( i ); + if ( mi && !mi->isEnabledAndVisible() ) + return; + + popupvisible = i >= 0 ? (show) : 0; + actItemDown = popupvisible; + + if ( i < 0 || actItem < 0 ) { + // just one item needs repainting + int n = QMAX( actItem, i ); + actItem = i; + if ( irects && n >= 0 ) + repaint( irects[n], FALSE ); + } else if ( QABS(i-actItem) == 1 ) { + // two neighbouring items need repainting + int o = actItem; + actItem = i; + if ( irects ) + repaint( irects[i].unite( irects[o] ), FALSE ); + } else { + // two non-neighbouring items need repainting + int o = actItem; + actItem = i; + if ( irects ) { + repaint( irects[o], FALSE ); + repaint( irects[i], FALSE ); + } + } + + hidePopups(); + + if ( !popupvisible && actItem >= 0 && irects ) { + QRect mfrect = irects[actItem]; + setMicroFocusHint( mfrect.x(), mfrect.y(), mfrect.width(), mfrect.height(), FALSE ); + } + +#if defined(QT_ACCESSIBILITY_SUPPORT) + if ( mi ) + QAccessible::updateAccessibility( this, indexOf( mi->id() )+1, QAccessible::Focus ); +#endif + + if ( actItem < 0 || !popupvisible || !mi ) + return; + + QPopupMenu *popup = mi->popup(); + if ( popup ) { + emit highlighted( mi->id() ); + openActPopup(); + if ( activate_first_item ) + popup->setFirstItemActive(); + } else { // not a popup + goodbye( FALSE ); + if ( mi->signal() ) // activate signal + mi->signal()->activate(); + emit activated( mi->id() ); + } +} + + +void QMenuBar::setAltMode( bool enable ) +{ +#if defined(QT_ACCESSIBILITY_SUPPORT) + if ( inMenu && !enable ) { + QAccessible::updateAccessibility( this, 0, QAccessible::MenuEnd ); + inMenu = FALSE; + } else if ( !inMenu && enable ) { + QAccessible::updateAccessibility( this, 0, QAccessible::MenuStart ); + inMenu = TRUE; + } +#endif + + waitforalt = 0; + actItemDown = FALSE; + if ( enable ) { + if ( !QMenuData::d->aWidget ) + QMenuData::d->aWidget = qApp->focusWidget(); + setFocus(); + updateItem( idAt( actItem ) ); + } else { + // set the focus back to the previous widget if + // we still have the focus. + if ( qApp->focusWidget() == this ) { + if ( QMenuData::d->aWidget ) + QMenuData::d->aWidget->setFocus(); + else + clearFocus(); + } + int actId = idAt( actItem ); + actItem = -1; + updateItem( actId ); + QMenuData::d->aWidget = 0; + } +} + +/*! + Sets up keyboard accelerators for the menu bar. +*/ +#ifndef QT_NO_ACCEL + +void QMenuBar::setupAccelerators() +{ + delete autoaccel; + autoaccel = 0; + + QMenuItemListIt it(*mitems); + register QMenuItem *mi; + while ( (mi=it.current()) ) { + ++it; + if ( !mi->isEnabledAndVisible() ) // ### when we have a good solution for the accel vs. focus widget problem, remove that. That is only a workaround + continue; + QString s = mi->text(); + if ( !s.isEmpty() ) { + int i = QAccel::shortcutKey( s ); + if ( i ) { + if ( !autoaccel ) { + autoaccel = new QAccel( this ); + Q_CHECK_PTR( autoaccel ); + autoaccel->setIgnoreWhatsThis( TRUE ); + connect( autoaccel, SIGNAL(activated(int)), + SLOT(accelActivated(int)) ); + connect( autoaccel, SIGNAL(activatedAmbiguously(int)), + SLOT(accelActivated(int)) ); + connect( autoaccel, SIGNAL(destroyed()), + SLOT(accelDestroyed()) ); + } + autoaccel->insertItem( i, mi->id() ); + } + } + if ( mi->popup() ) { + QPopupMenu* popup = mi->popup(); + popup->updateAccel( this ); + if ( !popup->isEnabled() ) + popup->enableAccel( FALSE ); + } + } +} +#endif + +/*! + \reimp + */ +bool QMenuBar::customWhatsThis() const +{ + return TRUE; +} + + + +/*! + \reimp + */ +void QMenuBar::focusInEvent( QFocusEvent * ) +{ + if ( actItem < 0 ) { + int i = -1; + while ( actItem < 0 && ++i < (int) mitems->count() ) { + QMenuItem* mi = mitems->at( i ); + if ( mi && mi->isEnabledAndVisible() && !mi->isSeparator() ) + setActiveItem( i, FALSE ); + } + } else if ( !popupvisible ) { + updateItem( idAt( actItem ) ); + } +} + +/*! + \reimp + */ +void QMenuBar::focusOutEvent( QFocusEvent * ) +{ + updateItem( idAt( actItem ) ); + if ( !popupvisible ) + setAltMode( FALSE ); +} + +/*! + \reimp +*/ + +QSize QMenuBar::sizeHint() const +{ + int h = height(); + if ( badSize ) + h = ( (QMenuBar*)this )->calculateRects(); + QSize s( 2*frameWidth(),0); + if ( irects ) { + for ( int i = 0; i < (int)mitems->count(); ++i ) + s.setWidth( s.width() + irects[ i ].width() + 2 ); + } + s.setHeight( h ); + return (style().sizeFromContents(QStyle::CT_MenuBar, this, s. + expandedTo(QApplication::globalStrut()))); +} + +/*! + \reimp +*/ + +QSize QMenuBar::minimumSize() const +{ +#ifndef QT_NO_TOOLBAR + QToolBar *tb = ::qt_cast<QToolBar*>(parent()); + if ( tb ) + return sizeHint(); +#endif + return QFrame::minimumSize(); +} + +/*! + \reimp +*/ + +QSize QMenuBar::minimumSizeHint() const +{ + return minimumSize(); +} + +/*! + \property QMenuBar::defaultUp + \brief the popup orientation + + The default popup orientation. By default, menus pop "down" the + screen. By setting the property to TRUE, the menu will pop "up". + You might call this for menus that are \e below the document to + which they refer. + + If the menu would not fit on the screen, the other direction is + used automatically. +*/ +void QMenuBar::setDefaultUp( bool on ) +{ + defaultup = on; +} + +bool QMenuBar::isDefaultUp() const +{ + return defaultup; +} + + +/*! + \reimp + */ +void QMenuBar::activateItemAt( int index ) +{ + if ( index >= 0 && index < (int) mitems->count() ) + setActiveItem( index ); + else + goodbye( FALSE ); +} + +#endif // QT_NO_MENUBAR diff --git a/src/widgets/qmenubar.h b/src/widgets/qmenubar.h new file mode 100644 index 0000000..74778d3 --- /dev/null +++ b/src/widgets/qmenubar.h @@ -0,0 +1,205 @@ +/**************************************************************************** +** +** Definition of QMenuBar class +** +** Created : 941209 +** +** 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. +** +**********************************************************************/ + +#ifndef QMENUBAR_H +#define QMENUBAR_H + +#ifndef QT_H +#include "qpopupmenu.h" // ### remove or keep for users' convenience? +#include "qframe.h" +#include "qmenudata.h" +#endif // QT_H + +#ifndef QT_NO_MENUBAR + +class QPopupMenu; + +class Q_EXPORT QMenuBar : public QFrame, public QMenuData +{ + Q_OBJECT + Q_ENUMS( Separator ) + Q_PROPERTY( Separator separator READ separator WRITE setSeparator DESIGNABLE false ) + Q_PROPERTY( bool defaultUp READ isDefaultUp WRITE setDefaultUp ) + +public: + QMenuBar( QWidget* parent=0, const char* name=0 ); + ~QMenuBar(); + + void updateItem( int id ); + + void show(); // reimplemented show + void hide(); // reimplemented hide + + bool eventFilter( QObject *, QEvent * ); + + int heightForWidth(int) const; + + enum Separator { Never=0, InWindowsStyle=1 }; + Separator separator() const; + virtual void setSeparator( Separator when ); + + void setDefaultUp( bool ); + bool isDefaultUp() const; + + bool customWhatsThis() const; + + QSize sizeHint() const; + QSize minimumSize() const; + QSize minimumSizeHint() const; + + void activateItemAt( int index ); + +#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE) + static void initialize(); + static void cleanup(); +#endif + +signals: + void activated( int itemId ); + void highlighted( int itemId ); + +protected: + void drawContents( QPainter * ); + void fontChange( const QFont & ); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void keyPressEvent( QKeyEvent * ); + void focusInEvent( QFocusEvent * ); + void focusOutEvent( QFocusEvent * ); + void resizeEvent( QResizeEvent * ); + void leaveEvent( QEvent * ); + void menuContentsChanged(); + void menuStateChanged(); + void styleChange( QStyle& ); + int itemAtPos( const QPoint & ); + void hidePopups(); + QRect itemRect( int item ); + +private slots: + void subActivated( int itemId ); + void subHighlighted( int itemId ); +#ifndef QT_NO_ACCEL + void accelActivated( int itemId ); + void accelDestroyed(); +#endif + void popupDestroyed( QObject* ); + void performDelayedChanges(); + + void languageChange(); + +private: + void performDelayedContentsChanged(); + void performDelayedStateChanged(); + void menuInsPopup( QPopupMenu * ); + void menuDelPopup( QPopupMenu * ); + void frameChanged(); + + bool tryMouseEvent( QPopupMenu *, QMouseEvent * ); + void tryKeyEvent( QPopupMenu *, QKeyEvent * ); + void goodbye( bool cancelled = FALSE ); + void openActPopup(); + + void setActiveItem( int index, bool show = TRUE, bool activate_first_item = TRUE ); + void setAltMode( bool ); + + int calculateRects( int max_width = -1 ); + +#ifndef QT_NO_ACCEL + void setupAccelerators(); + QAccel *autoaccel; +#endif + QRect *irects; + int rightSide; + + uint mseparator : 1; + uint waitforalt : 1; + uint popupvisible : 1; + uint hasmouse : 1; + uint defaultup : 1; + uint toggleclose : 1; + uint pendingDelayedContentsChanges : 1; + uint pendingDelayedStateChanges : 1; + + friend class QPopupMenu; + +#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE) + friend class QWidget; + friend class QApplication; + friend void qt_mac_set_modal_state(bool, QMenuBar *); + + void macCreateNativeMenubar(); + void macRemoveNativeMenubar(); + void macDirtyNativeMenubar(); + +#if !defined(QMAC_QMENUBAR_NO_EVENT) + static void qt_mac_install_menubar_event(MenuRef); + static OSStatus qt_mac_menubar_event(EventHandlerCallRef, EventRef, void *); +#endif + virtual void macWidgetChangedWindow(); + bool syncPopups(MenuRef ret, QPopupMenu *d); + MenuRef createMacPopup(QPopupMenu *d, int id, bool =FALSE); + bool updateMenuBar(); +#if !defined(QMAC_QMENUBAR_NO_MERGE) + uint isCommand(QMenuItem *, bool just_check=FALSE); +#endif + + uint mac_eaten_menubar : 1; + class MacPrivate; + MacPrivate *mac_d; + static bool activate(MenuRef, short, bool highlight=FALSE, bool by_accel=FALSE); + static bool activateCommand(uint cmd); + static bool macUpdateMenuBar(); + static bool macUpdatePopupVisible(MenuRef, bool); + static bool macUpdatePopup(MenuRef); +#endif + +private: // Disabled copy constructor and operator= + +#if defined(Q_DISABLE_COPY) + QMenuBar( const QMenuBar & ); + QMenuBar &operator=( const QMenuBar & ); +#endif +}; + + +#endif // QT_NO_MENUBAR + +#endif // QMENUBAR_H diff --git a/src/widgets/qmenudata.cpp b/src/widgets/qmenudata.cpp new file mode 100644 index 0000000..518ea6b --- /dev/null +++ b/src/widgets/qmenudata.cpp @@ -0,0 +1,1603 @@ +/**************************************************************************** +** +** Implementation of QMenuData 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 "qmenudata.h" +#ifndef QT_NO_MENUDATA +#include "qpopupmenu.h" +#include "qmenubar.h" +#include "qapplication.h" +#include "qguardedptr.h" + +class QMenuItemData { +public: + QCustomMenuItem *custom_item; // custom menu item +}; + +class QMenuDataData { + // attention: also defined in qmenubar.cpp and qpopupmenu.cpp +public: + QMenuDataData(); + QGuardedPtr<QWidget> aWidget; + int aInt; +}; +QMenuDataData::QMenuDataData() + : aInt(-1) +{} + +/*! + \class QMenuData qmenudata.h + \brief The QMenuData class is a base class for QMenuBar and QPopupMenu. + + \ingroup misc + + QMenuData has an internal list of menu items. A menu item can have + a text(), an \link accel() accelerator\endlink, a pixmap(), an + iconSet(), a whatsThis() text and a popup menu (unless it is a + separator). Menu items may optionally be \link setItemChecked() + checked\endlink (except for separators). + + The menu item sends out an \link QMenuBar::activated() + activated()\endlink signal when it is chosen and a \link + QMenuBar::highlighted() highlighted()\endlink signal when it + receives the user input focus. + + \keyword menu identifier + + Menu items are assigned the menu identifier \e id that is passed + in insertItem() or an automatically generated identifier if \e id + is < 0 (the default). The generated identifiers (negative + integers) are guaranteed to be unique within the entire + application. The identifier is used to access the menu item in + other functions. + + Menu items can be removed with removeItem() and removeItemAt(), or + changed with changeItem(). All menu items can be removed with + clear(). Accelerators can be changed or set with setAccel(). + Checkable items can be checked or unchecked with setItemChecked(). + Items can be enabled or disabled using setItemEnabled() and + connected and disconnected with connectItem() and disconnectItem() + respectively. By default, newly created menu items are visible. + They can be hidden (and shown again) with setItemVisible(). + + Menu items are stored in a list. Use findItem() to find an item by + its list position or by its menu identifier. (See also indexOf() + and idAt().) + + \sa QAccel QPopupMenu QAction +*/ + + +/***************************************************************************** + QMenuItem member functions + *****************************************************************************/ + +QMenuItem::QMenuItem() + :ident( -1 ), iconset_data( 0 ), pixmap_data( 0 ), popup_menu( 0 ), + widget_item( 0 ), signal_data( 0 ), is_separator( FALSE ), is_enabled( TRUE ), + is_checked( FALSE ), is_dirty( TRUE ), is_visible( TRUE ), d( 0) +{} + +QMenuItem::~QMenuItem() +{ + delete iconset_data; + delete pixmap_data; + delete signal_data; + delete widget_item; + if ( d ) + delete d->custom_item; + delete d; +} + + +/***************************************************************************** + QMenuData member functions + *****************************************************************************/ + +QMenuItemData* QMenuItem::extra() +{ + if ( !d ) d = new QMenuItemData; + return d; +} + +QCustomMenuItem *QMenuItem::custom() const +{ + if ( !d ) return 0; + return d->custom_item; +} + + +static int get_seq_id() +{ + static int seq_no = -2; + return seq_no--; +} + + +/*! + Constructs an empty menu data list. +*/ + +QMenuData::QMenuData() +{ + actItem = -1; // no active menu item + mitems = new QMenuItemList; // create list of menu items + Q_CHECK_PTR( mitems ); + mitems->setAutoDelete( TRUE ); + parentMenu = 0; // assume top level + isPopupMenu = FALSE; + isMenuBar = FALSE; + mouseBtDn = FALSE; + badSize = TRUE; + avoid_circularity = 0; + actItemDown = FALSE; + d = new QMenuDataData; +} + +/*! + Removes all menu items and disconnects any signals that have been + connected. +*/ + +QMenuData::~QMenuData() +{ + delete mitems; // delete menu item list + delete d; +} + + +/*! + Virtual function; notifies subclasses about an item with \a id + that has been changed. +*/ + +void QMenuData::updateItem( int /* id */ ) // reimplemented in subclass +{ +} + +/*! + Virtual function; notifies subclasses that one or more items have + been inserted or removed. +*/ + +void QMenuData::menuContentsChanged() // reimplemented in subclass +{ +} + +/*! + Virtual function; notifies subclasses that one or more items have + changed state (enabled/disabled or checked/unchecked). +*/ + +void QMenuData::menuStateChanged() // reimplemented in subclass +{ +} + +/*! + Virtual function; notifies subclasses that a popup menu item has + been inserted. +*/ + +void QMenuData::menuInsPopup( QPopupMenu * ) // reimplemented in subclass +{ +} + +/*! + Virtual function; notifies subclasses that a popup menu item has + been removed. +*/ + +void QMenuData::menuDelPopup( QPopupMenu * ) // reimplemented in subclass +{ +} + + +/*! + Returns the number of items in the menu. +*/ + +uint QMenuData::count() const +{ + return mitems->count(); +} + + + +/*! + \internal + + Internal function that insert a menu item. Called by all insert() + functions. +*/ + +int QMenuData::insertAny( const QString *text, const QPixmap *pixmap, + QPopupMenu *popup, const QIconSet* iconset, int id, int index, + QWidget* widget, QCustomMenuItem* custom ) +{ + if ( index < 0 ) { // append, but not if the rightmost item is an mdi separator in the menubar + index = mitems->count(); + if ( isMenuBar && mitems->last() && mitems->last()->widget() && mitems->last()->isSeparator() ) + index--; + } else if ( index > (int) mitems->count() ) { // append + index = mitems->count(); + } + if ( id < 0 ) // -2, -3 etc. + id = get_seq_id(); + + register QMenuItem *mi = new QMenuItem; + Q_CHECK_PTR( mi ); + mi->ident = id; + if ( widget != 0 ) { + mi->widget_item = widget; + mi->is_separator = !widget->isFocusEnabled(); + } else if ( custom != 0 ) { + mi->extra()->custom_item = custom; + mi->is_separator = custom->isSeparator(); + if ( iconset && !iconset->isNull() ) + mi->iconset_data = new QIconSet( *iconset ); + } else if ( text == 0 && pixmap == 0 && popup == 0 ) { + mi->is_separator = TRUE; // separator + } else { +#ifndef Q_OS_TEMP + mi->text_data = text?*text:QString(); +#else + QString newText( *text ); + newText.truncate( newText.findRev( '\t' ) ); + mi->text_data = newText.isEmpty()?QString():newText; +#endif +#ifndef QT_NO_ACCEL + mi->accel_key = Qt::Key_unknown; +#endif + if ( pixmap && !pixmap->isNull() ) + mi->pixmap_data = new QPixmap( *pixmap ); + if ( (mi->popup_menu = popup) ) + menuInsPopup( popup ); + if ( iconset && !iconset->isNull() ) + mi->iconset_data = new QIconSet( *iconset ); + } + + mitems->insert( index, mi ); + QPopupMenu* p = ::qt_cast<QPopupMenu*>(QMenuData::d->aWidget); + if (p && p->isVisible() && p->mitems) { + p->mitems->clear(); + for ( QMenuItemListIt it( *mitems ); it.current(); ++it ) { + if ( it.current()->id() != QMenuData::d->aInt && !it.current()->widget() ) + p->mitems->append( it.current() ); + } + } + menuContentsChanged(); // menu data changed + return mi->ident; +} + +/*! + \internal + + Internal function that finds the menu item where \a popup is located, + storing its index at \a index if \a index is not NULL. +*/ +QMenuItem *QMenuData::findPopup( QPopupMenu *popup, int *index ) +{ + int i = 0; + QMenuItem *mi = mitems->first(); + while ( mi ) { + if ( mi->popup_menu == popup ) // found popup + break; + i++; + mi = mitems->next(); + } + if ( index && mi ) + *index = i; + return mi; +} + +void QMenuData::removePopup( QPopupMenu *popup ) +{ + int index = 0; + QMenuItem *mi = findPopup( popup, &index ); + if ( mi ) { + mi->popup_menu = 0; + removeItemAt( index ); + } +} + + +/*! + The family of insertItem() functions inserts menu items into a + popup menu or a menu bar. + + A menu item is usually either a text string or a pixmap, both with + an optional icon or keyboard accelerator. For special cases it is + also possible to insert custom items (see \l{QCustomMenuItem}) or + even widgets into popup menus. + + Some insertItem() members take a popup menu as an additional + argument. Use this to insert submenus into existing menus or + pulldown menus into a menu bar. + + The number of insert functions may look confusing, but they are + actually quite simple to use. + + This default version inserts a menu item with the text \a text, + the accelerator key \a accel, an id and an optional index and + connects it to the slot \a member in the object \a receiver. + + Example: + \code + QMenuBar *mainMenu = new QMenuBar; + QPopupMenu *fileMenu = new QPopupMenu; + fileMenu->insertItem( "New", myView, SLOT(newFile()), CTRL+Key_N ); + fileMenu->insertItem( "Open", myView, SLOT(open()), CTRL+Key_O ); + mainMenu->insertItem( "File", fileMenu ); + \endcode + + Not all insert functions take an object/slot parameter or an + accelerator key. Use connectItem() and setAccel() on those items. + + If you need to translate accelerators, use tr() with the text and + accelerator. (For translations use a string \link QKeySequence key + sequence\endlink.): + \code + fileMenu->insertItem( tr("Open"), myView, SLOT(open()), + tr("Ctrl+O") ); + \endcode + + In the example above, pressing Ctrl+O or selecting "Open" from the + menu activates the myView->open() function. + + Some insert functions take a QIconSet parameter to specify the + little menu item icon. Note that you can always pass a QPixmap + object instead. + + The \a id specifies the identification number associated with the + menu item. Note that only positive values are valid, as a negative + value will make Qt select a unique id for the item. + + The \a index specifies the position in the menu. The menu item is + appended at the end of the list if \a index is negative. + + Note that keyboard accelerators in Qt are not application-global, + instead they are bound to a certain top-level window. For example, + accelerators in QPopupMenu items only work for menus that are + associated with a certain window. This is true for popup menus + that live in a menu bar since their accelerators will then be + installed in the menu bar itself. This also applies to stand-alone + popup menus that have a top-level widget in their parentWidget() + chain. The menu will then install its accelerator object on that + top-level widget. For all other cases use an independent QAccel + object. + + \warning Be careful when passing a literal 0 to insertItem() + because some C++ compilers choose the wrong overloaded function. + Cast the 0 to what you mean, e.g. \c{(QObject*)0}. + + \warning On Mac OS X, items that connect to a slot that are inserted into a + menubar will not function as we use the native menubar that knows nothing + about signals or slots. Instead insert the items into a popup menu and + insert the popup menu into the menubar. This may be fixed in a future Qt + version. + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem(), QAccel, + qnamespace.h +*/ + +int QMenuData::insertItem( const QString &text, + const QObject *receiver, const char* member, + const QKeySequence& accel, int id, int index ) +{ + int actualID = insertAny( &text, 0, 0, 0, id, index ); + connectItem( actualID, receiver, member ); +#ifndef QT_NO_ACCEL + if ( accel ) + setAccel( accel, actualID ); +#endif + return actualID; +} + +/*! + \overload + + Inserts a menu item with icon \a icon, text \a text, accelerator + \a accel, optional id \a id, and optional \a index position. The + menu item is connected it to the \a receiver's \a member slot. The + icon will be displayed to the left of the text in the item. + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem(), QAccel, + qnamespace.h +*/ + +int QMenuData::insertItem( const QIconSet& icon, + const QString &text, + const QObject *receiver, const char* member, + const QKeySequence& accel, int id, int index ) +{ + int actualID = insertAny( &text, 0, 0, &icon, id, index ); + connectItem( actualID, receiver, member ); +#ifndef QT_NO_ACCEL + if ( accel ) + setAccel( accel, actualID ); +#endif + return actualID; +} + +/*! + \overload + + Inserts a menu item with pixmap \a pixmap, accelerator \a accel, + optional id \a id, and optional \a index position. The menu item + is connected it to the \a receiver's \a member slot. The icon will + be displayed to the left of the text in the item. + + To look best when being highlighted as a menu item, the pixmap + should provide a mask (see QPixmap::mask()). + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem() +*/ + +int QMenuData::insertItem( const QPixmap &pixmap, + const QObject *receiver, const char* member, + const QKeySequence& accel, int id, int index ) +{ + int actualID = insertAny( 0, &pixmap, 0, 0, id, index ); + connectItem( actualID, receiver, member ); +#ifndef QT_NO_ACCEL + if ( accel ) + setAccel( accel, actualID ); +#endif + return actualID; +} + + +/*! + \overload + + Inserts a menu item with icon \a icon, pixmap \a pixmap, + accelerator \a accel, optional id \a id, and optional \a index + position. The icon will be displayed to the left of the pixmap in + the item. The item is connected to the \a member slot in the \a + receiver object. + + To look best when being highlighted as a menu item, the pixmap + should provide a mask (see QPixmap::mask()). + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem(), QAccel, + qnamespace.h +*/ + +int QMenuData::insertItem( const QIconSet& icon, + const QPixmap &pixmap, + const QObject *receiver, const char* member, + const QKeySequence& accel, int id, int index ) +{ + int actualID = insertAny( 0, &pixmap, 0, &icon, id, index ); + connectItem( actualID, receiver, member ); +#ifndef QT_NO_ACCEL + if ( accel ) + setAccel( accel, actualID ); +#endif + return actualID; +} + + + +/*! + \overload + + Inserts a menu item with text \a text, optional id \a id, and + optional \a index position. + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem() +*/ + +int QMenuData::insertItem( const QString &text, int id, int index ) +{ + return insertAny( &text, 0, 0, 0, id, index ); +} + +/*! + \overload + + Inserts a menu item with icon \a icon, text \a text, optional id + \a id, and optional \a index position. The icon will be displayed + to the left of the text in the item. + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem() +*/ + +int QMenuData::insertItem( const QIconSet& icon, + const QString &text, int id, int index ) +{ + return insertAny( &text, 0, 0, &icon, id, index ); +} + +/*! + \overload + + Inserts a menu item with text \a text, submenu \a popup, optional + id \a id, and optional \a index position. + + The \a popup must be deleted by the programmer or by its parent + widget. It is not deleted when this menu item is removed or when + the menu is deleted. + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem() +*/ + +int QMenuData::insertItem( const QString &text, QPopupMenu *popup, + int id, int index ) +{ + return insertAny( &text, 0, popup, 0, id, index ); +} + +/*! + \overload + + Inserts a menu item with icon \a icon, text \a text, submenu \a + popup, optional id \a id, and optional \a index position. The icon + will be displayed to the left of the text in the item. + + The \a popup must be deleted by the programmer or by its parent + widget. It is not deleted when this menu item is removed or when + the menu is deleted. + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem() +*/ + +int QMenuData::insertItem( const QIconSet& icon, + const QString &text, QPopupMenu *popup, + int id, int index ) +{ + return insertAny( &text, 0, popup, &icon, id, index ); +} + +/*! + \overload + + Inserts a menu item with pixmap \a pixmap, optional id \a id, and + optional \a index position. + + To look best when being highlighted as a menu item, the pixmap + should provide a mask (see QPixmap::mask()). + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem() +*/ + +int QMenuData::insertItem( const QPixmap &pixmap, int id, int index ) +{ + return insertAny( 0, &pixmap, 0, 0, id, index ); +} + +/*! + \overload + + Inserts a menu item with icon \a icon, pixmap \a pixmap, optional + id \a id, and optional \a index position. The icon will be + displayed to the left of the pixmap in the item. + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem() +*/ + +int QMenuData::insertItem( const QIconSet& icon, + const QPixmap &pixmap, int id, int index ) +{ + return insertAny( 0, &pixmap, 0, &icon, id, index ); +} + + +/*! + \overload + + Inserts a menu item with pixmap \a pixmap, submenu \a popup, + optional id \a id, and optional \a index position. + + The \a popup must be deleted by the programmer or by its parent + widget. It is not deleted when this menu item is removed or when + the menu is deleted. + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem() +*/ + +int QMenuData::insertItem( const QPixmap &pixmap, QPopupMenu *popup, + int id, int index ) +{ + return insertAny( 0, &pixmap, popup, 0, id, index ); +} + + +/*! + \overload + + Inserts a menu item with icon \a icon, pixmap \a pixmap submenu \a + popup, optional id \a id, and optional \a index position. The icon + will be displayed to the left of the pixmap in the item. + + The \a popup must be deleted by the programmer or by its parent + widget. It is not deleted when this menu item is removed or when + the menu is deleted. + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem(), changeItem(), setAccel(), connectItem() +*/ + +int QMenuData::insertItem( const QIconSet& icon, + const QPixmap &pixmap, QPopupMenu *popup, + int id, int index ) +{ + return insertAny( 0, &pixmap, popup, &icon, id, index ); +} + + + +/*! + \overload + + Inserts a menu item that consists of the widget \a widget with + optional id \a id, and optional \a index position. + + Ownership of \a widget is transferred to the popup menu or to the + menu bar. + + Theoretically, any widget can be inserted into a popup menu. In + practice, this only makes sense with certain widgets. + + If a widget is not focus-enabled (see + \l{QWidget::isFocusEnabled()}), the menu treats it as a separator; + this means that the item is not selectable and will never get + focus. In this way you can, for example, simply insert a QLabel if + you need a popup menu with a title. + + If the widget is focus-enabled it will get focus when the user + traverses the popup menu with the arrow keys. If the widget does + not accept \c ArrowUp and \c ArrowDown in its key event handler, + the focus will move back to the menu when the respective arrow key + is hit one more time. This works with a QLineEdit, for example. If + the widget accepts the arrow key itself, it must also provide the + possibility to put the focus back on the menu again by calling + QWidget::focusNextPrevChild(). Futhermore, if the embedded widget + closes the menu when the user made a selection, this can be done + safely by calling: + \code + if ( isVisible() && + parentWidget() && + parentWidget()->inherits("QPopupMenu") ) + parentWidget()->close(); + \endcode + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa removeItem() +*/ +int QMenuData::insertItem( QWidget* widget, int id, int index ) +{ + return insertAny( 0, 0, 0, 0, id, index, widget ); +} + + +/*! + \overload + + Inserts a custom menu item \a custom with optional id \a id, and + optional \a index position. + + This only works with popup menus. It is not supported for menu + bars. Ownership of \a custom is transferred to the popup menu. + + If you want to connect a custom item to a slot, use connectItem(). + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa connectItem(), removeItem(), QCustomMenuItem +*/ +int QMenuData::insertItem( QCustomMenuItem* custom, int id, int index ) +{ + return insertAny( 0, 0, 0, 0, id, index, 0, custom ); +} + +/*! + \overload + + Inserts a custom menu item \a custom with an \a icon and with + optional id \a id, and optional \a index position. + + This only works with popup menus. It is not supported for menu + bars. Ownership of \a custom is transferred to the popup menu. + + If you want to connect a custom item to a slot, use connectItem(). + + Returns the allocated menu identifier number (\a id if \a id >= 0). + + \sa connectItem(), removeItem(), QCustomMenuItem +*/ +int QMenuData::insertItem( const QIconSet& icon, QCustomMenuItem* custom, int id, int index ) +{ + return insertAny( 0, 0, 0, &icon, id, index, 0, custom ); +} + + +/*! + Inserts a separator at position \a index, and returns the menu identifier + number allocated to it. The separator becomes the last menu item if + \a index is negative. + + In a popup menu a separator is rendered as a horizontal line. In a + Motif menu bar a separator is spacing, so the rest of the items + (normally just "Help") are drawn right-justified. In a Windows + menu bar separators are ignored (to comply with the Windows style + guidelines). +*/ +int QMenuData::insertSeparator( int index ) +{ + return insertAny( 0, 0, 0, 0, -1, index ); +} + +/*! + \fn void QMenuData::removeItem( int id ) + + Removes the menu item that has the identifier \a id. + + \sa removeItemAt(), clear() +*/ + +void QMenuData::removeItem( int id ) +{ + QMenuData *parent; + if ( findItem( id, &parent ) ) + parent->removeItemAt(parent->indexOf(id)); +} + +/*! + Removes the menu item at position \a index. + + \sa removeItem(), clear() +*/ + +void QMenuData::removeItemAt( int index ) +{ + if ( index < 0 || index >= (int)mitems->count() ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QMenuData::removeItem: Index %d out of range", index ); +#endif + return; + } + QMenuItem *mi = mitems->at( index ); + if ( mi->popup_menu ) + menuDelPopup( mi->popup_menu ); + mitems->remove(); + QPopupMenu* p = ::qt_cast<QPopupMenu*>(QMenuData::d->aWidget); + if (p && p->isVisible() && p->mitems) { + p->mitems->clear(); + for ( QMenuItemListIt it( *mitems ); it.current(); ++it ) { + if ( it.current()->id() != QMenuData::d->aInt && !it.current()->widget() ) + p->mitems->append( it.current() ); + } + } + if ( !QApplication::closingDown() ) // avoid trouble + menuContentsChanged(); +} + + +/*! + Removes all menu items. + + \sa removeItem(), removeItemAt() +*/ + +void QMenuData::clear() +{ + register QMenuItem *mi = mitems->first(); + while ( mi ) { + if ( mi->popup_menu ) + menuDelPopup( mi->popup_menu ); + mitems->remove(); + mi = mitems->current(); + } + QPopupMenu* p = ::qt_cast<QPopupMenu*>(QMenuData::d->aWidget); + if (p && p->isVisible() && p->mitems) { + p->mitems->clear(); + } + if ( !QApplication::closingDown() ) // avoid trouble + menuContentsChanged(); +} + +#ifndef QT_NO_ACCEL + +/*! + Returns the accelerator key that has been defined for the menu + item \a id, or 0 if it has no accelerator key or if there is no + such menu item. + + \sa setAccel(), QAccel, qnamespace.h +*/ + +QKeySequence QMenuData::accel( int id ) const +{ + QMenuItem *mi = findItem( id ); + if ( mi ) + return mi->key(); + return QKeySequence(); +} + +/*! + Sets the accelerator key for the menu item \a id to \a key. + + An accelerator key consists of a key code and a combination of the + modifiers \c SHIFT, \c CTRL, \c ALT or \c UNICODE_ACCEL (OR'ed or + added). The header file \c qnamespace.h contains a list of key + codes. + + Defining an accelerator key produces a text that is added to the + menu item; for instance, \c CTRL + \c Key_O produces "Ctrl+O". The + text is formatted differently for different platforms. + + Note that keyboard accelerators in Qt are not application-global, + instead they are bound to a certain top-level window. For example, + accelerators in QPopupMenu items only work for menus that are + associated with a certain window. This is true for popup menus + that live in a menu bar since their accelerators will then be + installed in the menu bar itself. This also applies to stand-alone + popup menus that have a top-level widget in their parentWidget() + chain. The menu will then install its accelerator object on that + top-level widget. For all other cases use an independent QAccel + object. + + Example: + \code + QMenuBar *mainMenu = new QMenuBar; + QPopupMenu *fileMenu = new QPopupMenu; // file sub menu + fileMenu->insertItem( "Open Document", 67 ); // add "Open" item + fileMenu->setAccel( CTRL + Key_O, 67 ); // Ctrl+O to open + fileMenu->insertItem( "Quit", 69 ); // add "Quit" item + fileMenu->setAccel( CTRL + ALT + Key_Delete, 69 ); // add Alt+Del to quit + mainMenu->insertItem( "File", fileMenu ); // add the file menu + \endcode + + If you need to translate accelerators, use tr() with a string: + \code + fileMenu->setAccel( tr("Ctrl+O"), 67 ); + \endcode + + You can also specify the accelerator in the insertItem() function. + You may prefer to use QAction to associate accelerators with menu + items. + + \sa accel() insertItem() QAccel QAction +*/ + +void QMenuData::setAccel( const QKeySequence& key, int id ) +{ + QMenuData *parent; + QMenuItem *mi = findItem( id, &parent ); + if ( mi ) { + mi->accel_key = key; + parent->menuContentsChanged(); + } +} + +#endif // QT_NO_ACCEL + +/*! + Returns the icon set that has been set for menu item \a id, or 0 + if no icon set has been set. + + \sa changeItem(), text(), pixmap() +*/ + +QIconSet* QMenuData::iconSet( int id ) const +{ + QMenuItem *mi = findItem( id ); + return mi ? mi->iconSet() : 0; +} + +/*! + Returns the text that has been set for menu item \a id, or + QString::null if no text has been set. + + \sa changeItem(), pixmap(), iconSet() +*/ + +QString QMenuData::text( int id ) const +{ + QMenuItem *mi = findItem( id ); + return mi ? mi->text() : QString::null; +} + +/*! + Returns the pixmap that has been set for menu item \a id, or 0 if + no pixmap has been set. + + \sa changeItem(), text(), iconSet() +*/ + +QPixmap *QMenuData::pixmap( int id ) const +{ + QMenuItem *mi = findItem( id ); + return mi ? mi->pixmap() : 0; +} + +/*! + \fn void QMenuData::changeItem( const QString &, int ) + \obsolete + + Changes the text of the menu item \a id. If the item has an icon, + the icon remains unchanged. + + \sa text() +*/ +/*! + \fn void QMenuData::changeItem( const QPixmap &, int ) + \obsolete + + Changes the pixmap of the menu item \a id. If the item has an icon, + the icon remains unchanged. + + \sa pixmap() +*/ + +/*! + \fn void QMenuData::changeItem( const QIconSet &, const QString &, int ) + \obsolete + + Changes the icon and text of the menu item \a id. + + \sa pixmap() +*/ + +/*! + Changes the text of the menu item \a id to \a text. If the item + has an icon, the icon remains unchanged. + + \sa text() +*/ + +void QMenuData::changeItem( int id, const QString &text ) +{ + QMenuData *parent; + QMenuItem *mi = findItem( id, &parent ); + if ( mi ) { // item found + if ( mi->text_data == text ) // same string + return; + if ( mi->pixmap_data ) { // delete pixmap + delete mi->pixmap_data; + mi->pixmap_data = 0; + } + mi->text_data = text; +#ifndef QT_NO_ACCEL + if ( !mi->accel_key && text.find( '\t' ) != -1 ) + mi->accel_key = Qt::Key_unknown; +#endif + parent->menuContentsChanged(); + } +} + +/*! + \overload + + Changes the pixmap of the menu item \a id to the pixmap \a pixmap. + If the item has an icon, the icon is unchanged. + + \sa pixmap() +*/ + +void QMenuData::changeItem( int id, const QPixmap &pixmap ) +{ + QMenuData *parent; + QMenuItem *mi = findItem( id, &parent ); + if ( mi ) { // item found + register QPixmap *i = mi->pixmap_data; + bool fast_refresh = i != 0 && + i->width() == pixmap.width() && + i->height() == pixmap.height() && + !mi->text(); + if ( !mi->text_data.isNull() ) // delete text + mi->text_data = QString::null; + if ( !pixmap.isNull() ) + mi->pixmap_data = new QPixmap( pixmap ); + else + mi->pixmap_data = 0; + delete i; // old mi->pixmap_data, could be &pixmap + if ( fast_refresh ) + parent->updateItem( id ); + else + parent->menuContentsChanged(); + } +} + +/*! + \overload + + Changes the iconset and text of the menu item \a id to the \a icon + and \a text respectively. + + \sa pixmap() +*/ + +void QMenuData::changeItem( int id, const QIconSet &icon, const QString &text ) +{ + changeItem( id, text ); + changeItemIconSet( id, icon ); +} + +/*! + \overload + + Changes the iconset and pixmap of the menu item \a id to \a icon + and \a pixmap respectively. + + \sa pixmap() +*/ + +void QMenuData::changeItem( int id, const QIconSet &icon, const QPixmap &pixmap ) +{ + changeItem( id, pixmap ); + changeItemIconSet( id, icon ); +} + + + +/*! + Changes the icon of the menu item \a id to \a icon. + + \sa pixmap() +*/ + +void QMenuData::changeItemIconSet( int id, const QIconSet &icon ) +{ + QMenuData *parent; + QMenuItem *mi = findItem( id, &parent ); + if ( mi ) { // item found + register QIconSet *i = mi->iconset_data; + bool fast_refresh = i != 0; + if ( !icon.isNull() ) + mi->iconset_data = new QIconSet( icon ); + else + mi->iconset_data = 0; + delete i; // old mi->iconset_data, could be &icon + if ( fast_refresh ) + parent->updateItem( id ); + else + parent->menuContentsChanged(); + } +} + + +/*! + Returns TRUE if the item with identifier \a id is enabled; + otherwise returns FALSE + + \sa setItemEnabled(), isItemVisible() +*/ + +bool QMenuData::isItemEnabled( int id ) const +{ + QMenuItem *mi = findItem( id ); + return mi ? mi->isEnabled() : FALSE; +} + +/*! + If \a enable is TRUE, enables the menu item with identifier \a id; + otherwise disables the menu item with identifier \a id. + + \sa isItemEnabled() +*/ + +void QMenuData::setItemEnabled( int id, bool enable ) +{ + QMenuData *parent; + QMenuItem *mi = findItem( id, &parent ); + if ( mi && (bool)mi->is_enabled != enable ) { + mi->is_enabled = enable; +#if !defined(QT_NO_ACCEL) && !defined(QT_NO_POPUPMENU) + if ( mi->popup() ) + mi->popup()->enableAccel( enable ); +#endif + parent->menuStateChanged(); + } +} + + +/*! + Returns TRUE if the menu item with the id \a id is currently + active; otherwise returns FALSE. +*/ +bool QMenuData::isItemActive( int id ) const +{ + if ( actItem == -1 ) + return FALSE; + return indexOf( id ) == actItem; +} + +/*! + Returns TRUE if the menu item with the id \a id has been checked; + otherwise returns FALSE. + + \sa setItemChecked() +*/ + +bool QMenuData::isItemChecked( int id ) const +{ + QMenuItem *mi = findItem( id ); + return mi ? mi->isChecked() : FALSE; +} + +/*! + If \a check is TRUE, checks the menu item with id \a id; otherwise + unchecks the menu item with id \a id. Calls + QPopupMenu::setCheckable( TRUE ) if necessary. + + \sa isItemChecked() +*/ + +void QMenuData::setItemChecked( int id, bool check ) +{ + QMenuData *parent; + QMenuItem *mi = findItem( id, &parent ); + if ( mi && (bool)mi->is_checked != check ) { + mi->is_checked = check; +#ifndef QT_NO_POPUPMENU + if ( parent->isPopupMenu && !((QPopupMenu *)parent)->isCheckable() ) + ((QPopupMenu *)parent)->setCheckable( TRUE ); +#endif + parent->menuStateChanged(); + } +} + +/*! + Returns TRUE if the menu item with the id \a id is visible; + otherwise returns FALSE. + + \sa setItemVisible() +*/ + +bool QMenuData::isItemVisible( int id ) const +{ + QMenuItem *mi = findItem( id ); + return mi ? mi->isVisible() : FALSE; +} + +/*! + If \a visible is TRUE, shows the menu item with id \a id; otherwise + hides the menu item with id \a id. + + \sa isItemVisible(), isItemEnabled() +*/ + +void QMenuData::setItemVisible( int id, bool visible ) +{ + QMenuData *parent; + QMenuItem *mi = findItem( id, &parent ); + if ( mi && (bool)mi->is_visible != visible ) { + mi->is_visible = visible; + parent->menuContentsChanged(); + } +} + + +/*! + Returns the menu item with identifier \a id, or 0 if there is no + item with this identifier. + + Note that QMenuItem is an internal class, and that you should not + need to call this function. Use the higher level functions like + text(), pixmap() and changeItem() to get and modify menu item + attributes instead. + + \sa indexOf() +*/ + +QMenuItem *QMenuData::findItem( int id ) const +{ + return findItem( id, 0 ); +} + + +/*! + \overload + + Returns the menu item with identifier \a id, or 0 if there is no + item with this identifier. Changes \a *parent to point to the + parent of the return value. + + Note that QMenuItem is an internal class, and that you should not + need to call this function. Use the higher level functions like + text(), pixmap() and changeItem() to get and modify menu item + attributes instead. + + \sa indexOf() +*/ + +QMenuItem * QMenuData::findItem( int id, QMenuData ** parent ) const +{ + if ( parent ) + *parent = (QMenuData *)this; // ### + + if ( id == -1 ) // bad identifier + return 0; + QMenuItemListIt it( *mitems ); + QMenuItem *mi; + while ( (mi=it.current()) ) { // search this menu + ++it; + if ( mi->ident == id ) // found item + return mi; + } + it.toFirst(); + while ( (mi=it.current()) ) { // search submenus + ++it; +#ifndef QT_NO_POPUPMENU + if ( mi->popup_menu ) { + QPopupMenu *p = mi->popup_menu; + if (!p->avoid_circularity) { + p->avoid_circularity = 1; + mi = mi->popup_menu->findItem( id, parent ); + p->avoid_circularity = 0; + if ( mi ) // found item + return mi; + } + } +#endif + } + return 0; // not found +} + +/*! + Returns the index of the menu item with identifier \a id, or -1 if + there is no item with this identifier. + + \sa idAt(), findItem() +*/ + +int QMenuData::indexOf( int id ) const +{ + if ( id == -1 ) // bad identifier + return -1; + QMenuItemListIt it( *mitems ); + QMenuItem *mi; + int index = 0; + while ( (mi=it.current()) ) { + if ( mi->ident == id ) // this one? + return index; + ++index; + ++it; + } + return -1; // not found +} + +/*! + 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 setId(), indexOf() +*/ + +int QMenuData::idAt( int index ) const +{ + return index < (int)mitems->count() && index >= 0 ? + mitems->at(index)->id() : -1; +} + +/*! + Sets the menu identifier of the item at \a index to \a id. + + If \a index is out of range, the operation is ignored. + + \sa idAt() +*/ + +void QMenuData::setId( int index, int id ) +{ + if ( index < (int)mitems->count() ) + mitems->at(index)->ident = id; +} + + +/*! + Sets the parameter of the activation signal of item \a id to \a + param. + + If any receiver takes an integer parameter, this value is passed. + + \sa connectItem(), disconnectItem(), itemParameter() +*/ +bool QMenuData::setItemParameter( int id, int param ) { + QMenuItem *mi = findItem( id ); + if ( !mi ) // no such identifier + return FALSE; + if ( !mi->signal_data ) { // create new signal + mi->signal_data = new QSignal; + Q_CHECK_PTR( mi->signal_data ); + } + mi->signal_data->setValue( param ); + return TRUE; +} + + +/*! + Returns the parameter of the activation signal of item \a id. + + If no parameter has been specified for this item with + setItemParameter(), the value defaults to \a id. + + \sa connectItem(), disconnectItem(), setItemParameter() +*/ +int QMenuData::itemParameter( int id ) const +{ + QMenuItem *mi = findItem( id ); + if ( !mi || !mi->signal_data ) + return id; + return mi->signal_data->value().toInt(); +} + + +/*! + Connects the menu item with identifier \a id to \a{receiver}'s \a + member slot or signal. + + The receiver's slot (or signal) is activated when the menu item is + activated. + + \sa disconnectItem(), setItemParameter() +*/ + +bool QMenuData::connectItem( int id, const QObject *receiver, + const char* member ) +{ + QMenuItem *mi = findItem( id ); + if ( !mi ) // no such identifier + return FALSE; + if ( !mi->signal_data ) { // create new signal + mi->signal_data = new QSignal; + Q_CHECK_PTR( mi->signal_data ); + mi->signal_data->setValue( id ); + } + return mi->signal_data->connect( receiver, member ); +} + + +/*! + Disconnects the \a{receiver}'s \a member from the menu item with + identifier \a id. + + All connections are removed when the menu data object is + destroyed. + + \sa connectItem(), setItemParameter() +*/ + +bool QMenuData::disconnectItem( int id, const QObject *receiver, + const char* member ) +{ + QMenuItem *mi = findItem( id ); + if ( !mi || !mi->signal_data ) // no identifier or no signal + return FALSE; + return mi->signal_data->disconnect( receiver, member ); +} + +/*! + Sets \a text as What's This help for the menu item with identifier + \a id. + + \sa whatsThis() +*/ +void QMenuData::setWhatsThis( int id, const QString& text ) +{ + + QMenuData *parent; + QMenuItem *mi = findItem( id, &parent ); + if ( mi ) { + mi->setWhatsThis( text ); + parent->menuContentsChanged(); + } +} + +/*! + Returns the What's This help text for the item with identifier \a + id or QString::null if no text has yet been defined. + + \sa setWhatsThis() +*/ +QString QMenuData::whatsThis( int id ) const +{ + + QMenuItem *mi = findItem( id ); + return mi? mi->whatsThis() : QString::null; +} + + + +/*! + \class QCustomMenuItem qmenudata.h + \brief The QCustomMenuItem class is an abstract base class for custom menu items in popup menus. + + \ingroup misc + + A custom menu item is a menu item that is defined by two pure + virtual functions, paint() and sizeHint(). The size hint tells the + menu how much space it needs to reserve for this item, and paint + is called whenever the item needs painting. + + This simple mechanism allows you to create all kinds of + application specific menu items. Examples are items showing + different fonts in a word processor or menus that allow the + selection of drawing utilities in a vector drawing program. + + A custom item is inserted into a popup menu with + QPopupMenu::insertItem(). + + By default, a custom item can also have an icon and a keyboard + accelerator. You can reimplement fullSpan() to return TRUE if you + want the item to span the entire popup menu width. This is + particularly useful for labels. + + If you want the custom item to be treated just as a separator, + reimplement isSeparator() to return TRUE. + + Note that you can insert pixmaps or bitmaps as items into a popup + menu without needing to create a QCustomMenuItem. However, custom + menu items offer more flexibility, and -- especially important + with Windows style -- provide the possibility of drawing the item + with a different color when it is highlighted. + + \link menu-example.html menu/menu.cpp\endlink shows a simple + example how custom menu items can be used. + + Note: the current implementation of QCustomMenuItem will not + recognize shortcut keys that are from text with ampersands. Normal + accelerators work though. + + <img src=qpopmenu-fancy.png> + + \sa QMenuData, QPopupMenu +*/ + + + +/*! + Constructs a QCustomMenuItem +*/ +QCustomMenuItem::QCustomMenuItem() +{ +} + +/*! + Destroys a QCustomMenuItem +*/ +QCustomMenuItem::~QCustomMenuItem() +{ +} + + +/*! + Sets the font of the custom menu item to \a font. + + This function is called whenever the font in the popup menu + changes. For menu items that show their own individual font entry, + you want to ignore this. +*/ +void QCustomMenuItem::setFont( const QFont& /* font */ ) +{ +} + + + +/*! + Returns TRUE if this item wants to span the entire popup menu + width; otherwise returns FALSE. The default is FALSE, meaning that + the menu may show an icon and an accelerator key for this item as + well. +*/ +bool QCustomMenuItem::fullSpan() const +{ + return FALSE; +} + +/*! + Returns TRUE if this item is just a separator; otherwise returns + FALSE. +*/ +bool QCustomMenuItem::isSeparator() const +{ + return FALSE; +} + + +/*! + \fn void QCustomMenuItem::paint( QPainter* p, const QColorGroup& cg, bool act, bool enabled, int x, int y, int w, int h ); + + Paints this item. When this function is invoked, the painter \a p + is set to a font and foreground color suitable for a menu item + text using color group \a cg. The item is active if \a act is TRUE + and enabled if \a enabled is TRUE. The geometry values \a x, \a y, + \a w and \a h specify where to draw the item. + + Do not draw any background, this has already been done by the + popup menu according to the current GUI style. +*/ + + +/*! + \fn QSize QCustomMenuItem::sizeHint(); + + Returns the item's size hint. +*/ + + + +/*! + Activates the menu item at position \a index. + + If the index is invalid (for example, -1), the object itself is + deactivated. +*/ +void QMenuData::activateItemAt( int index ) +{ +#ifndef QT_NO_MENUBAR + if ( isMenuBar ) + ( (QMenuBar*)this )->activateItemAt( index ); + else +#endif + { +#ifndef QT_NO_POPUPMENU + if ( isPopupMenu ) + ( (QPopupMenu*)this )->activateItemAt( index ); +#endif + } +} + +#endif diff --git a/src/widgets/qmenudata.h b/src/widgets/qmenudata.h new file mode 100644 index 0000000..13f26c3 --- /dev/null +++ b/src/widgets/qmenudata.h @@ -0,0 +1,286 @@ +/**************************************************************************** +** +** Definition of QMenuData 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. +** +**********************************************************************/ + +#ifndef QMENUDATA_H +#define QMENUDATA_H + +#ifndef QT_H +#include "qglobal.h" +#include "qiconset.h" // conversion QPixmap->QIconset +#include "qkeysequence.h" +#include "qstring.h" +#include "qsignal.h" +#include "qfont.h" +#endif // QT_H + +#ifndef QT_NO_MENUDATA + +class QPopupMenu; +class QMenuDataData; +class QObject; + +class QCustomMenuItem; +class QMenuItemData; + +class Q_EXPORT QMenuItem // internal menu item class +{ +friend class QMenuData; +public: + QMenuItem(); + ~QMenuItem(); + + int id() const { return ident; } + QIconSet *iconSet() const { return iconset_data; } + QString text() const { return text_data; } + QString whatsThis() const { return whatsthis_data; } + QPixmap *pixmap() const { return pixmap_data; } + QPopupMenu *popup() const { return popup_menu; } + QWidget *widget() const { return widget_item; } + QCustomMenuItem *custom() const; +#ifndef QT_NO_ACCEL + QKeySequence key() const { return accel_key; } +#endif + QSignal *signal() const { return signal_data; } + bool isSeparator() const { return is_separator; } + bool isEnabled() const { return is_enabled; } + bool isChecked() const { return is_checked; } + bool isDirty() const { return is_dirty; } + bool isVisible() const { return is_visible; } + bool isEnabledAndVisible() const { return is_enabled && is_visible; } + + void setText( const QString &text ) { text_data = text; } + void setDirty( bool dirty ) { is_dirty = dirty; } + void setVisible( bool visible ) { is_visible = visible; } + void setWhatsThis( const QString &text ) { whatsthis_data = text; } + +private: + int ident; // item identifier + QIconSet *iconset_data; // icons + QString text_data; // item text + QString whatsthis_data; // item Whats This help text + QPixmap *pixmap_data; // item pixmap + QPopupMenu *popup_menu; // item popup menu + QWidget *widget_item; // widget menu item +#ifndef QT_NO_ACCEL + QKeySequence accel_key; // accelerator key (state|ascii) +#endif + QSignal *signal_data; // connection + uint is_separator : 1; // separator flag + uint is_enabled : 1; // disabled flag + uint is_checked : 1; // checked flag + uint is_dirty : 1; // dirty (update) flag + uint is_visible : 1; // visibility flag + QMenuItemData* d; + + QMenuItemData* extra(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QMenuItem( const QMenuItem & ); + QMenuItem &operator=( const QMenuItem & ); +#endif +}; + +#include "qptrlist.h" +typedef QPtrList<QMenuItem> QMenuItemList; +typedef QPtrListIterator<QMenuItem> QMenuItemListIt; + + +class Q_EXPORT QCustomMenuItem : public Qt +{ +public: + QCustomMenuItem(); + virtual ~QCustomMenuItem(); + virtual bool fullSpan() const; + virtual bool isSeparator() const; + virtual void setFont( const QFont& font ); + virtual void paint( QPainter* p, const QColorGroup& cg, bool act, + bool enabled, int x, int y, int w, int h ) = 0; + virtual QSize sizeHint() = 0; +}; + + +class Q_EXPORT QMenuData // menu data class +{ +friend class QMenuBar; +friend class QPopupMenu; +public: + QMenuData(); + virtual ~QMenuData(); + + uint count() const; + + + int insertItem( const QString &text, + const QObject *receiver, const char* member, + const QKeySequence& accel = 0, int id = -1, int index = -1 ); + int insertItem( const QIconSet& icon, + const QString &text, + const QObject *receiver, const char* member, + const QKeySequence& accel = 0, int id = -1, int index = -1 ); + int insertItem( const QPixmap &pixmap, + const QObject *receiver, const char* member, + const QKeySequence& accel = 0, int id = -1, int index = -1 ); + int insertItem( const QIconSet& icon, + const QPixmap &pixmap, + const QObject *receiver, const char* member, + const QKeySequence& accel = 0, int id = -1, int index = -1 ); + + int insertItem( const QString &text, int id=-1, int index=-1 ); + int insertItem( const QIconSet& icon, + const QString &text, int id=-1, int index=-1 ); + + int insertItem( const QString &text, QPopupMenu *popup, + int id=-1, int index=-1 ); + int insertItem( const QIconSet& icon, + const QString &text, QPopupMenu *popup, + int id=-1, int index=-1 ); + + + int insertItem( const QPixmap &pixmap, int id=-1, int index=-1 ); + int insertItem( const QIconSet& icon, + const QPixmap &pixmap, int id=-1, int index=-1 ); + int insertItem( const QPixmap &pixmap, QPopupMenu *popup, + int id=-1, int index=-1 ); + int insertItem( const QIconSet& icon, + const QPixmap &pixmap, QPopupMenu *popup, + int id=-1, int index=-1 ); + + int insertItem( QWidget* widget, int id=-1, int index=-1 ); + + int insertItem( const QIconSet& icon, QCustomMenuItem* custom, int id=-1, int index=-1 ); + int insertItem( QCustomMenuItem* custom, int id=-1, int index=-1 ); + + + int insertSeparator( int index=-1 ); + + void removeItem( int id ); + void removeItemAt( int index ); + void clear(); + +#ifndef QT_NO_ACCEL + QKeySequence accel( int id ) const; + void setAccel( const QKeySequence& key, int id ); +#endif + + QIconSet *iconSet( int id ) const; + QString text( int id ) const; + QPixmap *pixmap( int id ) const; + + void setWhatsThis( int id, const QString& ); + QString whatsThis( int id ) const; + + + void changeItem( int id, const QString &text ); + void changeItem( int id, const QPixmap &pixmap ); + void changeItem( int id, const QIconSet &icon, const QString &text ); + void changeItem( int id, const QIconSet &icon, const QPixmap &pixmap ); + + void changeItem( const QString &text, int id ) { changeItem( id, text); } // obsolete + void changeItem( const QPixmap &pixmap, int id ) { changeItem( id, pixmap ); } // obsolete + void changeItem( const QIconSet &icon, const QString &text, int id ) { // obsolete + changeItem( id, icon, text ); + } + + bool isItemActive( int id ) const; + + bool isItemEnabled( int id ) const; + void setItemEnabled( int id, bool enable ); + + bool isItemChecked( int id ) const; + void setItemChecked( int id, bool check ); + + bool isItemVisible( int id ) const; + void setItemVisible( int id, bool visible ); + + virtual void updateItem( int id ); + + int indexOf( int id ) const; + int idAt( int index ) const; + virtual void setId( int index, int id ); + + bool connectItem( int id, + const QObject *receiver, const char* member ); + bool disconnectItem( int id, + const QObject *receiver, const char* member ); + + bool setItemParameter( int id, int param ); + int itemParameter( int id ) const; + + QMenuItem *findItem( int id ) const; + QMenuItem *findItem( int id, QMenuData ** parent ) const; + QMenuItem * findPopup( QPopupMenu *, int *index = 0 ); + + virtual void activateItemAt( int index ); + +protected: + int actItem; + QMenuItemList *mitems; + QMenuData *parentMenu; + uint isPopupMenu : 1; + uint isMenuBar : 1; + uint badSize : 1; + uint mouseBtDn : 1; + uint avoid_circularity : 1; + uint actItemDown : 1; + virtual void menuContentsChanged(); + virtual void menuStateChanged(); + virtual void menuInsPopup( QPopupMenu * ); + virtual void menuDelPopup( QPopupMenu * ); + +private: + int insertAny( const QString *, const QPixmap *, QPopupMenu *, + const QIconSet*, int, int, QWidget* = 0, QCustomMenuItem* = 0); + void removePopup( QPopupMenu * ); + void changeItemIconSet( int id, const QIconSet &icon ); + + QMenuDataData *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QMenuData( const QMenuData & ); + QMenuData &operator=( const QMenuData & ); +#endif +}; + + +#endif // QT_NO_MENUDATA + +#endif // QMENUDATA_H diff --git a/src/widgets/qmultilineedit.cpp b/src/widgets/qmultilineedit.cpp new file mode 100644 index 0000000..2cead2c --- /dev/null +++ b/src/widgets/qmultilineedit.cpp @@ -0,0 +1,541 @@ +/********************************************************************** +** +** Implementation of QMultiLineEdit widget class +** +** Created : 961005 +** +** 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 "qmultilineedit.h" +#ifndef QT_NO_MULTILINEEDIT +#include "qpainter.h" +#include "qscrollbar.h" +#include "qcursor.h" +#include "qclipboard.h" +#include "qpixmap.h" +#include "qregexp.h" +#include "qapplication.h" +#include "qdragobject.h" +#include "qpopupmenu.h" +#include "qtimer.h" +#include "qdict.h" +#include "../kernel/qrichtext_p.h" + + +/*! + \class QMultiLineEdit qmultilineedit.h + \obsolete + + \brief The QMultiLineEdit widget is a simple editor for inputting text. + + \ingroup advanced + + The QMultiLineEdit was a simple editor widget in former Qt versions. Qt + 3.0 includes a new richtext engine which obsoletes QMultiLineEdit. It is + still included for compatibility reasons. It is now a subclass of + \l QTextEdit, and provides enough of the old QMultiLineEdit API to keep old + applications working. + + If you implement something new with QMultiLineEdit, we suggest using + \l QTextEdit instead and call QTextEdit::setTextFormat(Qt::PlainText). + + Although most of the old QMultiLineEdit API is still available, there is + a few difference. The old QMultiLineEdit operated on lines, not on + paragraphs. As lines change all the time during wordwrap, the new + richtext engine uses paragraphs as basic elements in the data structure. + All functions (numLines(), textLine(), etc.) that operated on lines, now + operate on paragraphs. Further, getString() has been removed completely. + It revealed too much of the internal data structure. + + Applications which made normal and reasonable use of QMultiLineEdit + should still work without problems. Some odd usage will require some + porting. In these cases, it may be better to use \l QTextEdit now. + + <img src=qmlined-m.png> <img src=qmlined-w.png> + + \sa QTextEdit +*/ + +/*! + \fn bool QMultiLineEdit::autoUpdate() const + \obsolete +*/ + +/*! + \fn virtual void QMultiLineEdit::setAutoUpdate( bool ) + \obsolete +*/ + +/*! + \fn int QMultiLineEdit::totalWidth() const + \obsolete +*/ + +/*! + \fn int QMultiLineEdit::totalHeight() const + \obsolete +*/ + +/*! + \fn int QMultiLineEdit::maxLines() const + \obsolete +*/ + +/*! + \fn void QMultiLineEdit::setMaxLines( int ) + \obsolete +*/ + +/*! + \fn void QMultiLineEdit::deselect() + \obsolete +*/ + + +class QMultiLineEditData +{ +}; + + +/*! + Constructs a new, empty, QMultiLineEdit with parent \a parent called + \a name. +*/ + +QMultiLineEdit::QMultiLineEdit( QWidget *parent , const char *name ) + : QTextEdit( parent, name ) +{ + d = new QMultiLineEditData; + setTextFormat( Qt::PlainText ); +} + +/*! \property QMultiLineEdit::numLines + \brief the number of paragraphs in the editor + + The count includes any empty paragraph at top and bottom, so for an + empty editor this method returns 1. +*/ + +int QMultiLineEdit::numLines() const +{ + return document()->lastParagraph()->paragId() + 1; +} + +/*! \property QMultiLineEdit::atEnd + \brief whether the cursor is placed at the end of the text + + \sa atBeginning +*/ + +bool QMultiLineEdit::atEnd() const +{ + return textCursor()->paragraph() == document()->lastParagraph() && textCursor()->atParagEnd(); +} + + +/*! \property QMultiLineEdit::atBeginning + \brief whether the cursor is placed at the beginning of the text + + \sa atEnd +*/ + +bool QMultiLineEdit::atBeginning() const +{ + return textCursor()->paragraph() == document()->firstParagraph() && textCursor()->atParagStart(); +} + +/*! Returns the number of characters at paragraph number \a row. If + \a row is out of range, -1 is returned. +*/ + +int QMultiLineEdit::lineLength( int row ) const +{ + if ( row < 0 || row > numLines() ) + return -1; + return document()->paragAt( row )->length() - 1; +} + + +/*! \reimp */ + +QMultiLineEdit::~QMultiLineEdit() +{ + delete d; +} + +/*! + If there is selected text, sets \a line1, \a col1, \a line2 and \a col2 + to the start and end of the selected region and returns TRUE. Returns + FALSE if there is no selected text. + */ +bool QMultiLineEdit::getMarkedRegion( int *line1, int *col1, + int *line2, int *col2 ) const +{ + int p1,c1, p2, c2; + getSelection( &p1, &c1, &p2, &c2 ); + if ( p1 == -1 && c1 == -1 && p2 == -1 && c2 == -1 ) + return FALSE; + if ( line1 ) + *line1 = p1; + if ( col1 ) + *col1 = c1; + if ( line2 ) + *line2 = p2; + if ( col2 ) + *col2 = c2; + return TRUE; +} + + +/*! + Returns TRUE if there is selected text. +*/ + +bool QMultiLineEdit::hasMarkedText() const +{ + return hasSelectedText(); +} + + +/*! + Returns a copy of the selected text. +*/ + +QString QMultiLineEdit::markedText() const +{ + return selectedText(); +} + +/*! + Moves the cursor one page down. If \a mark is TRUE, the text + is selected. +*/ + +void QMultiLineEdit::pageDown( bool mark ) +{ + moveCursor( MoveDown, mark ); +} + + +/*! + Moves the cursor one page up. If \a mark is TRUE, the text + is selected. +*/ + +void QMultiLineEdit::pageUp( bool mark ) +{ + moveCursor( MovePgUp, mark ); +} + + +/*! Inserts \a txt at paragraph number \a line. If \a line is less + than zero, or larger than the number of paragraphs, the new text is + put at the end. If \a txt contains newline characters, several + paragraphs are inserted. + + The cursor position is not changed. +*/ + +void QMultiLineEdit::insertLine( const QString &txt, int line ) +{ + insertParagraph( txt, line ); +} + +/*! Deletes the paragraph at paragraph number \a paragraph. If \a + paragraph is less than zero or larger than the number of paragraphs, + nothing is deleted. +*/ + +void QMultiLineEdit::removeLine( int paragraph ) +{ + removeParagraph( paragraph ); +} + +/*! Inserts \a str at the current cursor position and selects the + text if \a mark is TRUE. +*/ + +void QMultiLineEdit::insertAndMark( const QString& str, bool mark ) +{ + insert( str ); + if ( mark ) + document()->setSelectionEnd( QTextDocument::Standard, *textCursor() ); +} + +/*! Splits the paragraph at the current cursor position. +*/ + +void QMultiLineEdit::newLine() +{ + insert( "\n" ); +} + + +/*! Deletes the character on the left side of the text cursor and + moves the cursor one position to the left. If a text has been selected + by the user (e.g. by clicking and dragging) the cursor is put at the + beginning of the selected text and the selected text is removed. \sa + del() +*/ + +void QMultiLineEdit::backspace() +{ + if ( document()->hasSelection( QTextDocument::Standard ) ) { + removeSelectedText(); + return; + } + + if ( !textCursor()->paragraph()->prev() && + textCursor()->atParagStart() ) + return; + + doKeyboardAction( ActionBackspace ); +} + + +/*! Moves the text cursor to the left end of the line. If \a mark is + TRUE, text is selected toward the first position. If it is FALSE and the + cursor is moved, all selected text is unselected. + + \sa end() +*/ + +void QMultiLineEdit::home( bool mark ) +{ + moveCursor( MoveLineStart, mark ); +} + +/*! Moves the text cursor to the right end of the line. If \a mark is + TRUE, text is selected toward the last position. If it is FALSE and the + cursor is moved, all selected text is unselected. + + \sa home() +*/ + +void QMultiLineEdit::end( bool mark ) +{ + moveCursor( MoveLineEnd, mark ); +} + + +/*! + \fn void QMultiLineEdit::setCursorPosition( int line, int col ) + \reimp +*/ + +/*! Sets the cursor position to character number \a col in paragraph + number \a line. The parameters are adjusted to lie within the legal + range. + + If \a mark is FALSE, the selection is cleared. otherwise it is extended. + +*/ + +void QMultiLineEdit::setCursorPosition( int line, int col, bool mark ) +{ + if ( !mark ) + selectAll( FALSE ); + QTextEdit::setCursorPosition( line, col ); + if ( mark ) + document()->setSelectionEnd( QTextDocument::Standard, *textCursor() ); +} + +/*! Returns the top center point where the cursor is drawn. +*/ + +QPoint QMultiLineEdit::cursorPoint() const +{ + return QPoint( textCursor()->x(), textCursor()->y() + textCursor()->paragraph()->rect().y() ); +} + +/*! \property QMultiLineEdit::alignment + \brief The editor's paragraph alignment + + Sets the alignment to flag, which must be \c AlignLeft, \c + AlignHCenter or \c AlignRight. + + If flag is an illegal flag nothing happens. + + \sa Qt::AlignmentFlags +*/ +void QMultiLineEdit::setAlignment( int flag ) +{ + if ( flag == AlignCenter ) + flag = AlignHCenter; + if ( flag != AlignLeft && flag != AlignRight && flag != AlignHCenter ) + return; + QTextParagraph *p = document()->firstParagraph(); + while ( p ) { + p->setAlignment( flag ); + p = p->next(); + } +} + +int QMultiLineEdit::alignment() const +{ + return document()->firstParagraph()->alignment(); +} + + +void QMultiLineEdit::setEdited( bool e ) +{ + setModified( e ); +} + +/*! \property QMultiLineEdit::edited + \brief whether the document has been edited by the user + + This is the same as QTextEdit's "modifed" property. + + \sa QTextEdit::modified +*/ +bool QMultiLineEdit::edited() const +{ + return isModified(); +} + +/*! Moves the cursor one word to the right. If \a mark is TRUE, the text + is selected. + + \sa cursorWordBackward() +*/ +void QMultiLineEdit::cursorWordForward( bool mark ) +{ + moveCursor( MoveWordForward, mark ); +} + +/*! Moves the cursor one word to the left. If \a mark is TRUE, the + text is selected. + + \sa cursorWordForward() +*/ +void QMultiLineEdit::cursorWordBackward( bool mark ) +{ + moveCursor( MoveWordBackward, mark ); +} + +/*! + \fn QMultiLineEdit::insertAt( const QString &s, int line, int col ) + \reimp +*/ + +/*! Inserts string \a s at paragraph number \a line, after character + number \a col in the paragraph. If \a s contains newline + characters, new lines are inserted. + If \a mark is TRUE the inserted string will be selected. + + The cursor position is adjusted. + */ + +void QMultiLineEdit::insertAt( const QString &s, int line, int col, bool mark ) +{ + QTextEdit::insertAt( s, line, col ); + if ( mark ) + setSelection( line, col, line, col + s.length() ); +} + +// ### reggie - is this documentation correct? + +/*! Deletes text from the current cursor position to the end of the + line. (Note that this function still operates on lines, not paragraphs.) +*/ + +void QMultiLineEdit::killLine() +{ + doKeyboardAction( ActionKill ); +} + +/*! Moves the cursor one character to the left. If \a mark is TRUE, + the text is selected. + The \a wrap parameter is currently ignored. + + \sa cursorRight() cursorUp() cursorDown() +*/ + +void QMultiLineEdit::cursorLeft( bool mark, bool ) +{ + moveCursor( MoveBackward, mark ); +} + +/*! Moves the cursor one character to the right. If \a mark is TRUE, + the text is selected. + The \a wrap parameter is currently ignored. + + \sa cursorLeft() cursorUp() cursorDown() +*/ + +void QMultiLineEdit::cursorRight( bool mark, bool ) +{ + moveCursor( MoveForward, mark ); +} + +/*! Moves the cursor up one line. If \a mark is TRUE, the text is + selected. + + \sa cursorDown() cursorLeft() cursorRight() +*/ + +void QMultiLineEdit::cursorUp( bool mark ) +{ + moveCursor( MoveUp, mark ); +} + +/*! + Moves the cursor one line down. If \a mark is TRUE, the text + is selected. + \sa cursorUp() cursorLeft() cursorRight() +*/ + +void QMultiLineEdit::cursorDown( bool mark ) +{ + moveCursor( MoveDown, mark ); +} + + +/*! Returns the text at line number \a line (possibly the empty + string), or a \link QString::operator!() null string\endlink if \a + line is invalid. +*/ + +QString QMultiLineEdit::textLine( int line ) const +{ + if ( line < 0 || line >= numLines() ) + return QString::null; + QString str = document()->paragAt( line )->string()->toString(); + str.truncate( str.length() - 1 ); + return str; +} + +#endif diff --git a/src/widgets/qmultilineedit.h b/src/widgets/qmultilineedit.h new file mode 100644 index 0000000..48e2742 --- /dev/null +++ b/src/widgets/qmultilineedit.h @@ -0,0 +1,141 @@ +/********************************************************************** +** +** Definition of QMultiLineEdit widget class +** +** Created : 961005 +** +** 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. +** +**********************************************************************/ + +#ifndef QMULTILINEEDIT_H +#define QMULTILINEEDIT_H + +#ifndef QT_H +#include "qtextedit.h" +#endif // QT_H + +#ifndef QT_NO_MULTILINEEDIT + +class QMultiLineEditCommand; +class QValidator; +class QMultiLineEditData; + +class Q_EXPORT QMultiLineEdit : public QTextEdit +{ + Q_OBJECT + Q_PROPERTY( int numLines READ numLines ) + Q_PROPERTY( bool atBeginning READ atBeginning ) + Q_PROPERTY( bool atEnd READ atEnd ) + Q_PROPERTY( Alignment alignment READ alignment WRITE setAlignment ) + Q_PROPERTY( bool edited READ edited WRITE setEdited DESIGNABLE false ) + +public: + QMultiLineEdit( QWidget* parent=0, const char* name=0 ); + ~QMultiLineEdit(); + + QString textLine( int line ) const; + int numLines() const; + + virtual void insertLine( const QString &s, int line = -1 ); + virtual void insertAt( const QString &s, int line, int col ) { + insertAt( s, line, col, FALSE ); + } + virtual void insertAt( const QString &s, int line, int col, bool mark ); + virtual void removeLine( int line ); + virtual void setCursorPosition( int line, int col ) { + setCursorPosition( line, col, FALSE ); + } + virtual void setCursorPosition( int line, int col, bool mark ); + bool atBeginning() const; + bool atEnd() const; + + void setAlignment( int flags ); + int alignment() const; + + void setEdited( bool ); + bool edited() const; + + bool hasMarkedText() const; + QString markedText() const; + + void cursorWordForward( bool mark ); + void cursorWordBackward( bool mark ); + + // noops + bool autoUpdate() const { return TRUE; } + virtual void setAutoUpdate( bool ) {} + + int totalWidth() const { return contentsWidth(); } + int totalHeight() const { return contentsHeight(); } + + int maxLines() const { return QWIDGETSIZE_MAX; } + void setMaxLines( int ) {} + +public slots: + void deselect() { selectAll( FALSE ); } + +protected: + QPoint cursorPoint() const; + +protected: + virtual void insertAndMark( const QString&, bool mark ); + virtual void newLine(); + virtual void killLine(); + virtual void pageUp( bool mark=FALSE ); + virtual void pageDown( bool mark=FALSE ); + virtual void cursorLeft( bool mark=FALSE, bool wrap = TRUE ); + virtual void cursorRight( bool mark=FALSE, bool wrap = TRUE ); + virtual void cursorUp( bool mark=FALSE ); + virtual void cursorDown( bool mark=FALSE ); + virtual void backspace(); + virtual void home( bool mark=FALSE ); + virtual void end( bool mark=FALSE ); + + bool getMarkedRegion( int *line1, int *col1, + int *line2, int *col2 ) const; + int lineLength( int row ) const; + +private: + QMultiLineEditData *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QMultiLineEdit( const QMultiLineEdit & ); + QMultiLineEdit &operator=( const QMultiLineEdit & ); +#endif +}; + +#endif // QT_NO_MULTILINEEDIT + +#endif // QMULTILINED_H diff --git a/src/widgets/qpopupmenu.cpp b/src/widgets/qpopupmenu.cpp new file mode 100644 index 0000000..9e1e01c --- /dev/null +++ b/src/widgets/qpopupmenu.cpp @@ -0,0 +1,2886 @@ +/**************************************************************************** +** +** 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(); + 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++; +} + +#endif // QT_NO_POPUPMENU + diff --git a/src/widgets/qpopupmenu.h b/src/widgets/qpopupmenu.h new file mode 100644 index 0000000..0609731 --- /dev/null +++ b/src/widgets/qpopupmenu.h @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Definition 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. +** +**********************************************************************/ + +#ifndef QPOPUPMENU_H +#define QPOPUPMENU_H + +#ifndef QT_H +#include "qframe.h" +#include "qmenudata.h" +#endif // QT_H + +#ifndef QT_NO_POPUPMENU +class QPopupMenuPrivate; + +class Q_EXPORT QPopupMenu : public QFrame, public QMenuData +{ + Q_OBJECT + Q_PROPERTY( bool checkable READ isCheckable WRITE setCheckable ) +public: + QPopupMenu( QWidget* parent=0, const char* name=0 ); + ~QPopupMenu(); + + void popup( const QPoint & pos, int indexAtPoint = -1 ); // open + void updateItem( int id ); + + virtual void setCheckable( bool ); + bool isCheckable() const; + + void setFont( const QFont & ); + void show(); + void hide(); + + int exec(); + int exec( const QPoint & pos, int indexAtPoint = 0 ); // modal + + virtual void setActiveItem( int ); + QSize sizeHint() const; + + int idAt( int index ) const { return QMenuData::idAt( index ); } + int idAt( const QPoint& pos ) const; + + bool customWhatsThis() const; + + int insertTearOffHandle( int id=-1, int index=-1 ); + + void activateItemAt( int index ); + QRect itemGeometry( int index ); + + +signals: + void activated( int itemId ); + void highlighted( int itemId ); + void activatedRedirect( int itemId ); // to parent menu + void highlightedRedirect( int itemId ); + void aboutToShow(); + void aboutToHide(); + +protected: + int itemHeight( int ) const; + int itemHeight( QMenuItem* mi ) const; + void drawItem( QPainter* p, int tab, QMenuItem* mi, + bool act, int x, int y, int w, int h); + + void drawContents( QPainter * ); + + void closeEvent( QCloseEvent *e ); + void paintEvent( QPaintEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void keyPressEvent( QKeyEvent * ); + void focusInEvent( QFocusEvent * ); + void focusOutEvent( QFocusEvent * ); + void timerEvent( QTimerEvent * ); + void leaveEvent( QEvent * ); + void styleChange( QStyle& ); + void enabledChange( bool ); + int columns() const; + + bool focusNextPrevChild( bool next ); + + int itemAtPos( const QPoint &, bool ignoreSeparator = TRUE ) const; + +private slots: + void subActivated( int itemId ); + void subHighlighted( int itemId ); +#ifndef QT_NO_ACCEL + void accelActivated( int itemId ); + void accelDestroyed(); +#endif + void popupDestroyed( QObject* ); + void modalActivation( int ); + + void subMenuTimer(); + void subScrollTimer(); + void allowAnimation(); + void toggleTearOff(); + + void performDelayedChanges(); + +private: + void updateScrollerState(); + void menuContentsChanged(); + void menuStateChanged(); + void performDelayedContentsChanged(); + void performDelayedStateChanged(); + void menuInsPopup( QPopupMenu * ); + void menuDelPopup( QPopupMenu * ); + void frameChanged(); + + void actSig( int, bool = FALSE ); + void hilitSig( int ); + virtual void setFirstItemActive(); + void hideAllPopups(); + void hidePopups(); + bool tryMenuBar( QMouseEvent * ); + void byeMenuBar(); + + QSize updateSize(bool force_recalc=FALSE, bool do_resize=TRUE); + void updateRow( int row ); + QRect screenRect(const QPoint& pos); +#ifndef QT_NO_ACCEL + void updateAccel( QWidget * ); + void enableAccel( bool ); +#endif + QPopupMenuPrivate *d; +#ifndef QT_NO_ACCEL + QAccel *autoaccel; +#endif + +#if defined(Q_WS_MAC) && !defined(QMAC_QMENUBAR_NO_NATIVE) + bool macPopupMenu(const QPoint &, int); + uint mac_dirty_popup : 1; +#endif + + int popupActive; + int tab; + uint accelDisabled : 1; + uint checkable : 1; + uint connectModalRecursionSafety : 1; + uint tornOff : 1; + uint pendingDelayedContentsChanges : 1; + uint pendingDelayedStateChanges : 1; + int maxPMWidth; + int ncols; + bool snapToMouse; + bool tryMouseEvent( QPopupMenu *, QMouseEvent * ); + + friend class QMenuData; + friend class QMenuBar; + + void connectModal(QPopupMenu* receiver, bool doConnect); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QPopupMenu( const QPopupMenu & ); + QPopupMenu &operator=( const QPopupMenu & ); +#endif +}; + + +#endif // QT_NO_POPUPMENU + +#endif // QPOPUPMENU_H diff --git a/src/widgets/qprogressbar.cpp b/src/widgets/qprogressbar.cpp new file mode 100644 index 0000000..a64ea8c --- /dev/null +++ b/src/widgets/qprogressbar.cpp @@ -0,0 +1,408 @@ +/**************************************************************************** +** +** Implementation of QProgressBar class +** +** Created : 970521 +** +** 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 "qprogressbar.h" +#ifndef QT_NO_PROGRESSBAR +#include "qpainter.h" +#include "qdrawutil.h" +#include "qpixmap.h" +#include "qstyle.h" +#include "../kernel/qinternal_p.h" +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif +#include <limits.h> + +/*! + \class QProgressBar qprogressbar.h + \brief The QProgressBar widget provides a horizontal progress bar. + + \ingroup advanced + \mainclass + + A progress bar is used to give the user an indication of the + progress of an operation and to reassure them that the application + is still running. + + The progress bar uses the concept of \e steps; you give it the + total number of steps and the number of steps completed so far and + it will display the percentage of steps that have been completed. + You can specify the total number of steps in the constructor or + later with setTotalSteps(). The current number of steps is set + with setProgress(). The progress bar can be rewound to the + beginning with reset(). + + If the total is given as 0 the progress bar shows a busy indicator + instead of a percentage of steps. This is useful, for example, + when using QFtp or QHttp to download items when they are unable to + determine the size of the item being downloaded. + + \sa QProgressDialog + + <img src=qprogbar-m.png> <img src=qprogbar-w.png> + + \sa QProgressDialog + \link guibooks.html#fowler GUI Design Handbook: Progress Indicator\endlink +*/ + + +/*! + Constructs a progress bar. + + The total number of steps is set to 100 by default. + + The \a parent, \a name and widget flags, \a f, are passed on to + the QFrame::QFrame() constructor. + + \sa setTotalSteps() +*/ + +QProgressBar::QProgressBar( QWidget *parent, const char *name, WFlags f ) + : QFrame( parent, name, f | WNoAutoErase ), + total_steps( 100 ), + progress_val( -1 ), + percentage( -1 ), + center_indicator( TRUE ), + auto_indicator( TRUE ), + percentage_visible( TRUE ), + d( 0 ) +{ + setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + initFrame(); +} + + +/*! + Constructs a progress bar. + + The \a totalSteps is the total number of steps that need to be + completed for the operation which this progress bar represents. + For example, if the operation is to examine 50 files, this value + would be 50. Before examining the first file, call setProgress(0); + call setProgress(50) after examining the last file. + + The \a parent, \a name and widget flags, \a f, are passed to the + QFrame::QFrame() constructor. + + \sa setTotalSteps(), setProgress() +*/ + +QProgressBar::QProgressBar( int totalSteps, + QWidget *parent, const char *name, WFlags f ) + : QFrame( parent, name, f | WNoAutoErase ), + total_steps( totalSteps ), + progress_val( -1 ), + percentage( -1 ), + center_indicator( TRUE ), + auto_indicator( TRUE ), + percentage_visible( TRUE ), + d( 0 ) +{ + setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + initFrame(); +} + + +/*! + Reset the progress bar. The progress bar "rewinds" and shows no + progress. +*/ + +void QProgressBar::reset() +{ + progress_val = -1; + percentage = -1; + setIndicator(progress_str, progress_val, total_steps); + repaint( FALSE ); +} + + +/*! + \property QProgressBar::totalSteps + \brief The total number of steps. + + If totalSteps is 0, the progress bar will display a busy + indicator. + + \sa totalSteps() +*/ + +void QProgressBar::setTotalSteps( int totalSteps ) +{ + total_steps = totalSteps; + + // Current progress is invalid if larger than total + if ( total_steps < progress_val ) + progress_val = -1; + + if ( isVisible() && + ( setIndicator(progress_str, progress_val, total_steps) || !total_steps ) ) + repaint( FALSE ); +} + + +/*! + \property QProgressBar::progress + \brief The current amount of progress + + This property is -1 if progress counting has not started. +*/ + +void QProgressBar::setProgress( int progress ) +{ + if ( progress == progress_val || + progress < 0 || ( ( progress > total_steps ) && total_steps ) ) + return; + + progress_val = progress; + + setIndicator( progress_str, progress_val, total_steps ); + + repaint( FALSE ); + +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged ); +#endif +} + +/*! + \overload + + Sets the amount of progress to \a progress and the total number of + steps to \a totalSteps. + + \sa setTotalSteps() +*/ + +void QProgressBar::setProgress( int progress, int totalSteps ) +{ + if ( total_steps != totalSteps ) + setTotalSteps( totalSteps ); + setProgress( progress ); +} + +/*! + \property QProgressBar::progressString + \brief the amount of progress as a string + + This property is QString::null if progress counting has not started. +*/ + + +/*! + \reimp +*/ +QSize QProgressBar::sizeHint() const +{ + constPolish(); + QFontMetrics fm = fontMetrics(); + int cw = style().pixelMetric(QStyle::PM_ProgressBarChunkWidth, this); + return style().sizeFromContents(QStyle::CT_ProgressBar, this, + QSize( cw * 7 + fm.width( '0' ) * 4, + fm.height() + 8)); +} + + +/*! + \reimp +*/ +QSize QProgressBar::minimumSizeHint() const +{ + return sizeHint(); +} + +/*! + \property QProgressBar::centerIndicator + \brief whether the indicator string should be centered + + Changing this property sets \l QProgressBar::indicatorFollowsStyle + to FALSE. The default is TRUE. +*/ + +void QProgressBar::setCenterIndicator( bool on ) +{ + if ( !auto_indicator && on == center_indicator ) + return; + auto_indicator = FALSE; + center_indicator = on; + repaint( FALSE ); +} + +/*! + \property QProgressBar::indicatorFollowsStyle + \brief whether the display of the indicator string should follow the GUI style + + The default is TRUE. + + \sa centerIndicator +*/ + +void QProgressBar::setIndicatorFollowsStyle( bool on ) +{ + if ( on == auto_indicator ) + return; + auto_indicator = on; + repaint( FALSE ); +} + +/*! + \property QProgressBar::percentageVisible + \brief whether the current progress value is displayed + + The default is TRUE. + + \sa centerIndicator, indicatorFollowsStyle +*/ +void QProgressBar::setPercentageVisible( bool on ) +{ + if ( on == percentage_visible ) + return; + percentage_visible = on; + repaint( FALSE ); +} + +/*! + \reimp +*/ +void QProgressBar::show() +{ + setIndicator( progress_str, progress_val, total_steps ); + QFrame::show(); +} + +void QProgressBar::initFrame() +{ + setFrameStyle(QFrame::NoFrame); +} + +/*! + \reimp +*/ +void QProgressBar::styleChange( QStyle& old ) +{ + initFrame(); + QFrame::styleChange( old ); +} + + +/*! + This method is called to generate the text displayed in the center + (or in some styles, to the left) of the progress bar. + + The \a progress may be negative, indicating that the progress bar + is in the "reset" state before any progress is set. + + The default implementation is the percentage of completion or + blank in the reset state. The percentage is calculated based on + the \a progress and \a totalSteps. You can set the \a indicator + text if you wish. + + To allow efficient repainting of the progress bar, this method + should return FALSE if the string is unchanged from the last call + to this function. +*/ + +bool QProgressBar::setIndicator( QString & indicator, int progress, + int totalSteps ) +{ + if ( !totalSteps ) + return FALSE; + if ( progress < 0 ) { + indicator = QString::fromLatin1(""); + return TRUE; + } else { + // Get the values down to something usable. + if ( totalSteps > INT_MAX/1000 ) { + progress /= 1000; + totalSteps /= 1000; + } + + int np = progress * 100 / totalSteps; + if ( np != percentage ) { + percentage = np; + indicator.sprintf( "%d%%", np ); + return TRUE; + } else { + return FALSE; + } + } +} + + +/*! + \reimp +*/ +void QProgressBar::drawContents( QPainter *p ) +{ + const QRect bar = contentsRect(); + + QSharedDoubleBuffer buffer( p, bar.x(), bar.y(), bar.width(), bar.height() ); + + QPoint pn = backgroundOffset(); + buffer.painter()->setBrushOrigin( -pn.x(), -pn.y() ); + + const QPixmap *bpm = paletteBackgroundPixmap(); + if ( bpm ) + buffer.painter()->fillRect( bar, QBrush( paletteBackgroundColor(), *bpm ) ); + else + buffer.painter()->fillRect( bar, paletteBackgroundColor() ); + buffer.painter()->setFont( p->font() ); + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (hasFocus()) + flags |= QStyle::Style_HasFocus; + + style().drawControl(QStyle::CE_ProgressBarGroove, buffer.painter(), this, + QStyle::visualRect(style().subRect(QStyle::SR_ProgressBarGroove, this), this ), + colorGroup(), flags); + + style().drawControl(QStyle::CE_ProgressBarContents, buffer.painter(), this, + QStyle::visualRect(style().subRect(QStyle::SR_ProgressBarContents, this), this ), + colorGroup(), flags); + + if (percentageVisible()) + style().drawControl(QStyle::CE_ProgressBarLabel, buffer.painter(), this, + QStyle::visualRect(style().subRect(QStyle::SR_ProgressBarLabel, this), this ), + colorGroup(), flags); +} + +#endif diff --git a/src/widgets/qprogressbar.h b/src/widgets/qprogressbar.h new file mode 100644 index 0000000..2c4f039 --- /dev/null +++ b/src/widgets/qprogressbar.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Definition of QProgressBar class +** +** Created : 970520 +** +** 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. +** +**********************************************************************/ + +#ifndef QPROGRESSBAR_H +#define QPROGRESSBAR_H + +#ifndef QT_H +#include "qframe.h" +#endif // QT_H + +#ifndef QT_NO_PROGRESSBAR + + +class QProgressBarPrivate; + + +class Q_EXPORT QProgressBar : public QFrame +{ + Q_OBJECT + Q_PROPERTY( int totalSteps READ totalSteps WRITE setTotalSteps ) + Q_PROPERTY( int progress READ progress WRITE setProgress ) + Q_PROPERTY( QString progressString READ progressString ) + Q_PROPERTY( bool centerIndicator READ centerIndicator WRITE setCenterIndicator ) + Q_PROPERTY( bool indicatorFollowsStyle READ indicatorFollowsStyle WRITE setIndicatorFollowsStyle ) + Q_PROPERTY( bool percentageVisible READ percentageVisible WRITE setPercentageVisible ) + +public: + QProgressBar( QWidget* parent=0, const char* name=0, WFlags f=0 ); + QProgressBar( int totalSteps, QWidget* parent=0, const char* name=0, WFlags f=0 ); + + int totalSteps() const; + int progress() const; + const QString &progressString() const; + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + void setCenterIndicator( bool on ); + bool centerIndicator() const; + + void setIndicatorFollowsStyle( bool ); + bool indicatorFollowsStyle() const; + + bool percentageVisible() const; + void setPercentageVisible( bool ); + + void show(); + +public slots: + void reset(); + virtual void setTotalSteps( int totalSteps ); + virtual void setProgress( int progress ); + void setProgress( int progress, int totalSteps ); + +protected: + void drawContents( QPainter * ); + virtual bool setIndicator( QString & progress_str, int progress, + int totalSteps ); + void styleChange( QStyle& ); + +private: + int total_steps; + int progress_val; + int percentage; + QString progress_str; + bool center_indicator : 1; + bool auto_indicator : 1; + bool percentage_visible : 1; + QProgressBarPrivate * d; + void initFrame(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QProgressBar( const QProgressBar & ); + QProgressBar &operator=( const QProgressBar & ); +#endif +}; + + +inline int QProgressBar::totalSteps() const +{ + return total_steps; +} + +inline int QProgressBar::progress() const +{ + return progress_val; +} + +inline const QString &QProgressBar::progressString() const +{ + return progress_str; +} + +inline bool QProgressBar::centerIndicator() const +{ + return center_indicator; +} + +inline bool QProgressBar::indicatorFollowsStyle() const +{ + return auto_indicator; +} + +inline bool QProgressBar::percentageVisible() const +{ + return percentage_visible; +} + +#endif // QT_NO_PROGRESSBAR + +#endif // QPROGRESSBAR_H diff --git a/src/widgets/qpushbutton.cpp b/src/widgets/qpushbutton.cpp new file mode 100644 index 0000000..e3db3f7 --- /dev/null +++ b/src/widgets/qpushbutton.cpp @@ -0,0 +1,760 @@ +/**************************************************************************** +** +** Implementation of QPushButton class +** +** Created : 940221 +** +** 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 "qpushbutton.h" +#ifndef QT_NO_PUSHBUTTON +#include "qdialog.h" +#include "qfontmetrics.h" +#include "qpainter.h" +#include "qdrawutil.h" +#include "qpixmap.h" +#include "qbitmap.h" +#include "qpopupmenu.h" +#include "qguardedptr.h" +#include "qapplication.h" +#include "qtoolbar.h" +#include "qstyle.h" +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif + +/*! + \class QPushButton qpushbutton.h + \brief The QPushButton widget provides a command button. + + \ingroup basic + \mainclass + + The push button, or command button, is perhaps the most commonly + used widget in any graphical user interface. Push (click) a button + to command the computer to perform some action, or to answer a + question. Typical buttons are OK, Apply, Cancel, Close, Yes, No + and Help. + + A command button is rectangular and typically displays a text + label describing its action. An underlined character in the label + (signified by preceding it with an ampersand in the text) + indicates an accelerator key, e.g. + \code + QPushButton *pb = new QPushButton( "&Download", this ); + \endcode + In this example the accelerator is \e{Alt+D}, and the label text + will be displayed as <b><u>D</u>ownload</b>. + + Push buttons can display a textual label or a pixmap, and + optionally a small icon. These can be set using the constructors + and changed later using setText(), setPixmap() and setIconSet(). + If the button is disabled the appearance of the text or pixmap and + iconset will be manipulated with respect to the GUI style to make + the button look "disabled". + + A push button emits the signal clicked() when it is activated by + the mouse, the Spacebar or by a keyboard accelerator. Connect to + this signal to perform the button's action. Push buttons also + provide less commonly used signals, for example, pressed() and + released(). + + Command buttons in dialogs are by default auto-default buttons, + i.e. they become the default push button automatically when they + receive the keyboard input focus. A default button is a push + button that is activated when the user presses the Enter or Return + key in a dialog. You can change this with setAutoDefault(). Note + that auto-default buttons reserve a little extra space which is + necessary to draw a default-button indicator. If you do not want + this space around your buttons, call setAutoDefault(FALSE). + + Being so central, the button widget has grown to accommodate a + great many variations in the past decade. The Microsoft style + guide now shows about ten different states of Windows push buttons + and the text implies that there are dozens more when all the + combinations of features are taken into consideration. + + The most important modes or states are: + \list + \i Available or not (grayed out, disabled). + \i Standard push button, toggling push button or menu button. + \i On or off (only for toggling push buttons). + \i Default or normal. The default button in a dialog can generally + be "clicked" using the Enter or Return key. + \i Auto-repeat or not. + \i Pressed down or not. + \endlist + + As a general rule, use a push button when the application or + dialog window performs an action when the user clicks on it (such + as Apply, Cancel, Close and Help) \e and when the widget is + supposed to have a wide, rectangular shape with a text label. + Small, typically square buttons that change the state of the + window rather than performing an action (such as the buttons in + the top-right corner of the QFileDialog) are not command buttons, + but tool buttons. Qt provides a special class (QToolButton) for + these buttons. + + If you need toggle behavior (see setToggleButton()) or a button + that auto-repeats the activation signal when being pushed down + like the arrows in a scroll bar (see setAutoRepeat()), a command + button is probably not what you want. When in doubt, use a tool + button. + + A variation of a command button is a menu button. These provide + not just one command, but several, since when they are clicked + they pop up a menu of options. Use the method setPopup() to + associate a popup menu with a push button. + + Other classes of buttons are option buttons (see QRadioButton) and + check boxes (see QCheckBox). + + <img src="qpushbt-m.png"> <img src="qpushbt-w.png"> + + In Qt, the QButton abstract base class provides most of the modes + and other API, and QPushButton provides GUI logic. See QButton for + more information about the API. + + \important text, setText, text, pixmap, setPixmap, accel, setAccel, + isToggleButton, setDown, isDown, isOn, state, autoRepeat, + isExclusiveToggle, group, setAutoRepeat, toggle, pressed, released, + clicked, toggled, state stateChanged + + \sa QToolButton, QRadioButton QCheckBox + \link guibooks.html#fowler GUI Design Handbook: Push Button\endlink +*/ + +/*! + \property QPushButton::autoDefault + \brief whether the push button is the auto default button + + If this property is set to TRUE then the push button is the auto + default button in a dialog. + + In some GUI styles a default button is drawn with an extra frame + around it, up to 3 pixels or more. Qt automatically keeps this + space free around auto-default buttons, i.e. auto-default buttons + may have a slightly larger size hint. + + This property's default is TRUE for buttons that have a QDialog + parent; otherwise it defaults to FALSE. + + See the \l default property for details of how \l default and + auto-default interact. +*/ + +/*! + \property QPushButton::autoMask + \brief whether the button is automatically masked + + \sa QWidget::setAutoMask() +*/ + +/*! + \property QPushButton::default + \brief whether the push button is the default button + + If this property is set to TRUE then the push button will be + pressed if the user presses the Enter (or Return) key in a dialog. + + Regardless of focus, if the user presses Enter: If there is a + default button the default button is pressed; otherwise, if + there are one or more \l autoDefault buttons the first \l autoDefault + button that is next in the tab order is pressed. If there are no + default or \l autoDefault buttons only pressing Space on a button + with focus, mouse clicking, or using an accelerator will press a + button. + + In a dialog, only one push button at a time can be the default + button. This button is then displayed with an additional frame + (depending on the GUI style). + + The default button behavior is provided only in dialogs. Buttons + can always be clicked from the keyboard by pressing Enter (or + Return) or the Spacebar when the button has focus. + + This property's default is FALSE. +*/ + +/*! + \property QPushButton::flat + \brief whether the border is disabled + + This property's default is FALSE. +*/ + +/*! + \property QPushButton::iconSet + \brief the icon set on the push button + + This property will return 0 if the push button has no iconset. +*/ + +/*! + \property QPushButton::on + \brief whether the push button is toggled + + This property should only be set for toggle push buttons. The + default value is FALSE. + + \sa isOn(), toggle(), toggled(), isToggleButton() +*/ + +/*! + \property QPushButton::toggleButton + \brief whether the button is a toggle button + + Toggle buttons have an on/off state similar to \link QCheckBox + check boxes. \endlink A push button is initially not a toggle + button. + + \sa setOn(), toggle(), isToggleButton() toggled() +*/ + +/*! \property QPushButton::menuButton + \brief whether the push button has a menu button on it + \obsolete + + If this property is set to TRUE, then a down arrow is drawn on the push + button to indicate that a menu will pop up if the user clicks on the + arrow. +*/ + +class QPushButtonPrivate +{ +public: + QPushButtonPrivate() + :iconset( 0 ) + {} + ~QPushButtonPrivate() + { +#ifndef QT_NO_ICONSET + delete iconset; +#endif + } +#ifndef QT_NO_POPUPMENU + QGuardedPtr<QPopupMenu> popup; +#endif + QIconSet* iconset; +}; + + +/*! + Constructs a push button with no text. + + The \a parent and \a name arguments are sent on to the QWidget + constructor. +*/ + +QPushButton::QPushButton( QWidget *parent, const char *name ) + : QButton( parent, name ) +{ + init(); +} + +/*! + Constructs a push button called \a name with the parent \a parent + and the text \a text. +*/ + +QPushButton::QPushButton( const QString &text, QWidget *parent, + const char *name ) + : QButton( parent, name ) +{ + init(); + setText( text ); +} + + +/*! + Constructs a push button with an \a icon and a \a text. + + Note that you can also pass a QPixmap object as an icon (thanks to + the implicit type conversion provided by C++). + + The \a parent and \a name arguments are sent to the QWidget + constructor. +*/ +#ifndef QT_NO_ICONSET +QPushButton::QPushButton( const QIconSet& icon, const QString &text, + QWidget *parent, const char *name ) + : QButton( parent, name ) +{ + init(); + setText( text ); + setIconSet( icon ); +} +#endif + + +/*! + Destroys the push button. +*/ +QPushButton::~QPushButton() +{ + delete d; +} + +void QPushButton::init() +{ + d = 0; + defButton = FALSE; + lastEnabled = FALSE; + hasMenuArrow = FALSE; + flt = FALSE; +#ifndef QT_NO_DIALOG + autoDefButton = ::qt_cast<QDialog*>(topLevelWidget()) != 0; +#else + autoDefButton = FALSE; +#endif + setBackgroundMode( PaletteButton ); + setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) ); +} + + +/* + Makes the push button a toggle button if \a enable is TRUE or a normal + push button if \a enable is FALSE. + + Toggle buttons have an on/off state similar to \link QCheckBox check + boxes. \endlink A push button is initially not a toggle button. + + \sa setOn(), toggle(), isToggleButton() toggled() +*/ + +void QPushButton::setToggleButton( bool enable ) +{ + QButton::setToggleButton( enable ); +} + + +/* + Switches a toggle button on if \a enable is TRUE or off if \a enable is + FALSE. + \sa isOn(), toggle(), toggled(), isToggleButton() +*/ + +void QPushButton::setOn( bool enable ) +{ + if ( !isToggleButton() ) + return; + QButton::setOn( enable ); +} + +void QPushButton::setAutoDefault( bool enable ) +{ + if ( (bool)autoDefButton == enable ) + return; + autoDefButton = enable; + update(); + updateGeometry(); +} + + +void QPushButton::setDefault( bool enable ) +{ + if ( (bool)defButton == enable ) + return; // no change + defButton = enable; +#ifndef QT_NO_DIALOG + if ( defButton && ::qt_cast<QDialog*>(topLevelWidget()) ) + ((QDialog*)topLevelWidget())->setMainDefault( this ); +#endif + update(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::StateChanged ); +#endif +} + + +/*! + \reimp +*/ +QSize QPushButton::sizeHint() const +{ + constPolish(); + + int w = 0, h = 0; + + // calculate contents size... +#ifndef QT_NO_ICONSET + if ( iconSet() && !iconSet()->isNull() ) { + int iw = iconSet()->pixmap( QIconSet::Small, QIconSet::Normal ).width() + 4; + int ih = iconSet()->pixmap( QIconSet::Small, QIconSet::Normal ).height(); + w += iw; + h = QMAX( h, ih ); + } +#endif + if ( isMenuButton() ) + w += style().pixelMetric(QStyle::PM_MenuButtonIndicator, this); + + if ( pixmap() ) { + QPixmap *pm = (QPixmap *)pixmap(); + w += pm->width(); + h += pm->height(); + } else { + QString s( text() ); + bool empty = s.isEmpty(); + if ( empty ) + s = QString::fromLatin1("XXXX"); + QFontMetrics fm = fontMetrics(); + QSize sz = fm.size( ShowPrefix, s ); + if(!empty || !w) + w += sz.width(); + if(!empty || !h) + h = QMAX(h, sz.height()); + } + + return (style().sizeFromContents(QStyle::CT_PushButton, this, QSize(w, h)). + expandedTo(QApplication::globalStrut())); +} + + +/*! + \reimp +*/ +void QPushButton::move( int x, int y ) +{ + QWidget::move( x, y ); +} + +/*! + \reimp +*/ +void QPushButton::move( const QPoint &p ) +{ + move( p.x(), p.y() ); +} + +/*! + \reimp +*/ +void QPushButton::resize( int w, int h ) +{ + QWidget::resize( w, h ); +} + +/*! + \reimp +*/ +void QPushButton::resize( const QSize &s ) +{ + resize( s.width(), s.height() ); +} + +/*! + \reimp +*/ +void QPushButton::setGeometry( int x, int y, int w, int h ) +{ + QWidget::setGeometry( x, y, w, h ); +} + +/*! + \reimp +*/ +void QPushButton::setGeometry( const QRect &r ) +{ + QWidget::setGeometry( r ); +} + +/*! + \reimp + */ +void QPushButton::resizeEvent( QResizeEvent * ) +{ + if ( autoMask() ) + updateMask(); +} + +/*! + \reimp +*/ +void QPushButton::drawButton( QPainter *paint ) +{ + int diw = 0; + if ( isDefault() || autoDefault() ) { + diw = style().pixelMetric(QStyle::PM_ButtonDefaultIndicator, this); + + if ( diw > 0 ) { + if (backgroundMode() == X11ParentRelative) { + erase( 0, 0, width(), diw ); + erase( 0, 0, diw, height() ); + erase( 0, height() - diw, width(), diw ); + erase( width() - diw, 0, diw, height() ); + } else if ( parentWidget() && parentWidget()->backgroundPixmap() ){ + // pseudo tranparency + paint->drawTiledPixmap( 0, 0, width(), diw, + *parentWidget()->backgroundPixmap(), + x(), y() ); + paint->drawTiledPixmap( 0, 0, diw, height(), + *parentWidget()->backgroundPixmap(), + x(), y() ); + paint->drawTiledPixmap( 0, height()-diw, width(), diw, + *parentWidget()->backgroundPixmap(), + x(), y()+height() ); + paint->drawTiledPixmap( width()-diw, 0, diw, height(), + *parentWidget()->backgroundPixmap(), + x()+width(), y() ); + } else { + paint->fillRect( 0, 0, width(), diw, + colorGroup().brush(QColorGroup::Background) ); + paint->fillRect( 0, 0, diw, height(), + colorGroup().brush(QColorGroup::Background) ); + paint->fillRect( 0, height()-diw, width(), diw, + colorGroup().brush(QColorGroup::Background) ); + paint->fillRect( width()-diw, 0, diw, height(), + colorGroup().brush(QColorGroup::Background) ); + } + + } + } + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (hasFocus()) + flags |= QStyle::Style_HasFocus; + if (isDown()) + flags |= QStyle::Style_Down; + if (isOn()) + flags |= QStyle::Style_On; + if (! isFlat() && ! isDown()) + flags |= QStyle::Style_Raised; + if (isDefault()) + flags |= QStyle::Style_ButtonDefault; + + style().drawControl(QStyle::CE_PushButton, paint, this, rect(), colorGroup(), flags); + drawButtonLabel( paint ); + + lastEnabled = isEnabled(); +} + + +/*! + \reimp +*/ +void QPushButton::drawButtonLabel( QPainter *paint ) +{ + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (hasFocus()) + flags |= QStyle::Style_HasFocus; + if (isDown()) + flags |= QStyle::Style_Down; + if (isOn()) + flags |= QStyle::Style_On; + if (! isFlat() && ! isDown()) + flags |= QStyle::Style_Raised; + if (isDefault()) + flags |= QStyle::Style_ButtonDefault; + + style().drawControl(QStyle::CE_PushButtonLabel, paint, this, + style().subRect(QStyle::SR_PushButtonContents, this), + colorGroup(), flags); +} + + +/*! + \reimp + */ +void QPushButton::updateMask() +{ + QBitmap bm( size() ); + bm.fill( color0 ); + + { + QPainter p( &bm, this ); + style().drawControlMask(QStyle::CE_PushButton, &p, this, rect()); + } + + setMask( bm ); +} + +/*! + \reimp +*/ +void QPushButton::focusInEvent( QFocusEvent *e ) +{ + if (autoDefButton && !defButton) { + defButton = TRUE; +#ifndef QT_NO_DIALOG + if ( defButton && ::qt_cast<QDialog*>(topLevelWidget()) ) + ((QDialog*)topLevelWidget())->setDefault( this ); +#endif + } + QButton::focusInEvent( e ); +} + +/*! + \reimp +*/ +void QPushButton::focusOutEvent( QFocusEvent *e ) +{ +#ifndef QT_NO_DIALOG + if ( defButton && autoDefButton ) { + if ( ::qt_cast<QDialog*>(topLevelWidget()) ) + ((QDialog*)topLevelWidget())->setDefault( 0 ); + } +#endif + + QButton::focusOutEvent( e ); +#ifndef QT_NO_POPUPMENU + if ( popup() && popup()->isVisible() ) // restore pressed status + setDown( TRUE ); +#endif +} + + +#ifndef QT_NO_POPUPMENU +/*! + Associates the popup menu \a popup with this push button. This + turns the button into a menu button. + + Ownership of the popup menu is \e not transferred to the push + button. + + \sa popup() +*/ +void QPushButton::setPopup( QPopupMenu* popup ) +{ + if ( !d ) + d = new QPushButtonPrivate; + if ( popup && !d->popup ) + connect( this, SIGNAL( pressed() ), this, SLOT( popupPressed() ) ); + + d->popup = popup; + setIsMenuButton( popup != 0 ); +} +#endif //QT_NO_POPUPMENU +#ifndef QT_NO_ICONSET +void QPushButton::setIconSet( const QIconSet& icon ) +{ + if ( !d ) + d = new QPushButtonPrivate; + if ( !icon.isNull() ) { + if ( d->iconset ) + *d->iconset = icon; + else + d->iconset = new QIconSet( icon ); + } else if ( d->iconset) { + delete d->iconset; + d->iconset = 0; + } + + update(); + updateGeometry(); +} + + +QIconSet* QPushButton::iconSet() const +{ + return d ? d->iconset : 0; +} +#endif // QT_NO_ICONSET +#ifndef QT_NO_POPUPMENU +/*! + Returns the button's associated popup menu or 0 if no popup menu + has been set. + + \sa setPopup() +*/ +QPopupMenu* QPushButton::popup() const +{ + return d ? (QPopupMenu*)d->popup : 0; +} + +void QPushButton::popupPressed() +{ + QPopupMenu* popup = d ? (QPopupMenu*) d->popup : 0; + QGuardedPtr<QPushButton> that = this; + if ( isDown() && popup ) { + bool horizontal = TRUE; + bool topLeft = TRUE; // ### always TRUE +#ifndef QT_NO_TOOLBAR + QToolBar *tb = ::qt_cast<QToolBar*>(parentWidget()); + if ( tb && tb->orientation() == Vertical ) + horizontal = FALSE; +#endif + if ( horizontal ) { + if ( topLeft ) { + if ( mapToGlobal( QPoint( 0, rect().bottom() ) ).y() + popup->sizeHint().height() <= qApp->desktop()->height() ) + popup->exec( mapToGlobal( rect().bottomLeft() ) ); + else + popup->exec( mapToGlobal( rect().topLeft() - QPoint( 0, popup->sizeHint().height() ) ) ); + } else { + QSize sz( popup->sizeHint() ); + QPoint p = mapToGlobal( rect().topLeft() ); + p.ry() -= sz.height(); + popup->exec( p ); + } + } else { + if ( topLeft ) { + if ( mapToGlobal( QPoint( rect().right(), 0 ) ).x() + popup->sizeHint().width() <= qApp->desktop()->width() ) + popup->exec( mapToGlobal( rect().topRight() ) ); + else + popup->exec( mapToGlobal( rect().topLeft() - QPoint( popup->sizeHint().width(), 0 ) ) ); + } else { + QSize sz( popup->sizeHint() ); + QPoint p = mapToGlobal( rect().topLeft() ); + p.rx() -= sz.width(); + popup->exec( p ); + } + } + if (that) + setDown( FALSE ); + } +} +#endif + +void QPushButton::setFlat( bool f ) +{ + flt = f; + update(); +} + +bool QPushButton::isFlat() const +{ + return flt; +} + +/*! + \obsolete + \fn virtual void QPushButton::setIsMenuButton( bool enable ) +*/ + +#endif diff --git a/src/widgets/qpushbutton.h b/src/widgets/qpushbutton.h new file mode 100644 index 0000000..ff1b98d --- /dev/null +++ b/src/widgets/qpushbutton.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Definition of QPushButton class +** +** Created : 940221 +** +** 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. +** +**********************************************************************/ + +#ifndef QPUSHBUTTON_H +#define QPUSHBUTTON_H + +#ifndef QT_H +#include "qbutton.h" +#include "qiconset.h" +#endif // QT_H + +#ifndef QT_NO_PUSHBUTTON +class QPushButtonPrivate; +class QPopupMenu; + +class Q_EXPORT QPushButton : public QButton +{ + Q_OBJECT + + Q_PROPERTY( bool autoDefault READ autoDefault WRITE setAutoDefault ) + Q_PROPERTY( bool default READ isDefault WRITE setDefault ) + Q_PROPERTY( bool menuButton READ isMenuButton DESIGNABLE false ) + Q_PROPERTY( QIconSet iconSet READ iconSet WRITE setIconSet ) + Q_OVERRIDE( bool toggleButton WRITE setToggleButton ) + Q_OVERRIDE( bool on WRITE setOn ) + Q_PROPERTY( bool flat READ isFlat WRITE setFlat ) + Q_OVERRIDE( bool autoMask DESIGNABLE true SCRIPTABLE true ) + +public: + QPushButton( QWidget *parent, const char* name=0 ); + QPushButton( const QString &text, QWidget *parent, const char* name=0 ); +#ifndef QT_NO_ICONSET + QPushButton( const QIconSet& icon, const QString &text, QWidget *parent, const char* name=0 ); +#endif + ~QPushButton(); + + QSize sizeHint() const; + + void move( int x, int y ); + void move( const QPoint &p ); + void resize( int w, int h ); + void resize( const QSize & ); + void setGeometry( int x, int y, int w, int h ); + + void setGeometry( const QRect & ); + + void setToggleButton( bool ); + + bool autoDefault() const { return autoDefButton; } + virtual void setAutoDefault( bool autoDef ); + bool isDefault() const { return defButton; } + virtual void setDefault( bool def ); + + virtual void setIsMenuButton( bool enable ) { // obsolete functions + if ( (bool)hasMenuArrow == enable ) + return; + hasMenuArrow = enable ? 1 : 0; + update(); + updateGeometry(); + } + bool isMenuButton() const { return hasMenuArrow; } + +#ifndef QT_NO_POPUPMENU + void setPopup( QPopupMenu* popup ); + QPopupMenu* popup() const; +#endif +#ifndef QT_NO_ICONSET + void setIconSet( const QIconSet& ); + QIconSet* iconSet() const; +#endif + void setFlat( bool ); + bool isFlat() const; + +public slots: + virtual void setOn( bool ); + +protected: + void drawButton( QPainter * ); + void drawButtonLabel( QPainter * ); + void focusInEvent( QFocusEvent * ); + void focusOutEvent( QFocusEvent * ); + void resizeEvent( QResizeEvent * ); + void updateMask(); +private slots: +#ifndef QT_NO_POPUPMENU + void popupPressed(); +#endif +private: + void init(); + + uint autoDefButton : 1; + uint defButton : 1; + uint flt : 1; + uint reserved : 1; // UNUSED + uint lastEnabled : 1; // UNUSED + uint hasMenuArrow : 1; + + QPushButtonPrivate* d; + + friend class QDialog; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QPushButton( const QPushButton & ); + QPushButton &operator=( const QPushButton & ); +#endif +}; + + +#endif // QT_NO_PUSHBUTTON + +#endif // QPUSHBUTTON_H diff --git a/src/widgets/qradiobutton.cpp b/src/widgets/qradiobutton.cpp new file mode 100644 index 0000000..13aa096 --- /dev/null +++ b/src/widgets/qradiobutton.cpp @@ -0,0 +1,358 @@ +/**************************************************************************** +** +** Implementation of QRadioButton class +** +** Created : 940222 +** +** 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 "qradiobutton.h" +#ifndef QT_NO_RADIOBUTTON +#include "qbuttongroup.h" +#include "qpainter.h" +#include "qdrawutil.h" +#include "qpixmap.h" +#include "qpixmapcache.h" +#include "qbitmap.h" +#include "qtextstream.h" +#include "qapplication.h" +#include "qstyle.h" + +/*! + \class QRadioButton qradiobutton.h + \brief The QRadioButton widget provides a radio button with a text or pixmap label. + + \ingroup basic + \mainclass + + QRadioButton and QCheckBox are both option buttons. That is, they + can be switched on (checked) or off (unchecked). The classes + differ in how the choices for the user are restricted. Check boxes + define "many of many" choices, whereas radio buttons provide a + "one of many" choice. In a group of radio buttons only one radio + button at a time can be checked; if the user selects another + button, the previously selected button is switched off. + + The easiest way to implement a "one of many" choice is simply to + put the radio buttons into QButtonGroup. + + Whenever a button is switched on or off it emits the signal + toggled(). Connect to this signal if you want to trigger an action + each time the button changes state. Otherwise, use isChecked() to + see if a particular button is selected. + + Just like QPushButton, a radio button can display text or a + pixmap. The text can be set in the constructor or with setText(); + the pixmap is set with setPixmap(). + + <img src=qradiobt-m.png> <img src=qradiobt-w.png> + + \important text, setText, text, pixmap, setPixmap, accel, setAccel, isToggleButton, setDown, isDown, isOn, state, autoRepeat, isExclusiveToggle, group, setAutoRepeat, toggle, pressed, released, clicked, toggled, state stateChanged + + \sa QPushButton QToolButton + \link guibooks.html#fowler GUI Design Handbook: Radio Button\endlink +*/ + +/*! + \property QRadioButton::checked \brief Whether the radio button is + checked + + This property will not effect any other radio buttons unless they + have been placed in the same QButtonGroup. The default value is + FALSE (unchecked). +*/ + +/*! + \property QRadioButton::autoMask \brief whether the radio button + is automatically masked + + \sa QWidget::setAutoMask() +*/ + +/*! + Constructs a radio button with no text. + + The \a parent and \a name arguments are sent on to the QWidget + constructor. +*/ + +QRadioButton::QRadioButton( QWidget *parent, const char *name ) + : QButton( parent, name, WNoAutoErase | WMouseNoMask ) +{ + init(); +} + +/*! + Constructs a radio button with the text \a text. + + The \a parent and \a name arguments are sent on to the QWidget + constructor. +*/ + +QRadioButton::QRadioButton( const QString &text, QWidget *parent, + const char *name ) + : QButton( parent, name, WNoAutoErase | WMouseNoMask ) +{ + init(); + setText( text ); +} + + +/* + Initializes the radio button. +*/ + +void QRadioButton::init() +{ + setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) ); + setToggleButton( TRUE ); +#ifndef QT_NO_BUTTONGROUP + QButtonGroup *bgrp = ::qt_cast<QButtonGroup*>(parentWidget()); + if ( bgrp ) + bgrp->setRadioButtonExclusive( TRUE ); +#endif +} + +void QRadioButton::setChecked( bool check ) +{ + setOn( check ); +} + + + + +/*! + \reimp +*/ +QSize QRadioButton::sizeHint() const +{ + // Any more complex, and we will use style().itemRect() + // NB: QCheckBox::sizeHint() is similar + constPolish(); + + QPainter p(this); + QSize sz = style().itemRect(&p, QRect(0, 0, 1, 1), ShowPrefix, FALSE, + pixmap(), text()).size(); + + return (style().sizeFromContents(QStyle::CT_RadioButton, this, sz). + expandedTo(QApplication::globalStrut())); +} + + +/*! + \reimp +*/ +bool QRadioButton::hitButton( const QPoint &pos ) const +{ + QRect r = + QStyle::visualRect( style().subRect( QStyle::SR_RadioButtonFocusRect, + this ), this ); + if ( qApp->reverseLayout() ) { + r.setRight( width() ); + } else { + r.setLeft( 0 ); + } + return r.contains( pos ); +} + + +/*! + \reimp +*/ +void QRadioButton::drawButton( QPainter *paint ) +{ + QPainter *p = paint; + QRect irect = QStyle::visualRect( style().subRect(QStyle::SR_RadioButtonIndicator, this), this ); + const QColorGroup &cg = colorGroup(); + +#if !defined( QT_NO_TEXTSTREAM ) && !defined( Q_WS_MACX ) +# define SAVE_RADIOBUTTON_PIXMAPS +#endif +#if defined(SAVE_RADIOBUTTON_PIXMAPS) + QString pmkey; // pixmap key + int kf = 0; + if ( isDown() ) + kf |= 1; + if ( isOn() ) + kf |= 2; + if ( isEnabled() ) + kf |= 4; + if( isActiveWindow() ) + kf |= 8; + if ( hasMouse() ) + kf |= 16; + if ( hasFocus() ) + kf |= 32; + + QTextOStream os(&pmkey); + os << "$qt_radio_" << style().className() << "_" + << palette().serialNumber() << "_" << irect.width() << "x" << irect.height() << "_" << kf; + QPixmap *pm = QPixmapCache::find( pmkey ); + if ( pm ) { // pixmap exists + drawButtonLabel( p ); + p->drawPixmap( irect.topLeft(), *pm ); + return; + } + bool use_pm = TRUE; + QPainter pmpaint; + int wx, wy; + if ( use_pm ) { + pm = new QPixmap( irect.size() ); // create new pixmap + Q_CHECK_PTR( pm ); + pm->fill(paletteBackgroundColor()); + QPainter::redirect(this, pm); + pmpaint.begin(this); + p = &pmpaint; // draw in pixmap + wx = irect.x(); // save x,y coords + wy = irect.y(); + irect.moveTopLeft(QPoint(0, 0)); + p->setBackgroundColor(paletteBackgroundColor()); + } +#endif + + QStyle::SFlags flags = QStyle::Style_Default; + if ( isEnabled() ) + flags |= QStyle::Style_Enabled; + if ( hasFocus() ) + flags |= QStyle::Style_HasFocus; + if ( isDown() ) + flags |= QStyle::Style_Down; + if ( hasMouse() ) + flags |= QStyle::Style_MouseOver; + if ( state() == QButton::On ) + flags |= QStyle::Style_On; + else if ( state() == QButton::Off ) + flags |= QStyle::Style_Off; + + style().drawControl(QStyle::CE_RadioButton, p, this, irect, cg, flags); + +#if defined(SAVE_RADIOBUTTON_PIXMAPS) + if ( use_pm ) { + pmpaint.end(); + QPainter::redirect(this, NULL); + if ( backgroundPixmap() || backgroundMode() == X11ParentRelative ) { + QBitmap bm( pm->size() ); + bm.fill( color0 ); + pmpaint.begin( &bm ); + style().drawControlMask(QStyle::CE_RadioButton, &pmpaint, this, irect); + pmpaint.end(); + pm->setMask( bm ); + } + p = paint; // draw in default device + p->drawPixmap( wx, wy, *pm ); + if (!QPixmapCache::insert(pmkey, pm) ) // save in cache + delete pm; + } +#endif + + drawButtonLabel( p ); +} + + + +/*! + \reimp +*/ +void QRadioButton::drawButtonLabel( QPainter *p ) +{ + QRect r = + QStyle::visualRect( style().subRect(QStyle::SR_RadioButtonContents, + this), this ); + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (hasFocus()) + flags |= QStyle::Style_HasFocus; + if (isDown()) + flags |= QStyle::Style_Down; + if (state() == QButton::On) + flags |= QStyle::Style_On; + else if (state() == QButton::Off) + flags |= QStyle::Style_Off; + + style().drawControl(QStyle::CE_RadioButtonLabel, p, this, r, colorGroup(), flags); +} + + +/*! + \reimp +*/ +void QRadioButton::resizeEvent( QResizeEvent* e ) +{ + QButton::resizeEvent(e); + if ( isVisible() ) { + QPainter p(this); + QSize isz = style().itemRect(&p, QRect(0, 0, 1, 1), ShowPrefix, FALSE, + pixmap(), text()).size(); + QSize wsz = (style().sizeFromContents(QStyle::CT_RadioButton, this, isz). + expandedTo(QApplication::globalStrut())); + + update(wsz.width(), isz.width(), 0, wsz.height()); + } + if (autoMask()) + updateMask(); +} + +/*! + \reimp +*/ +void QRadioButton::updateMask() +{ + QRect irect = + QStyle::visualRect( style().subRect( QStyle::SR_RadioButtonIndicator, + this ), this ); + + QBitmap bm(width(), height()); + bm.fill(color0); + + QPainter p( &bm, this ); + style().drawControlMask(QStyle::CE_RadioButton, &p, this, irect); + if ( ! text().isNull() || ( pixmap() && ! pixmap()->isNull() ) ) { + QRect crect = + QStyle::visualRect( style().subRect( QStyle::SR_RadioButtonContents, + this ), this ); + QRect frect = + QStyle::visualRect( style().subRect( QStyle::SR_RadioButtonFocusRect, + this ), this ); + QRect label(crect.unite(frect)); + p.fillRect(label, color1); + } + p.end(); + + setMask(bm); +} + +#endif diff --git a/src/widgets/qradiobutton.h b/src/widgets/qradiobutton.h new file mode 100644 index 0000000..2234a71 --- /dev/null +++ b/src/widgets/qradiobutton.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Definition of QRadioButton class +** +** Created : 940222 +** +** 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. +** +**********************************************************************/ + +#ifndef QRADIOBUTTON_H +#define QRADIOBUTTON_H + +#ifndef QT_H +#include "qbutton.h" +#endif // QT_H + +#ifndef QT_NO_RADIOBUTTON + +class Q_EXPORT QRadioButton : public QButton +{ + Q_OBJECT + Q_PROPERTY( bool checked READ isChecked WRITE setChecked ) + Q_OVERRIDE( bool autoMask DESIGNABLE true SCRIPTABLE true ) + +public: + QRadioButton( QWidget *parent, const char* name=0 ); + QRadioButton( const QString &text, QWidget *parent, const char* name=0 ); + + bool isChecked() const; + + QSize sizeHint() const; + +public slots: + virtual void setChecked( bool check ); + +protected: + bool hitButton( const QPoint & ) const; + void drawButton( QPainter * ); + void drawButtonLabel( QPainter * ); + void updateMask(); + + void resizeEvent( QResizeEvent* ); + +private: + void init(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QRadioButton( const QRadioButton & ); + QRadioButton &operator=( const QRadioButton & ); +#endif +}; + + +inline bool QRadioButton::isChecked() const +{ return isOn(); } + +#endif // QT_NO_RADIOBUTTON + +#endif // QRADIOBUTTON_H diff --git a/src/widgets/qrangecontrol.cpp b/src/widgets/qrangecontrol.cpp new file mode 100644 index 0000000..c329831 --- /dev/null +++ b/src/widgets/qrangecontrol.cpp @@ -0,0 +1,565 @@ +/**************************************************************************** +** +** Implementation of QRangeControl class +** +** Created : 940427 +** +** 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 "qrangecontrol.h" +#ifndef QT_NO_RANGECONTROL +#include "qglobal.h" +#include <limits.h> + +static bool sumOutOfRange(int current, int add); + + +/*! + \class QRangeControl qrangecontrol.h + \brief The QRangeControl class provides an integer value within a range. + + \ingroup misc + + Although originally designed for the QScrollBar widget, the + QRangeControl can also be used in conjunction with other widgets + such as QSlider and QSpinBox. Here are the five main concepts in + the class: + + \list 1 + + \i \e{Current value} The bounded integer that + QRangeControl maintains. value() returns it, and several + functions, including setValue(), set it. + + \i \e{Minimum} The lowest value that value() can ever + return. Returned by minValue() and set by setRange() or one of the + constructors. + + \i \e{Maximum} The highest value that value() can ever + return. Returned by maxValue() and set by setRange() or one of the + constructors. + + \i \e{Line step} The smaller of two natural steps that + QRangeControl provides and typically corresponds to the user + pressing an arrow key. The line step is returned by lineStep() + and set using setSteps(). The functions addLine() and + subtractLine() respectively increment and decrement the current + value by lineStep(). + + \i \e{Page step} The larger of two natural steps that + QRangeControl provides and typically corresponds to the user + pressing PageUp or PageDown. The page step is returned by + pageStep() and set using setSteps(). The functions addPage() and + substractPage() respectively increment and decrement the current + value by pageStep(). + + \endlist + + Unity (1) may be viewed as a third step size. setValue() lets you + set the current value to any integer in the allowed range, not + just minValue() + \e n * lineStep() for integer values of \e n. + Some widgets may allow the user to set any value at all; others + may just provide multiples of lineStep() or pageStep(). + + QRangeControl provides three virtual functions that are well + suited for updating the on-screen representation of range controls + and emitting signals: valueChange(), rangeChange() and + stepChange(). + + QRangeControl also provides a function called bound() which lets + you force arbitrary integers to be within the allowed range of the + range control. + + We recommend that all widgets that inherit QRangeControl provide + at least a signal called valueChanged(); many widgets will want to + provide addStep(), addPage(), substractStep() and substractPage() + as slots. + + Note that you must use multiple inheritance if you plan to + implement a widget using QRangeControl because QRangeControl is + not derived from QWidget. +*/ + + +/*! + Constructs a range control with a minimum value of 0, maximum + value of 99, line step of 1, page step of 10 and initial value 0. +*/ + +QRangeControl::QRangeControl() +{ + minVal = 0; + maxVal = 99; + line = 1; + page = 10; + val = 0; + prevVal = -1; + d = 0; +} + +/*! + Constructs a range control whose value can never be smaller than + \a minValue or greater than \a maxValue, whose line step size is + \a lineStep and page step size is \a pageStep and whose value is + initially \a value (which is guaranteed to be in range using + bound()). +*/ + +QRangeControl::QRangeControl( int minValue, int maxValue, + int lineStep, int pageStep, + int value ) +{ + minVal = minValue; + maxVal = maxValue; + line = QABS( lineStep ); + page = QABS( pageStep ); + prevVal = minVal - 1; + val = bound( value ); + d = 0; +} + +/*! + Destroys the range control +*/ + +QRangeControl::~QRangeControl() +{ +} + + +/*! + \fn int QRangeControl::value() const + + Returns the current range control value. This is guaranteed to be + within the range [minValue(), maxValue()]. + + \sa setValue() prevValue() +*/ + +/*! + \fn int QRangeControl::prevValue() const + + Returns the previous value of the range control. "Previous value" + means the value before the last change occurred. Setting a new + range may affect the value, too, because the value is forced to be + inside the specified range. When the range control is initially + created, this is the same as value(). + + prevValue() can be outside the current legal range if a call to + setRange() causes the current value to change. For example, if the + range was [0, 1000] and the current value is 500, setRange(0, 400) + makes value() return 400 and prevValue() return 500. + + \sa value() setRange() +*/ + +/*! + Sets the range control's value to \a value and forces it to be + within the legal range. + + Calls the virtual valueChange() function if the new value is + different from the previous value. The old value can still be + retrieved using prevValue(). + + \sa value() +*/ + +void QRangeControl::setValue( int value ) +{ + directSetValue( value ); + if ( prevVal != val ) + valueChange(); +} + +/*! + Sets the range control \a value directly without calling + valueChange(). + + Forces the new \a value to be within the legal range. + + You will rarely have to call this function. However, if you want + to change the range control's value inside the overloaded method + valueChange(), setValue() would call the function valueChange() + again. To avoid this recursion you must use directSetValue() + instead. + + \sa setValue() +*/ + +void QRangeControl::directSetValue(int value) +{ + prevVal = val; + val = bound( value ); +} + +/*! + Equivalent to \c{setValue( value() + pageStep() )}. + + If the value is changed, then valueChange() is called. + + \sa subtractPage() addLine() setValue() +*/ + +void QRangeControl::addPage() +{ + if (!sumOutOfRange(value(), pageStep())) + setValue(value() + pageStep()); +} + +/*! + Equivalent to \c{setValue( value() - pageStep() )}. + + If the value is changed, then valueChange() is called. + + \sa addPage() subtractLine() setValue() +*/ + +void QRangeControl::subtractPage() +{ + if (!sumOutOfRange(value(), -pageStep())) + setValue(value() - pageStep()); +} + +/*! + Equivalent to \c{setValue( value() + lineStep() )}. + + If the value is changed, then valueChange() is called. + + \sa subtractLine() addPage() setValue() +*/ + +void QRangeControl::addLine() +{ + if (!sumOutOfRange(value(), lineStep())) + setValue(value() + lineStep()); +} + +/*! + Equivalent to \c{setValue( value() - lineStep() )}. + + If the value is changed, then valueChange() is called. + + \sa addLine() subtractPage() setValue() +*/ + +void QRangeControl::subtractLine() +{ + if (!sumOutOfRange(value(), -lineStep())) + setValue(value() - lineStep()); +} + + +/*! + \fn int QRangeControl::minValue() const + + Returns the minimum value of the range. + + \sa setMinValue() setRange() maxValue() +*/ + +/*! + \fn int QRangeControl::maxValue() const + + Returns the maximum value of the range. + + \sa setMaxValue() setRange() minValue() +*/ + +/*! + Sets the minimum value of the range to \a minVal. + + If necessary, the maxValue() is adjusted so that the range remains + valid. + + \sa minValue() setMaxValue() +*/ +void QRangeControl::setMinValue( int minVal ) +{ + int maxVal = maxValue(); + if ( maxVal < minVal ) + maxVal = minVal; + setRange( minVal, maxVal ); +} + +/*! + Sets the minimum value of the range to \a maxVal. + + If necessary, the minValue() is adjusted so that the range remains + valid. + + \sa maxValue() setMinValue() +*/ +void QRangeControl::setMaxValue( int maxVal ) +{ + int minVal = minValue(); + if ( minVal > maxVal ) + minVal = maxVal; + setRange( minVal, maxVal ); +} + +/*! + Sets the range control's minimum value to \a minValue and its + maximum value to \a maxValue. + + Calls the virtual rangeChange() function if one or both of the new + minimum and maximum values are different from the previous + setting. Calls the virtual valueChange() function if the current + value is adjusted because it was outside the new range. + + If \a maxValue is smaller than \a minValue, \a minValue becomes + the only legal value. + + \sa minValue() maxValue() +*/ + +void QRangeControl::setRange( int minValue, int maxValue ) +{ + if ( minValue > maxValue ) { +#if defined(QT_CHECK_RANGE) + qWarning( "QRangeControl::setRange: minValue %d > maxValue %d", + minValue, maxValue ); +#endif + maxValue = minValue; + } + if ( minValue == minVal && maxValue == maxVal ) + return; + minVal = minValue; + maxVal = maxValue; + int tmp = bound( val ); + rangeChange(); + if ( tmp != val ) { + prevVal = val; + val = tmp; + valueChange(); + } +} + + +/*! + \fn int QRangeControl::lineStep() const + + Returns the line step. + + \sa setSteps() pageStep() +*/ + +/*! + \fn int QRangeControl::pageStep() const + + Returns the page step. + + \sa setSteps() lineStep() +*/ + +/*! + Sets the range's line step to \a lineStep and page step to \a + pageStep. + + Calls the virtual stepChange() function if the new line step + or page step are different from the previous settings. + + \sa lineStep() pageStep() setRange() +*/ + +void QRangeControl::setSteps( int lineStep, int pageStep ) +{ + if ( lineStep != line || pageStep != page ) { + line = QABS( lineStep ); + page = QABS( pageStep ); + stepChange(); + } +} + + +/*! + This virtual function is called whenever the range control value + changes. You can reimplement it if you want to be notified when + the value changes. The default implementation does nothing. + + Note that this method is called after the value has changed. The + previous value can be retrieved using prevValue(). + + \sa setValue(), addPage(), subtractPage(), addLine(), + subtractLine() rangeChange(), stepChange() +*/ + +void QRangeControl::valueChange() +{ +} + + +/*! + This virtual function is called whenever the range control's range + changes. You can reimplement it if you want to be notified when + the range changes. The default implementation does nothing. + + Note that this method is called after the range has changed. + + \sa setRange(), valueChange(), stepChange() +*/ + +void QRangeControl::rangeChange() +{ +} + + +/*! + This virtual function is called whenever the range control's + line or page step settings change. You can reimplement it if you + want to be notified when the step changes. The default + implementation does nothing. + + Note that this method is called after a step setting has changed. + + \sa setSteps(), rangeChange(), valueChange() +*/ + +void QRangeControl::stepChange() +{ +} + + +/*! + Forces the value \a v to be within the range from minValue() to + maxValue() inclusive, and returns the result. + + This function is provided so that you can easily force other + numbers than value() into the allowed range. You do not need to + call it in order to use QRangeControl itself. + + \sa setValue() value() minValue() maxValue() +*/ + +int QRangeControl::bound( int v ) const +{ + if ( v < minVal ) + return minVal; + if ( v > maxVal ) + return maxVal; + return v; +} + + +/*! + Converts \a logical_val to a pixel position. minValue() maps to 0, + maxValue() maps to \a span and other values are distributed evenly + in-between. + + This function can handle the entire integer range without + overflow, providing \a span is \<= 4096. + + Calling this method is useful when actually drawing a range + control such as a QScrollBar on-screen. + + \sa valueFromPosition() +*/ + +int QRangeControl::positionFromValue( int logical_val, int span ) const +{ + if ( span <= 0 || logical_val < minValue() || maxValue() <= minValue() ) + return 0; + if ( logical_val > maxValue() ) + return span; + + uint range = maxValue() - minValue(); + uint p = logical_val - minValue(); + + if ( range > (uint)INT_MAX/4096 ) { + const int scale = 4096*2; + return ( (p/scale) * span ) / (range/scale); + // ### the above line is probably not 100% correct + // ### but fixing it isn't worth the extreme pain... + } else if ( range > (uint)span ) { + return (2*p*span + range) / (2*range); + } else { + uint div = span / range; + uint mod = span % range; + return p*div + (2*p*mod + range) / (2*range); + } + //equiv. to (p*span)/range + 0.5 + // no overflow because of this implicit assumption: + // span <= 4096 +} + + +/*! + Converts the pixel position \a pos to a value. 0 maps to + minValue(), \a span maps to maxValue() and other values are + distributed evenly in-between. + + This function can handle the entire integer range without + overflow. + + Calling this method is useful if you actually implemented a range + control widget such as QScrollBar and want to handle mouse press + events. This function then maps screen coordinates to the logical + values. + + \sa positionFromValue() +*/ + +int QRangeControl::valueFromPosition( int pos, int span ) const +{ + if ( span <= 0 || pos <= 0 ) + return minValue(); + if ( pos >= span ) + return maxValue(); + + uint range = maxValue() - minValue(); + + if ( (uint)span > range ) + return minValue() + (2*pos*range + span) / (2*span); + else { + uint div = range / span; + uint mod = range % span; + return minValue() + pos*div + (2*pos*mod + span) / (2*span); + } + // equiv. to minValue() + (pos*range)/span + 0.5 + // no overflow because of this implicit assumption: + // pos <= span < sqrt(INT_MAX+0.0625)+0.25 ~ sqrt(INT_MAX) +} + +static bool sumOutOfRange(int current, int add) +{ + if (add > 0 && INT_MAX - add < current) { + return true; + } + if (add < 0 && INT_MIN - add > current) { + return true; + } + return false; +} + +#endif diff --git a/src/widgets/qrangecontrol.h b/src/widgets/qrangecontrol.h new file mode 100644 index 0000000..4efb37d --- /dev/null +++ b/src/widgets/qrangecontrol.h @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Definition of QRangeControl class +** +** Created : 940427 +** +** 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. +** +**********************************************************************/ + +#ifndef QRANGECONTROL_H +#define QRANGECONTROL_H + +#ifndef QT_H +#include "qglobal.h" +#include "qframe.h" +#endif // QT_H + +#ifndef QT_NO_RANGECONTROL + + +class QRangeControlPrivate; + + +class Q_EXPORT QRangeControl +{ +public: + QRangeControl(); + QRangeControl( int minValue, int maxValue, + int lineStep, int pageStep, int value ); + virtual ~QRangeControl(); + int value() const; + void setValue( int ); + void addPage(); + void subtractPage(); + void addLine(); + void subtractLine(); + + int minValue() const; + int maxValue() const; + void setRange( int minValue, int maxValue ); + void setMinValue( int minVal ); + void setMaxValue( int minVal ); + + int lineStep() const; + int pageStep() const; + void setSteps( int line, int page ); + + int bound( int ) const; + +protected: + int positionFromValue( int val, int space ) const; + int valueFromPosition( int pos, int space ) const; + void directSetValue( int val ); + int prevValue() const; + + virtual void valueChange(); + virtual void rangeChange(); + virtual void stepChange(); + +private: + int minVal, maxVal; + int line, page; + int val, prevVal; + + QRangeControlPrivate * d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QRangeControl( const QRangeControl & ); + QRangeControl &operator=( const QRangeControl & ); +#endif +}; + + +inline int QRangeControl::value() const +{ return val; } + +inline int QRangeControl::prevValue() const +{ return prevVal; } + +inline int QRangeControl::minValue() const +{ return minVal; } + +inline int QRangeControl::maxValue() const +{ return maxVal; } + +inline int QRangeControl::lineStep() const +{ return line; } + +inline int QRangeControl::pageStep() const +{ return page; } + + +#endif // QT_NO_RANGECONTROL + +#ifndef QT_NO_SPINWIDGET + +class QSpinWidgetPrivate; +class Q_EXPORT QSpinWidget : public QWidget +{ + Q_OBJECT +public: + QSpinWidget( QWidget* parent=0, const char* name=0 ); + ~QSpinWidget(); + + void setEditWidget( QWidget * widget ); + QWidget * editWidget(); + + QRect upRect() const; + QRect downRect() const; + + void setUpEnabled( bool on ); + void setDownEnabled( bool on ); + + bool isUpEnabled() const; + bool isDownEnabled() const; + + enum ButtonSymbols { UpDownArrows, PlusMinus }; + virtual void setButtonSymbols( ButtonSymbols bs ); + ButtonSymbols buttonSymbols() const; + + void arrange(); + +signals: + void stepUpPressed(); + void stepDownPressed(); + +public slots: + void stepUp(); + void stepDown(); + +protected: + void mousePressEvent( QMouseEvent *e ); + void resizeEvent( QResizeEvent* ev ); + void mouseReleaseEvent( QMouseEvent *e ); + void mouseMoveEvent( QMouseEvent *e ); +#ifndef QT_NO_WHEELEVENT + void wheelEvent( QWheelEvent * ); +#endif + void styleChange( QStyle& ); + void paintEvent( QPaintEvent * ); + void enableChanged( bool old ); + void windowActivationChange( bool ); + +private slots: + void timerDone(); + void timerDoneEx(); + +private: + QSpinWidgetPrivate * d; + + void updateDisplay(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QSpinWidget( const QSpinWidget& ); + QSpinWidget& operator=( const QSpinWidget& ); +#endif +}; + +#endif // QT_NO_SPINWIDGET + +#endif // QRANGECONTROL_H diff --git a/src/widgets/qscrollbar.cpp b/src/widgets/qscrollbar.cpp new file mode 100644 index 0000000..9a5e8f4 --- /dev/null +++ b/src/widgets/qscrollbar.cpp @@ -0,0 +1,1072 @@ +/**************************************************************************** +** +** Implementation of QScrollBar class +** +** Created : 940427 +** +** 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 "qscrollbar.h" +#ifndef QT_NO_SCROLLBAR +#include "qpainter.h" +#include "qbitmap.h" +#include "qapplication.h" +#include "qtimer.h" +#include "qstyle.h" +#ifndef QT_NO_CURSOR +#include <qcursor.h> +#endif +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif +#include <limits.h> + +/*! + \class QScrollBar + \brief The QScrollBar widget provides a vertical or horizontal scroll bar. + + \ingroup basic + + A scroll bar allows the user to control a value within a + program-definable range and gives users a visible indication of + the current value of a \link QRangeControl range control \endlink. + + Scroll bars include four separate controls: + + \list + + \i The \e line-up and \e line-down controls are little buttons + which the user can use to move one "line" up or down. The meaning + of line is configurable. In editors and list boxes it means one + line of text; in an image viewer it might mean 20 pixels. + + \i The \e slider is the handle that indicates the current value of + the scroll bar, which the user can drag to change the value. This + part of the scroll bar is sometimes called the "thumb". + + \i The \e page-up/page-down control is the area on which the + slider slides (the scroll bar's background). Clicking here moves + the scroll bar towards the click. The meaning of "page" is also + configurable: in editors and list boxes it means as many lines as + there is space for in the widget. + + \endlist + + QScrollBar has very few of its own functions; it mostly relies on + QRangeControl. The most useful functions are setValue() to set the + scroll bar directly to some value; addPage(), addLine(), + subtractPage(), and subtractLine() to simulate the effects of + clicking (useful for accelerator keys); setSteps() to define the + values of pageStep() and lineStep(); and setRange() to set the + minValue() and maxValue() of the scroll bar. QScrollBar has a + convenience constructor with which you can set most of these + properties. + + Some GUI styles (for example, the Windows and Motif styles + provided with Qt), also use the pageStep() value to calculate the + size of the slider. + + In addition to the access functions from QRangeControl, QScrollBar + provides a comprehensive set of signals: + \table + \header \i Signal \i Emitted when + \row \i \l valueChanged() + \i the scroll bar's value has changed. The tracking() + determines whether this signal is emitted during user + interaction. + \row \i \l sliderPressed() + \i the user starts to drag the slider. + \row \i \l sliderMoved() + \i the user drags the slider. + \row \i \l sliderReleased() + \i the user releases the slider. + \row \i \l nextLine() + \i the scroll bar has moved one line down or right. Line is + defined in QRangeControl. + \row \i \l prevLine() + \i the scroll bar has moved one line up or left. + \row \i \l nextPage() + \i the scroll bar has moved one page down or right. + \row \i \l prevPage() + \i the scroll bar has moved one page up or left. + \endtable + + QScrollBar only provides integer ranges. Note that although + QScrollBar handles very large numbers, scroll bars on current + screens cannot usefully control ranges above about 100,000 pixels. + Beyond that, it becomes difficult for the user to control the + scroll bar using either the keyboard or the mouse. + + A scroll bar can be controlled by the keyboard, but it has a + default focusPolicy() of \c NoFocus. Use setFocusPolicy() to + enable keyboard focus. See keyPressEvent() for a list of key + bindings. + + If you need to add scroll bars to an interface, consider using the + QScrollView class, which encapsulates the common uses for scroll + bars. + + <img src=qscrbar-m.png> <img src=qscrbar-w.png> + + \sa QSlider QSpinBox QScrollView + \link guibooks.html#fowler GUI Design Handbook: Scroll Bar\endlink +*/ + + +/*! + \fn void QScrollBar::valueChanged( int value ) + + This signal is emitted when the scroll bar value has changed, with + the new scroll bar \a value as an argument. +*/ + +/*! + \fn void QScrollBar::sliderPressed() + + This signal is emitted when the user presses the slider with the + mouse. +*/ + +/*! + \fn void QScrollBar::sliderMoved( int value ) + + This signal is emitted when the slider is dragged by the user, with + the new scroll bar \a value as an argument. + + This signal is emitted even when tracking is turned off. + + \sa tracking() valueChanged() nextLine() prevLine() nextPage() + prevPage() +*/ + +/*! + \fn void QScrollBar::sliderReleased() + + This signal is emitted when the user releases the slider with the + mouse. +*/ + +/*! + \fn void QScrollBar::nextLine() + + This signal is emitted when the scroll bar scrolls one line down + or right. +*/ + +/*! + \fn void QScrollBar::prevLine() + + This signal is emitted when the scroll bar scrolls one line up or + left. +*/ + +/*! + \fn void QScrollBar::nextPage() + + This signal is emitted when the scroll bar scrolls one page down + or right. +*/ + +/*! + \fn void QScrollBar::prevPage() + + This signal is emitted when the scroll bar scrolls one page up or + left. +*/ + + + +static const int thresholdTime = 500; +static const int repeatTime = 50; + +#define HORIZONTAL (orientation() == Horizontal) +#define VERTICAL !HORIZONTAL +#define MOTIF_BORDER 2 +#define SLIDER_MIN 9 + + +/*! + Constructs a vertical scroll bar. + + The \a parent and \a name arguments are sent on to the QWidget + constructor. + + The \c minValue defaults to 0, the \c maxValue to 99, with a \c + lineStep size of 1 and a \c pageStep size of 10, and an initial + \c value of 0. +*/ + +QScrollBar::QScrollBar( QWidget *parent, const char *name ) + : QWidget( parent, name ), orient( Vertical ) +{ + init(); +} + +/*! + Constructs a scroll bar. + + The \a orientation must be \c Qt::Vertical or \c Qt::Horizontal. + + The \a parent and \a name arguments are sent on to the QWidget + constructor. + + The \c minValue defaults to 0, the \c maxValue to 99, with a \c + lineStep size of 1 and a \c pageStep size of 10, and an initial + \c value of 0. +*/ + +QScrollBar::QScrollBar( Orientation orientation, QWidget *parent, + const char *name ) + : QWidget( parent, name ), orient( orientation ) +{ + init(); +} + +/*! + Constructs a scroll bar whose value can never be smaller than \a + minValue or greater than \a maxValue, whose line step size is \a + lineStep and page step size is \a pageStep and whose value is + initially \a value (which is guaranteed to be in range using + bound()). + + If \a orientation is \c Vertical the scroll bar is vertical and if + it is \c Horizontal the scroll bar is horizontal. + + The \a parent and \a name arguments are sent on to the QWidget + constructor. +*/ + +QScrollBar::QScrollBar( int minValue, int maxValue, int lineStep, int pageStep, + int value, Orientation orientation, + QWidget *parent, const char *name ) + : QWidget( parent, name ), + QRangeControl( minValue, maxValue, lineStep, pageStep, value ), + orient( orientation ) +{ + init(); +} + +/*! + Destructor. +*/ +QScrollBar::~QScrollBar() +{ +} + +void QScrollBar::init() +{ + track = TRUE; + sliderPos = 0; + pressedControl = QStyle::SC_None; + clickedAt = FALSE; + setFocusPolicy( NoFocus ); + + repeater = 0; + + setBackgroundMode((Qt::BackgroundMode) + style().styleHint(QStyle::SH_ScrollBar_BackgroundMode)); + + QSizePolicy sp( QSizePolicy::Minimum, QSizePolicy::Fixed ); + if ( orient == Vertical ) + sp.transpose(); + setSizePolicy( sp ); + clearWState( WState_OwnSizePolicy ); +} + + +/*! + \property QScrollBar::orientation + \brief the orientation of the scroll bar + + The orientation must be \l Qt::Vertical (the default) or \l + Qt::Horizontal. +*/ + +void QScrollBar::setOrientation( Orientation orientation ) +{ + if ( orientation == orient ) + return; + if ( !testWState( WState_OwnSizePolicy ) ) { + QSizePolicy sp = sizePolicy(); + sp.transpose(); + setSizePolicy( sp ); + clearWState( WState_OwnSizePolicy ); + } + + orient = orientation; + + positionSliderFromValue(); + update(); + updateGeometry(); +} + +/*! + \property QScrollBar::tracking + \brief whether scroll bar tracking is enabled + + If tracking is enabled (the default), the scroll bar emits the + valueChanged() signal while the slider is being dragged. If + tracking is disabled, the scroll bar emits the valueChanged() + signal only when the user releases the mouse button after moving + the slider. +*/ + + +/*! + \property QScrollBar::draggingSlider + \brief whether the user has clicked the mouse on the slider and is currently dragging it +*/ + +bool QScrollBar::draggingSlider() const +{ + return pressedControl == QStyle::SC_ScrollBarSlider; +} + + +/*! + Reimplements the virtual function QWidget::setPalette(). + + Sets the background color to the mid color for Motif style scroll + bars using palette \a p. +*/ + +void QScrollBar::setPalette( const QPalette &p ) +{ + QWidget::setPalette( p ); + setBackgroundMode((Qt::BackgroundMode) + style().styleHint(QStyle::SH_ScrollBar_BackgroundMode)); +} + + +/*! \reimp */ +QSize QScrollBar::sizeHint() const +{ + constPolish(); + int sbextent = style().pixelMetric(QStyle::PM_ScrollBarExtent, this); + + if ( orient == Horizontal ) { + return QSize( 30, sbextent ); + } else { + return QSize( sbextent, 30 ); + } +} + +/*! \fn void QScrollBar::setSizePolicy( QSizePolicy::SizeType, QSizePolicy::SizeType, bool ) + \reimp +*/ + +/*! \reimp */ +void QScrollBar::setSizePolicy( QSizePolicy sp ) +{ + //## remove 4.0 + QWidget::setSizePolicy( sp ); +} + +/*! + \internal + Implements the virtual QRangeControl function. +*/ + +void QScrollBar::valueChange() +{ + int tmp = sliderPos; + positionSliderFromValue(); + if ( tmp != sliderPos && isVisible() ) + drawControls(QStyle::SC_ScrollBarAddPage | + QStyle::SC_ScrollBarSubPage | + QStyle::SC_ScrollBarSlider, + pressedControl ); + emit valueChanged(value()); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged ); +#endif +} + +/*! + \internal + Implements the virtual QRangeControl function. +*/ + +void QScrollBar::stepChange() +{ + rangeChange(); +} + +/*! + \internal + Implements the virtual QRangeControl function. +*/ + +void QScrollBar::rangeChange() +{ + positionSliderFromValue(); + + if ( isVisible() ) + drawControls(QStyle::SC_ScrollBarAddLine | + QStyle::SC_ScrollBarSubLine | + QStyle::SC_ScrollBarAddPage | + QStyle::SC_ScrollBarSubPage | + QStyle::SC_ScrollBarFirst | + QStyle::SC_ScrollBarLast | + QStyle::SC_ScrollBarSlider, + pressedControl ); +} + + +/*! + Handles timer events for the scroll bar. +*/ + +void QScrollBar::doAutoRepeat() +{ + bool sendRepeat = clickedAt; +#if !defined( QT_NO_CURSOR ) && !defined( QT_NO_STYLE ) + if(sendRepeat && (pressedControl == QStyle::SC_ScrollBarAddPage || + pressedControl == QStyle::SC_ScrollBarSubPage) && + style().styleHint(QStyle::SH_ScrollBar_StopMouseOverSlider, this) && + style().querySubControl(QStyle::CC_ScrollBar, this, + mapFromGlobal(QCursor::pos()) ) == QStyle::SC_ScrollBarSlider) + sendRepeat = FALSE; +#endif + if ( sendRepeat ){ + if ( repeater ) + repeater->changeInterval( repeatTime ); + action( (QStyle::SubControl) pressedControl ); + QApplication::syncX(); + } else { + stopAutoRepeat(); + } +} + + +/*! + Starts the auto-repeat logic. Some time after this function is + called, the auto-repeat starts taking effect and from then on + repeats until stopAutoRepeat() is called. +*/ + +void QScrollBar::startAutoRepeat() +{ + if ( !repeater ) { + repeater = new QTimer( this, "auto-repeat timer" ); + connect( repeater, SIGNAL(timeout()), + this, SLOT(doAutoRepeat()) ); + } + repeater->start( thresholdTime, FALSE ); +} + + +/*! + Stops the auto-repeat logic. +*/ + +void QScrollBar::stopAutoRepeat() +{ + delete repeater; + repeater = 0; +} + + +/*! + \reimp +*/ +#ifndef QT_NO_WHEELEVENT +void QScrollBar::wheelEvent( QWheelEvent *e ) +{ + static float offset = 0; + static QScrollBar* offset_owner = 0; + if (offset_owner != this){ + offset_owner = this; + offset = 0; + } + if ( e->orientation() != orient && !rect().contains(e->pos()) ) + return; + e->accept(); + int step = QMIN( QApplication::wheelScrollLines()*lineStep(), + pageStep() ); + if ( ( e->state() & ControlButton ) || ( e->state() & ShiftButton ) ) + step = pageStep(); + offset += -e->delta()*step/120; + if (QABS(offset)<1) + return; + setValue( value() + int(offset) ); + offset -= int(offset); +} +#endif + +/*! + \reimp +*/ +void QScrollBar::keyPressEvent( QKeyEvent *e ) +{ + // \list + // \i Left/Right move a horizontal scrollbar by one line. + // \i Up/Down move a vertical scrollbar by one line. + // \i PageUp moves up one page. + // \i PageDown moves down one page. + // \i Home moves to the start (minValue()). + // \i End moves to the end (maxValue()). + // \endlist + + // Note that unless you call setFocusPolicy(), the default NoFocus + // will apply and the user will not be able to use the keyboard to + // interact with the scrollbar. + switch ( e->key() ) { + case Key_Left: + if ( orient == Horizontal ) + subtractLine(); + break; + case Key_Right: + if ( orient == Horizontal ) + addLine(); + break; + case Key_Up: + if ( orient == Vertical ) + subtractLine(); + break; + case Key_Down: + if ( orient == Vertical ) + addLine(); + break; + case Key_PageUp: + subtractPage(); + break; + case Key_PageDown: + addPage(); + break; + case Key_Home: + setValue( minValue() ); + break; + case Key_End: + setValue( maxValue() ); + break; + default: + e->ignore(); + break; + } +} + + +/*! + \reimp +*/ +void QScrollBar::resizeEvent( QResizeEvent * ) +{ + positionSliderFromValue(); +} + + +/*! + \reimp +*/ +void QScrollBar::paintEvent( QPaintEvent * ) +{ + QPainter p( this ); + drawControls(QStyle::SC_ScrollBarAddLine | + QStyle::SC_ScrollBarSubLine | + QStyle::SC_ScrollBarAddPage | + QStyle::SC_ScrollBarSubPage | + QStyle::SC_ScrollBarFirst | + QStyle::SC_ScrollBarLast | + QStyle::SC_ScrollBarSlider, + pressedControl, &p ); +} + +static QCOORD sliderStartPos = 0; + +/*! + \reimp + */ +void QScrollBar::contextMenuEvent( QContextMenuEvent *e ) +{ + if(clickedAt) + e->consume(); + else + e->ignore(); +} + +/*! + \reimp +*/ +void QScrollBar::mousePressEvent( QMouseEvent *e ) +{ + bool midButtonAbsPos = + style().styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition, + this); + + if ( !(e->button() == LeftButton || + (midButtonAbsPos && e->button() == MidButton) ) ) + return; + + if ( maxValue() == minValue() ) // nothing to be done + return; + + if ( e->state() & MouseButtonMask ) // another button was already pressed + return; + + clickedAt = TRUE; + pressedControl = style().querySubControl(QStyle::CC_ScrollBar, this, e->pos() ); + + if ( (pressedControl == QStyle::SC_ScrollBarAddPage || + pressedControl == QStyle::SC_ScrollBarSubPage || + pressedControl == QStyle::SC_ScrollBarSlider ) && + ((midButtonAbsPos && e->button() == MidButton) || + style().styleHint(QStyle::SH_ScrollBar_LeftClickAbsolutePosition) && e->button() == LeftButton)) { + + QRect sr = style().querySubControlMetrics(QStyle::CC_ScrollBar, this, + QStyle::SC_ScrollBarSlider ), + gr = style().querySubControlMetrics(QStyle::CC_ScrollBar, this, + QStyle::SC_ScrollBarGroove ); + int sliderMin, sliderMax, sliderLength; + sliderMin = sliderMax = sliderLength = 0; + if (HORIZONTAL) { + sliderMin = gr.x(); + sliderMax = sliderMin + gr.width(); + sliderLength = sr.width(); + } else { + sliderMin = gr.y(); + sliderMax = sliderMin + gr.height(); + sliderLength = sr.height(); + } + + int newSliderPos = (HORIZONTAL ? e->pos().x() : e->pos().y()) + - sliderLength/2; + newSliderPos = QMIN( newSliderPos, sliderMax - sliderLength ); + newSliderPos = QMAX( newSliderPos, sliderMin ); + setValue( sliderPosToRangeValue(newSliderPos) ); + sliderPos = newSliderPos; + pressedControl = QStyle::SC_ScrollBarSlider; + } + + if ( pressedControl == QStyle::SC_ScrollBarSlider ) { + clickOffset = (QCOORD)( (HORIZONTAL ? e->pos().x() : e->pos().y()) + - sliderPos ); + slidePrevVal = value(); + sliderStartPos = sliderPos; + drawControls( pressedControl, pressedControl ); + emit sliderPressed(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ScrollingStart ); +#endif + } else if ( pressedControl != QStyle::SC_None ) { + drawControls( pressedControl, pressedControl ); + action( (QStyle::SubControl) pressedControl ); + startAutoRepeat(); + } +} + + +/*! + \reimp +*/ +void QScrollBar::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( !clickedAt ) + return; + + if ( e->stateAfter() & MouseButtonMask ) // some other button is still pressed + return; + + QStyle::SubControl tmp = (QStyle::SubControl) pressedControl; + clickedAt = FALSE; + stopAutoRepeat(); + mouseMoveEvent( e ); // Might have moved since last mouse move event. + pressedControl = QStyle::SC_None; + + if (tmp == QStyle::SC_ScrollBarSlider) { + directSetValue( calculateValueFromSlider() ); + emit sliderReleased(); + if ( value() != prevValue() ) { + emit valueChanged( value() ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged ); +#endif + } +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ScrollingEnd ); +#endif + } + drawControls( tmp, pressedControl ); + if ( e->button() == MidButton ) + repaint( FALSE ); +} + + +/*! + \reimp +*/ +void QScrollBar::mouseMoveEvent( QMouseEvent *e ) +{ + if ( !isVisible() ) { + clickedAt = FALSE; + return; + } + + bool mcab = style().styleHint(QStyle::SH_ScrollBar_MiddleClickAbsolutePosition, + this); + if ( ! clickedAt || ! (e->state() & LeftButton || + ((e->state() & MidButton) && mcab))) + return; + + int newSliderPos; + if ( pressedControl == QStyle::SC_ScrollBarSlider ) { + QRect gr = style().querySubControlMetrics(QStyle::CC_ScrollBar, this, + QStyle::SC_ScrollBarGroove ), + sr = style().querySubControlMetrics(QStyle::CC_ScrollBar, this, + QStyle::SC_ScrollBarSlider ); + int sliderMin, sliderMax, sliderLength; + + if (HORIZONTAL) { + sliderLength = sr.width(); + sliderMin = gr.x(); + sliderMax = gr.right() - sliderLength + 1; + } else { + sliderLength = sr.height(); + sliderMin = gr.y(); + sliderMax = gr.bottom() - sliderLength + 1; + } + + QRect r = rect(); + int m = style().pixelMetric(QStyle::PM_MaximumDragDistance, this); + if ( m >= 0 ) { + if ( orientation() == Horizontal ) + r.setRect( r.x() - m, r.y() - 2*m, r.width() + 2*m, r.height() + 4*m ); + else + r.setRect( r.x() - 2*m, r.y() - m, r.width() + 4*m, r.height() + 2*m ); + if (! r.contains( e->pos())) + newSliderPos = sliderStartPos; + else + newSliderPos = (HORIZONTAL ? e->pos().x() : + e->pos().y()) -clickOffset; + } else + newSliderPos = (HORIZONTAL ? e->pos().x() : + e->pos().y()) -clickOffset; + + if ( newSliderPos < sliderMin ) + newSliderPos = sliderMin; + else if ( newSliderPos > sliderMax ) + newSliderPos = sliderMax; + int newVal = sliderPosToRangeValue(newSliderPos); + if ( newVal != slidePrevVal ) + emit sliderMoved( newVal ); + if ( track && newVal != value() ) { + directSetValue( newVal ); // Set directly, painting done below + emit valueChanged( value() ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged ); +#endif + } + slidePrevVal = newVal; + sliderPos = (QCOORD)newSliderPos; + drawControls( QStyle::SC_ScrollBarAddPage | + QStyle::SC_ScrollBarSlider | + QStyle::SC_ScrollBarSubPage, + pressedControl ); + } else if (! style().styleHint(QStyle::SH_ScrollBar_ScrollWhenPointerLeavesControl)) { + // stop scrolling when the mouse pointer leaves a control + // similar to push buttons + if ( pressedControl != (uint)style().querySubControl(QStyle::CC_ScrollBar, this, e->pos() ) ) { + drawControls( pressedControl, QStyle::SC_None ); + stopAutoRepeat(); + } else if ( !repeater ) { + drawControls( pressedControl, pressedControl ); + action( (QStyle::SubControl) pressedControl ); + startAutoRepeat(); + } + } +} + + +/*! + \fn int QScrollBar::sliderStart() const + + Returns the pixel position where the scroll bar slider starts. + + This is equivalent to sliderRect().y() for vertical scroll bars or + sliderRect().x() for horizontal scroll bars. +*/ + +/*! + Returns the scroll bar slider rectangle. + + \sa sliderStart() +*/ + +QRect QScrollBar::sliderRect() const +{ + return style().querySubControlMetrics(QStyle::CC_ScrollBar, this, + QStyle::SC_ScrollBarSlider ); +} + +void QScrollBar::positionSliderFromValue() +{ + sliderPos = (QCOORD)rangeValueToSliderPos( value() ); +} + +int QScrollBar::calculateValueFromSlider() const +{ + return sliderPosToRangeValue( sliderPos ); +} + +int QScrollBar::rangeValueToSliderPos( int v ) const +{ + QRect gr = style().querySubControlMetrics(QStyle::CC_ScrollBar, this, + QStyle::SC_ScrollBarGroove ); + QRect sr = style().querySubControlMetrics(QStyle::CC_ScrollBar, this, + QStyle::SC_ScrollBarSlider ); + int sliderMin, sliderMax, sliderLength; + + if (HORIZONTAL) { + sliderLength = sr.width(); + sliderMin = gr.x(); + sliderMax = gr.right() - sliderLength + 1; + } else { + sliderLength = sr.height(); + sliderMin = gr.y(); + sliderMax = gr.bottom() - sliderLength + 1; + } + + return positionFromValue( v, sliderMax-sliderMin ) + sliderMin; +} + +int QScrollBar::sliderPosToRangeValue( int pos ) const +{ + QRect gr = style().querySubControlMetrics(QStyle::CC_ScrollBar, this, + QStyle::SC_ScrollBarGroove ); + QRect sr = style().querySubControlMetrics(QStyle::CC_ScrollBar, this, + QStyle::SC_ScrollBarSlider ); + int sliderMin, sliderMax, sliderLength; + + if (HORIZONTAL) { + sliderLength = sr.width(); + sliderMin = gr.x(); + sliderMax = gr.right() - sliderLength + 1; + } else { + sliderLength = sr.height(); + sliderMin = gr.y(); + sliderMax = gr.bottom() - sliderLength + 1; + } + + return valueFromPosition( pos - sliderMin, sliderMax - sliderMin ); +} + + +void QScrollBar::action( int control ) +{ + switch( control ) { + case QStyle::SC_ScrollBarAddLine: + addLine(); + emit nextLine(); + break; + case QStyle::SC_ScrollBarSubLine: + subtractLine(); + emit prevLine(); + break; + case QStyle::SC_ScrollBarAddPage: + addPage(); + emit nextPage(); + break; + case QStyle::SC_ScrollBarSubPage: + subtractPage(); + emit prevPage(); + break; + case QStyle::SC_ScrollBarFirst: + setValue( minValue() ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged ); +#endif + emit valueChanged( minValue() ); + break; + case QStyle::SC_ScrollBarLast: + setValue( maxValue() ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged ); +#endif + emit valueChanged( maxValue() ); + break; + default: + break; + } +} + + +void QScrollBar::drawControls( uint controls, uint activeControl ) const +{ + QPainter p ( this ); + drawControls( controls, activeControl, &p ); +} + + +void QScrollBar::drawControls( uint controls, uint activeControl, + QPainter *p ) const +{ + if ( !isUpdatesEnabled() ) + return; + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (hasFocus()) + flags |= QStyle::Style_HasFocus; + if ( orientation() == Horizontal ) + flags |= QStyle::Style_Horizontal; + + style().drawComplexControl(QStyle::CC_ScrollBar, p, this, rect(), colorGroup(), + flags, (QStyle::SubControl) controls, + (QStyle::SubControl) activeControl ); +} + +/*! + \reimp +*/ +void QScrollBar::styleChange( QStyle& old ) +{ + positionSliderFromValue(); + setBackgroundMode((Qt::BackgroundMode) + style().styleHint(QStyle::SH_ScrollBar_BackgroundMode)); + QWidget::styleChange( old ); +} + +/*! + \property QScrollBar::minValue + \brief the scroll bar's minimum value + + When setting this property, the \l QScrollBar::maxValue is + adjusted if necessary to ensure that the range remains valid. + + \sa setRange() +*/ +int QScrollBar::minValue() const +{ + return QRangeControl::minValue(); +} + +void QScrollBar::setMinValue( int minVal ) +{ + QRangeControl::setMinValue( minVal ); +} + +/*! + \property QScrollBar::maxValue + \brief the scroll bar's maximum value + + When setting this property, the \l QScrollBar::minValue is + adjusted if necessary to ensure that the range remains valid. + + \sa setRange() +*/ +int QScrollBar::maxValue() const +{ + return QRangeControl::maxValue(); +} + +void QScrollBar::setMaxValue( int maxVal ) +{ + QRangeControl::setMaxValue( maxVal ); +} + +/*! + \property QScrollBar::lineStep + \brief the line step + + When setting lineStep, the virtual stepChange() function will be + called if the new line step is different from the previous + setting. + + \sa setSteps() QRangeControl::pageStep() setRange() +*/ + +int QScrollBar::lineStep() const +{ + return QRangeControl::lineStep(); +} + +/*! + \property QScrollBar::pageStep + \brief the page step + + When setting pageStep, the virtual stepChange() function will be + called if the new page step is different from the previous + setting. + + \sa QRangeControl::setSteps() setLineStep() setRange() +*/ + +int QScrollBar::pageStep() const +{ + return QRangeControl::pageStep(); +} + +void QScrollBar::setLineStep( int i ) +{ + setSteps( i, pageStep() ); +} + +void QScrollBar::setPageStep( int i ) +{ + setSteps( lineStep(), i ); +} + +/*! + \property QScrollBar::value + \brief the scroll bar's value + + \sa QRangeControl::value() prevValue() +*/ + +int QScrollBar::value() const +{ + return QRangeControl::value(); +} + +void QScrollBar::setValue( int i ) +{ + QRangeControl::setValue( i ); +} + + +/*! + This function is called when the scrollbar is hidden. +*/ +void QScrollBar::hideEvent( QHideEvent* ) +{ + pressedControl = QStyle::SC_None; + clickedAt = FALSE; +} + + +#undef ADD_LINE_ACTIVE +#undef SUB_LINE_ACTIVE +#endif diff --git a/src/widgets/qscrollbar.h b/src/widgets/qscrollbar.h new file mode 100644 index 0000000..c8960f4 --- /dev/null +++ b/src/widgets/qscrollbar.h @@ -0,0 +1,197 @@ +/**************************************************************************** +** +** Definition of QScrollBar class +** +** Created : 940427 +** +** 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. +** +**********************************************************************/ + +#ifndef QSCROLLBAR_H +#define QSCROLLBAR_H + +class QTimer; + +#ifndef QT_H +#include "qwidget.h" +#include "qrangecontrol.h" +#endif // QT_H + +#ifndef QT_NO_SCROLLBAR + +class Q_EXPORT QScrollBar : public QWidget, public QRangeControl +{ + Q_OBJECT + Q_PROPERTY( int minValue READ minValue WRITE setMinValue ) + Q_PROPERTY( int maxValue READ maxValue WRITE setMaxValue ) + Q_PROPERTY( int lineStep READ lineStep WRITE setLineStep ) + Q_PROPERTY( int pageStep READ pageStep WRITE setPageStep ) + Q_PROPERTY( int value READ value WRITE setValue ) + Q_PROPERTY( bool tracking READ tracking WRITE setTracking ) + Q_PROPERTY( bool draggingSlider READ draggingSlider ) + Q_PROPERTY( Orientation orientation READ orientation WRITE setOrientation ) + +public: + QScrollBar( QWidget *parent, const char* name = 0 ); + QScrollBar( Orientation, QWidget *parent, const char* name = 0 ); + QScrollBar( int minValue, int maxValue, int lineStep, int pageStep, + int value, Orientation, QWidget *parent, const char* name = 0 ); + ~QScrollBar(); + + virtual void setOrientation( Orientation ); + Orientation orientation() const; + virtual void setTracking( bool enable ); + bool tracking() const; + bool draggingSlider() const; + + virtual void setPalette( const QPalette & ); + virtual QSize sizeHint() const; + virtual void setSizePolicy( QSizePolicy sp ); + void setSizePolicy( QSizePolicy::SizeType hor, QSizePolicy::SizeType ver, bool hfw = FALSE ); + + int minValue() const; + int maxValue() const; + void setMinValue( int ); + void setMaxValue( int ); + int lineStep() const; + int pageStep() const; + void setLineStep( int ); + void setPageStep( int ); + int value() const; + + int sliderStart() const; + QRect sliderRect() const; + +public slots: + void setValue( int ); + +signals: + void valueChanged( int value ); + void sliderPressed(); + void sliderMoved( int value ); + void sliderReleased(); + void nextLine(); + void prevLine(); + void nextPage(); + void prevPage(); + +protected: +#ifndef QT_NO_WHEELEVENT + void wheelEvent( QWheelEvent * ); +#endif + void keyPressEvent( QKeyEvent * ); + void resizeEvent( QResizeEvent * ); + void paintEvent( QPaintEvent * ); + + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void contextMenuEvent( QContextMenuEvent * ); + void hideEvent( QHideEvent* ); + + void valueChange(); + void stepChange(); + void rangeChange(); + + void styleChange( QStyle& ); + +private slots: + void doAutoRepeat(); + +private: + void init(); + void positionSliderFromValue(); + int calculateValueFromSlider() const; + + void startAutoRepeat(); + void stopAutoRepeat(); + + int rangeValueToSliderPos( int val ) const; + int sliderPosToRangeValue( int val ) const; + + void action( int control ); + + void drawControls( uint controls, uint activeControl ) const; + void drawControls( uint controls, uint activeControl, + QPainter *p ) const; + + uint pressedControl; + bool track; + bool clickedAt; + Orientation orient; + + int slidePrevVal; + QCOORD sliderPos; + QCOORD clickOffset; + + QTimer * repeater; + void * d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QScrollBar( const QScrollBar & ); + QScrollBar &operator=( const QScrollBar & ); +#endif +}; + + +inline void QScrollBar::setTracking( bool t ) +{ + track = t; +} + +inline bool QScrollBar::tracking() const +{ + return track; +} + +inline QScrollBar::Orientation QScrollBar::orientation() const +{ + return orient; +} + +inline int QScrollBar::sliderStart() const +{ + return sliderPos; +} + +inline void QScrollBar::setSizePolicy( QSizePolicy::SizeType hor, QSizePolicy::SizeType ver, bool hfw ) +{ + QWidget::setSizePolicy( hor, ver, hfw ); +} + + +#endif // QT_NO_SCROLLBAR + +#endif // QSCROLLBAR_H diff --git a/src/widgets/qscrollview.cpp b/src/widgets/qscrollview.cpp new file mode 100644 index 0000000..6af8f12 --- /dev/null +++ b/src/widgets/qscrollview.cpp @@ -0,0 +1,2854 @@ +/**************************************************************************** +** +** Implementation of QScrollView class +** +** Created : 950524 +** +** 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 "qwidget.h" +#ifndef QT_NO_SCROLLVIEW +#include "qscrollbar.h" +#include "qobjectlist.h" +#include "qpainter.h" +#include "qpixmap.h" +#include "qcursor.h" +#include "qfocusdata.h" +#include "qscrollview.h" +#include "qptrdict.h" +#include "qapplication.h" +#include "qtimer.h" +#include "qstyle.h" +#include "qlistview.h" +#ifdef Q_WS_MAC +# include "qt_mac.h" +#endif + +static const int coord_limit = 4000; +static const int autoscroll_margin = 16; +static const int initialScrollTime = 30; +static const int initialScrollAccel = 5; + +struct QSVChildRec { + QSVChildRec(QWidget* c, int xx, int yy) : + child(c), + x(xx), y(yy) + { + } + + void hideOrShow(QScrollView* sv, QWidget* clipped_viewport); + void moveTo(QScrollView* sv, int xx, int yy, QWidget* clipped_viewport) + { + if ( x != xx || y != yy ) { + x = xx; + y = yy; + hideOrShow(sv,clipped_viewport); + } + } + QWidget* child; + int x, y; +}; + +void QSVChildRec::hideOrShow(QScrollView* sv, QWidget* clipped_viewport) +{ + if ( clipped_viewport ) { + if ( x+child->width() < sv->contentsX()+clipped_viewport->x() + || x > sv->contentsX()+clipped_viewport->width() + || y+child->height() < sv->contentsY()+clipped_viewport->y() + || y > sv->contentsY()+clipped_viewport->height() ) { + child->move(clipped_viewport->width(), + clipped_viewport->height()); + } else { + child->move(x-sv->contentsX()-clipped_viewport->x(), + y-sv->contentsY()-clipped_viewport->y()); + } + } else { + child->move(x-sv->contentsX(), y-sv->contentsY()); + } +} + +class QViewportWidget : public QWidget +{ + Q_OBJECT + +public: + QViewportWidget( QScrollView* parent=0, const char* name=0, WFlags f = 0 ) + : QWidget( parent, name, f ) {} +}; + +class QClipperWidget : public QWidget +{ + Q_OBJECT + +public: + QClipperWidget( QWidget * parent=0, const char * name=0, WFlags f=0 ) + : QWidget ( parent,name,f) {} +}; + +#include "qscrollview.moc" + +class QScrollViewData { +public: + QScrollViewData(QScrollView* parent, int vpwflags) : + hbar( new QScrollBar( QScrollBar::Horizontal, parent, "qt_hbar" ) ), + vbar( new QScrollBar( QScrollBar::Vertical, parent, "qt_vbar" ) ), + viewport( new QViewportWidget( parent, "qt_viewport", vpwflags ) ), + clipped_viewport( 0 ), + flags( vpwflags ), + vx( 0 ), vy( 0 ), vwidth( 1 ), vheight( 1 ), +#ifndef QT_NO_DRAGANDDROP + autoscroll_timer( parent, "scrollview autoscroll timer" ), + drag_autoscroll( TRUE ), +#endif + scrollbar_timer( parent, "scrollview scrollbar timer" ), + inresize( FALSE ), use_cached_size_hint( TRUE ) + { + l_marg = r_marg = t_marg = b_marg = 0; + viewport->polish(); + viewport->setBackgroundMode( QWidget::PaletteDark ); + viewport->setBackgroundOrigin( QWidget::WidgetOrigin ); + vMode = QScrollView::Auto; + hMode = QScrollView::Auto; + corner = 0; + defaultCorner = new QWidget( parent, "qt_default_corner" ); + defaultCorner->hide(); + vbar->setSteps( 20, 1/*set later*/ ); + hbar->setSteps( 20, 1/*set later*/ ); + policy = QScrollView::Default; + signal_choke = FALSE; + static_bg = FALSE; + fake_scroll = FALSE; + hbarPressed = FALSE; + vbarPressed = FALSE; + } + ~QScrollViewData(); + + QSVChildRec* rec(QWidget* w) { return childDict.find(w); } + QSVChildRec* ancestorRec(QWidget* w); + QSVChildRec* addChildRec(QWidget* w, int x, int y ) + { + QSVChildRec *r = new QSVChildRec(w,x,y); + children.append(r); + childDict.insert(w, r); + return r; + } + void deleteChildRec(QSVChildRec* r) + { + childDict.remove(r->child); + children.removeRef(r); + delete r; + } + + void hideOrShowAll(QScrollView* sv, bool isScroll = FALSE ); + void moveAllBy(int dx, int dy); + bool anyVisibleChildren(); + void autoMove(QScrollView* sv); + void autoResize(QScrollView* sv); + void autoResizeHint(QScrollView* sv); + void viewportResized( int w, int h ); + + QScrollBar* hbar; + QScrollBar* vbar; + bool hbarPressed; + bool vbarPressed; + QViewportWidget* viewport; + QClipperWidget* clipped_viewport; + int flags; + QPtrList<QSVChildRec> children; + QPtrDict<QSVChildRec> childDict; + QWidget* corner, *defaultCorner; + int vx, vy, vwidth, vheight; // for drawContents-style usage + int l_marg, r_marg, t_marg, b_marg; + QScrollView::ResizePolicy policy; + QScrollView::ScrollBarMode vMode; + QScrollView::ScrollBarMode hMode; +#ifndef QT_NO_DRAGANDDROP + QPoint cpDragStart; + QTimer autoscroll_timer; + int autoscroll_time; + int autoscroll_accel; + bool drag_autoscroll; +#endif + QTimer scrollbar_timer; + + uint static_bg : 1; + uint fake_scroll : 1; + + // This variable allows ensureVisible to move the contents then + // update both the sliders. Otherwise, updating the sliders would + // cause two image scrolls, creating ugly flashing. + // + uint signal_choke : 1; + + // This variables indicates in updateScrollBars() that we are + // in a resizeEvent() and thus don't want to flash scrollbars + uint inresize : 1; + uint use_cached_size_hint : 1; + QSize cachedSizeHint; + + inline int contentsX() const { return -vx; } + inline int contentsY() const { return -vy; } + inline int contentsWidth() const { return vwidth; } +}; + +inline QScrollViewData::~QScrollViewData() +{ + children.setAutoDelete( TRUE ); +} + +QSVChildRec* QScrollViewData::ancestorRec(QWidget* w) +{ + if ( clipped_viewport ) { + while (w->parentWidget() != clipped_viewport) { + w = w->parentWidget(); + if (!w) return 0; + } + } else { + while (w->parentWidget() != viewport) { + w = w->parentWidget(); + if (!w) return 0; + } + } + return rec(w); +} + +void QScrollViewData::hideOrShowAll(QScrollView* sv, bool isScroll ) +{ + if ( !clipped_viewport ) + return; + if ( clipped_viewport->x() <= 0 + && clipped_viewport->y() <= 0 + && clipped_viewport->width()+clipped_viewport->x() >= + viewport->width() + && clipped_viewport->height()+clipped_viewport->y() >= + viewport->height() ) { + // clipped_viewport still covers viewport + if( static_bg ) + clipped_viewport->repaint( TRUE ); + else if ( ( !isScroll && !clipped_viewport->testWFlags( Qt::WStaticContents) ) + || static_bg ) + QApplication::postEvent( clipped_viewport, + new QPaintEvent( clipped_viewport->clipRegion(), + !clipped_viewport->testWFlags(Qt::WResizeNoErase) ) ); + } else { + // Re-center + int nx = ( viewport->width() - clipped_viewport->width() ) / 2; + int ny = ( viewport->height() - clipped_viewport->height() ) / 2; + clipped_viewport->move(nx,ny); + clipped_viewport->update(); + } + for (QSVChildRec *r = children.first(); r; r=children.next()) { + r->hideOrShow(sv, clipped_viewport); + } +} + +void QScrollViewData::moveAllBy(int dx, int dy) +{ + if ( clipped_viewport && !static_bg ) { + clipped_viewport->move( clipped_viewport->x()+dx, + clipped_viewport->y()+dy ); + } else { + for (QSVChildRec *r = children.first(); r; r=children.next()) { + r->child->move(r->child->x()+dx,r->child->y()+dy); + } + if ( static_bg ) + viewport->repaint( TRUE ); + } +} + +bool QScrollViewData::anyVisibleChildren() +{ + for (QSVChildRec *r = children.first(); r; r=children.next()) { + if (r->child->isVisible()) return TRUE; + } + return FALSE; +} + +void QScrollViewData::autoMove(QScrollView* sv) +{ + if ( policy == QScrollView::AutoOne ) { + QSVChildRec* r = children.first(); + if (r) + sv->setContentsPos(-r->child->x(),-r->child->y()); + } +} + +void QScrollViewData::autoResize(QScrollView* sv) +{ + if ( policy == QScrollView::AutoOne ) { + QSVChildRec* r = children.first(); + if (r) + sv->resizeContents(r->child->width(),r->child->height()); + } +} + +void QScrollViewData::autoResizeHint(QScrollView* sv) +{ + if ( policy == QScrollView::AutoOne ) { + QSVChildRec* r = children.first(); + if (r) { + QSize s = r->child->sizeHint(); + if ( s.isValid() ) + r->child->resize(s); + } + } else if ( policy == QScrollView::AutoOneFit ) { + QSVChildRec* r = children.first(); + if (r) { + QSize sh = r->child->sizeHint(); + sh = sh.boundedTo( r->child->maximumSize() ); + sv->resizeContents( sh.width(), sh.height() ); + } + } +} + +void QScrollViewData::viewportResized( int w, int h ) +{ + if ( policy == QScrollView::AutoOneFit ) { + QSVChildRec* r = children.first(); + if (r) { + QSize sh = r->child->sizeHint(); + sh = sh.boundedTo( r->child->maximumSize() ); + r->child->resize( QMAX(w,sh.width()), QMAX(h,sh.height()) ); + } + + } +} + + +/*! + \class QScrollView qscrollview.h + \brief The QScrollView widget provides a scrolling area with on-demand scroll bars. + + \ingroup abstractwidgets + \mainclass + + The QScrollView is a large canvas - potentially larger than the + coordinate system normally supported by the underlying window + system. This is important because it is quite easy to go beyond + these limitations (e.g. many web pages are more than 32000 pixels + high). Additionally, the QScrollView can have QWidgets positioned + on it that scroll around with the drawn content. These sub-widgets + can also have positions outside the normal coordinate range (but + they are still limited in size). + + To provide content for the widget, inherit from QScrollView, + reimplement drawContents() and use resizeContents() to set the + size of the viewed area. Use addChild() and moveChild() to + position widgets on the view. + + To use QScrollView effectively it is important to understand its + widget structure in the three styles of use: a single large child + widget, a large panning area with some widgets and a large panning + area with many widgets. + + \section1 Using One Big Widget + + \img qscrollview-vp2.png + + The first, simplest usage of QScrollView (depicted above), is + appropriate for scrolling areas that are never more than about + 4000 pixels in either dimension (this is about the maximum + reliable size on X11 servers). In this usage, you just make one + large child in the QScrollView. The child should be a child of the + viewport() of the scrollview and be added with addChild(): + \code + QScrollView* sv = new QScrollView(...); + QVBox* big_box = new QVBox(sv->viewport()); + sv->addChild(big_box); + \endcode + You can go on to add arbitrary child widgets to the single child + in the scrollview as you would with any widget: + \code + QLabel* child1 = new QLabel("CHILD", big_box); + QLabel* child2 = new QLabel("CHILD", big_box); + QLabel* child3 = new QLabel("CHILD", big_box); + ... + \endcode + + Here the QScrollView has four children: the viewport(), the + verticalScrollBar(), the horizontalScrollBar() and a small + cornerWidget(). The viewport() has one child: the big QVBox. The + QVBox has the three QLabel objects as child widgets. When the view + is scrolled, the QVBox is moved; its children move with it as + child widgets normally do. + + \section1 Using a Very Big View with Some Widgets + + \img qscrollview-vp.png + + The second usage of QScrollView (depicted above) is appropriate + when few, if any, widgets are on a very large scrolling area that + is potentially larger than 4000 pixels in either dimension. In + this usage you call resizeContents() to set the size of the area + and reimplement drawContents() to paint the contents. You may also + add some widgets by making them children of the viewport() and + adding them with addChild() (this is the same as the process for + the single large widget in the previous example): + \code + QScrollView* sv = new QScrollView(...); + QLabel* child1 = new QLabel("CHILD", sv->viewport()); + sv->addChild(child1); + QLabel* child2 = new QLabel("CHILD", sv->viewport()); + sv->addChild(child2); + QLabel* child3 = new QLabel("CHILD", sv->viewport()); + sv->addChild(child3); + \endcode + Here, the QScrollView has the same four children: the viewport(), + the verticalScrollBar(), the horizontalScrollBar() and a small + cornerWidget(). The viewport() has the three QLabel objects as + child widgets. When the view is scrolled, the scrollview moves the + child widgets individually. + + \section1 Using a Very Big View with Many Widgets + + \target enableclipper + \img qscrollview-cl.png + + The final usage of QScrollView (depicted above) is appropriate + when many widgets are on a very large scrolling area that is + potentially larger than 4000 pixels in either dimension. In this + usage you call resizeContents() to set the size of the area and + reimplement drawContents() to paint the contents. You then call + enableClipper(TRUE) and add widgets, again by making them children + of the viewport(), and adding them with addChild(): + \code + QScrollView* sv = new QScrollView(...); + sv->enableClipper(TRUE); + QLabel* child1 = new QLabel("CHILD", sv->viewport()); + sv->addChild(child1); + QLabel* child2 = new QLabel("CHILD", sv->viewport()); + sv->addChild(child2); + QLabel* child3 = new QLabel("CHILD", sv->viewport()); + sv->addChild(child3); + \endcode + + Here, the QScrollView has four children: the clipper() (not the + viewport() this time), the verticalScrollBar(), the + horizontalScrollBar() and a small cornerWidget(). The clipper() + has one child: the viewport(). The viewport() has the same three + labels as child widgets. When the view is scrolled the viewport() + is moved; its children move with it as child widgets normally do. + + \target allviews + \section1 Details Relevant for All Views + + Normally you will use the first or third method if you want any + child widgets in the view. + + Note that the widget you see in the scrolled area is the + viewport() widget, not the QScrollView itself. So to turn mouse + tracking on, for example, use viewport()->setMouseTracking(TRUE). + + To enable drag-and-drop, you would setAcceptDrops(TRUE) on the + QScrollView (because drag-and-drop events propagate to the + parent). But to work out the logical position in the view, you + would need to map the drop co-ordinate from being relative to the + QScrollView to being relative to the contents; use the function + viewportToContents() for this. + + To handle mouse events on the scrolling area, subclass scrollview + as you would subclass other widgets, but rather than + reimplementing mousePressEvent(), reimplement + contentsMousePressEvent() instead. The contents specific event + handlers provide translated events in the coordinate system of the + scrollview. If you reimplement mousePressEvent(), you'll get + called only when part of the QScrollView is clicked: and the only + such part is the "corner" (if you don't set a cornerWidget()) and + the frame; everything else is covered up by the viewport, clipper + or scroll bars. + + When you construct a QScrollView, some of the widget flags apply + to the viewport() instead of being sent to the QWidget constructor + for the QScrollView. This applies to \c WNoAutoErase, \c + WStaticContents, and \c WPaintClever. See \l Qt::WidgetFlags for + documentation about these flags. Here are some examples: + + \list + + \i An image-manipulation widget would use \c + WNoAutoErase|WStaticContents because the widget draws all pixels + itself, and when its size increases, it only needs a paint event + for the new part because the old part remains unchanged. + + \i A scrolling game widget in which the background scrolls as the + characters move might use \c WNoAutoErase (in addition to \c + WStaticContents) so that the window system background does not + flash in and out during scrolling. + + \i A word processing widget might use \c WNoAutoErase and repaint + itself line by line to get a less-flickery resizing. If the widget + is in a mode in which no text justification can take place, it + might use \c WStaticContents too, so that it would only get a + repaint for the newly visible parts. + + \endlist + + Child widgets may be moved using addChild() or moveChild(). Use + childX() and childY() to get the position of a child widget. + + A widget may be placed in the corner between the vertical and + horizontal scrollbars with setCornerWidget(). You can get access + to the scrollbars using horizontalScrollBar() and + verticalScrollBar(), and to the viewport with viewport(). The + scroll view can be scrolled using scrollBy(), ensureVisible(), + setContentsPos() or center(). + + The visible area is given by visibleWidth() and visibleHeight(), + and the contents area by contentsWidth() and contentsHeight(). The + contents may be repainted using one of the repaintContents() or + updateContents() functions. + + Coordinate conversion is provided by contentsToViewport() and + viewportToContents(). + + The contentsMoving() signal is emitted just before the contents + are moved to a new position. + + \warning QScrollView currently does not erase the background when + resized, i.e. you must always clear the background manually in + scrollview subclasses. This will change in a future version of Qt + and we recommend specifying the WNoAutoErase flag explicitly. + + <img src=qscrollview-m.png> <img src=qscrollview-w.png> +*/ + + +/*! + \enum QScrollView::ResizePolicy + + This enum type is used to control a QScrollView's reaction to + resize events. + + \value Default the QScrollView selects one of the other settings + automatically when it has to. In this version of Qt, QScrollView + changes to \c Manual if you resize the contents with + resizeContents() and to \c AutoOne if a child is added. + + \value Manual the contents stays the size set by resizeContents(). + + \value AutoOne if there is only one child widget the contents stays + the size of that widget. Otherwise the behavior is undefined. + + \value AutoOneFit if there is only one child widget the contents stays + the size of that widget's sizeHint(). If the scrollview is resized + larger than the child's sizeHint(), the child will be resized to + fit. If there is more than one child, the behavior is undefined. + +*/ +//#### The widget will be resized to its sizeHint() when a LayoutHint event +//#### is received + +/*! + Constructs a QScrollView called \a name with parent \a parent and + widget flags \a f. + + The widget flags \c WStaticContents, \c WNoAutoErase and \c + WPaintClever are propagated to the viewport() widget. The other + widget flags are propagated to the parent constructor as usual. +*/ + +QScrollView::QScrollView( QWidget *parent, const char *name, WFlags f ) : + QFrame( parent, name, f & (~WStaticContents) & (~WResizeNoErase) ) +{ + WFlags flags = WResizeNoErase | (f&WPaintClever) | (f&WRepaintNoErase) | (f&WStaticContents); + d = new QScrollViewData( this, flags ); + +#ifndef QT_NO_DRAGANDDROP + connect( &d->autoscroll_timer, SIGNAL( timeout() ), + this, SLOT( doDragAutoScroll() ) ); +#endif + + connect( d->hbar, SIGNAL( valueChanged(int) ), + this, SLOT( hslide(int) ) ); + connect( d->vbar, SIGNAL( valueChanged(int) ), + this, SLOT( vslide(int) ) ); + + connect( d->hbar, SIGNAL(sliderPressed()), this, SLOT(hbarIsPressed()) ); + connect( d->hbar, SIGNAL(sliderReleased()), this, SLOT(hbarIsReleased()) ); + connect( d->vbar, SIGNAL(sliderPressed()), this, SLOT(vbarIsPressed()) ); + connect( d->vbar, SIGNAL(sliderReleased()), this, SLOT(vbarIsReleased()) ); + + + d->viewport->installEventFilter( this ); + + connect( &d->scrollbar_timer, SIGNAL( timeout() ), + this, SLOT( updateScrollBars() ) ); + + setFrameStyle( QFrame::StyledPanel | QFrame::Sunken ); + setLineWidth( style().pixelMetric(QStyle::PM_DefaultFrameWidth, this) ); + setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) ); +} + + +/*! + Destroys the QScrollView. Any children added with addChild() will + be deleted. +*/ +QScrollView::~QScrollView() +{ + // Be careful not to get all those useless events... + if ( d->clipped_viewport ) + d->clipped_viewport->removeEventFilter( this ); + else + d->viewport->removeEventFilter( this ); + + // order is important + // ~QWidget may cause a WM_ERASEBKGND on Windows + delete d->vbar; + d->vbar = 0; + delete d->hbar; + d->hbar = 0; + delete d->viewport; + d->viewport = 0; + delete d; + d = 0; +} + +/*! + \fn void QScrollView::horizontalSliderPressed() + + This signal is emitted whenever the user presses the horizontal slider. +*/ +/*! + \fn void QScrollView::horizontalSliderReleased() + + This signal is emitted whenever the user releases the horizontal slider. +*/ +/*! + \fn void QScrollView::verticalSliderPressed() + + This signal is emitted whenever the user presses the vertical slider. +*/ +/*! + \fn void QScrollView::verticalSliderReleased() + + This signal is emitted whenever the user releases the vertical slider. +*/ +void QScrollView::hbarIsPressed() +{ + d->hbarPressed = TRUE; + emit( horizontalSliderPressed() ); +} + +void QScrollView::hbarIsReleased() +{ + d->hbarPressed = FALSE; + emit( horizontalSliderReleased() ); +} + +/*! + Returns TRUE if horizontal slider is pressed by user; otherwise returns FALSE. +*/ +bool QScrollView::isHorizontalSliderPressed() +{ + return d->hbarPressed; +} + +void QScrollView::vbarIsPressed() +{ + d->vbarPressed = TRUE; + emit( verticalSliderPressed() ); +} + +void QScrollView::vbarIsReleased() +{ + d->vbarPressed = FALSE; + emit( verticalSliderReleased() ); +} + +/*! + Returns TRUE if vertical slider is pressed by user; otherwise returns FALSE. +*/ +bool QScrollView::isVerticalSliderPressed() +{ + return d->vbarPressed; +} + +/*! + \reimp +*/ +void QScrollView::styleChange( QStyle& old ) +{ + QWidget::styleChange( old ); + updateScrollBars(); + d->cachedSizeHint = QSize(); +} + +/*! + \reimp +*/ +void QScrollView::fontChange( const QFont &old ) +{ + QWidget::fontChange( old ); + updateScrollBars(); + d->cachedSizeHint = QSize(); +} + +void QScrollView::hslide( int pos ) +{ + if ( !d->signal_choke ) { + moveContents( -pos, -d->contentsY() ); + QApplication::syncX(); + } +} + +void QScrollView::vslide( int pos ) +{ + if ( !d->signal_choke ) { + moveContents( -d->contentsX(), -pos ); + QApplication::syncX(); + } +} + +/*! + Called when the horizontal scroll bar geometry changes. This is + provided as a protected function so that subclasses can do + interesting things such as providing extra buttons in some of the + space normally used by the scroll bars. + + The default implementation simply gives all the space to \a hbar. + The new geometry is given by \a x, \a y, \a w and \a h. + + \sa setVBarGeometry() +*/ +void QScrollView::setHBarGeometry(QScrollBar& hbar, + int x, int y, int w, int h) +{ + hbar.setGeometry( x, y, w, h ); +} + +/*! + Called when the vertical scroll bar geometry changes. This is + provided as a protected function so that subclasses can do + interesting things such as providing extra buttons in some of the + space normally used by the scroll bars. + + The default implementation simply gives all the space to \a vbar. + The new geometry is given by \a x, \a y, \a w and \a h. + + \sa setHBarGeometry() +*/ +void QScrollView::setVBarGeometry( QScrollBar& vbar, + int x, int y, int w, int h) +{ + vbar.setGeometry( x, y, w, h ); +} + + +/*! + Returns the viewport size for size (\a x, \a y). + + The viewport size depends on \a (x, y) (the size of the contents), + the size of this widget and the modes of the horizontal and + vertical scroll bars. + + This function permits widgets that can trade vertical and + horizontal space for each other to control scroll bar appearance + better. For example, a word processor or web browser can control + the width of the right margin accurately, whether or not there + needs to be a vertical scroll bar. +*/ + +QSize QScrollView::viewportSize( int x, int y ) const +{ + int fw = frameWidth(); + int lmarg = fw+d->l_marg; + int rmarg = fw+d->r_marg; + int tmarg = fw+d->t_marg; + int bmarg = fw+d->b_marg; + + int w = width(); + int h = height(); + + bool needh, needv; + bool showh, showv; + int hsbExt = horizontalScrollBar()->sizeHint().height(); + int vsbExt = verticalScrollBar()->sizeHint().width(); + + if ( d->policy != AutoOne || d->anyVisibleChildren() ) { + // Do we definitely need the scrollbar? + needh = w-lmarg-rmarg < x; + needv = h-tmarg-bmarg < y; + + // Do we intend to show the scrollbar? + if (d->hMode == AlwaysOn) + showh = TRUE; + else if (d->hMode == AlwaysOff) + showh = FALSE; + else + showh = needh; + + if (d->vMode == AlwaysOn) + showv = TRUE; + else if (d->vMode == AlwaysOff) + showv = FALSE; + else + showv = needv; + + // Given other scrollbar will be shown, NOW do we need one? + if ( showh && h-vsbExt-tmarg-bmarg < y ) { + if (d->vMode == Auto) + showv=TRUE; + } + if ( showv && w-hsbExt-lmarg-rmarg < x ) { + if (d->hMode == Auto) + showh=TRUE; + } + } else { + // Scrollbars not needed, only show scrollbar that are always on. + showh = d->hMode == AlwaysOn; + showv = d->vMode == AlwaysOn; + } + + return QSize( w-lmarg-rmarg - (showv ? vsbExt : 0), + h-tmarg-bmarg - (showh ? hsbExt : 0) ); +} + + +/*! + Updates scroll bars: all possibilities are considered. You should + never need to call this in your code. +*/ +void QScrollView::updateScrollBars() +{ + if(!horizontalScrollBar() && !verticalScrollBar()) + return; + + // I support this should use viewportSize()... but it needs + // so many of the temporary variables from viewportSize. hm. + int fw = frameWidth(); + int lmarg = fw+d->l_marg; + int rmarg = fw+d->r_marg; + int tmarg = fw+d->t_marg; + int bmarg = fw+d->b_marg; + + int w = width(); + int h = height(); + + int portw, porth; + + bool needh; + bool needv; + bool showh; + bool showv; + bool showc = FALSE; + + int hsbExt = horizontalScrollBar()->sizeHint().height(); + int vsbExt = verticalScrollBar()->sizeHint().width(); + + QSize oldVisibleSize( visibleWidth(), visibleHeight() ); + + if ( d->policy != AutoOne || d->anyVisibleChildren() ) { + // Do we definitely need the scrollbar? + needh = w-lmarg-rmarg < d->contentsWidth(); + if ( d->inresize ) + needh = !horizontalScrollBar()->isHidden(); + needv = h-tmarg-bmarg < contentsHeight(); + + // Do we intend to show the scrollbar? + if (d->hMode == AlwaysOn) + showh = TRUE; + else if (d->hMode == AlwaysOff) + showh = FALSE; + else + showh = needh; + + if (d->vMode == AlwaysOn) + showv = TRUE; + else if (d->vMode == AlwaysOff) + showv = FALSE; + else + showv = needv; + +#ifdef Q_WS_MAC + bool mac_need_scroll = FALSE; + if(!parentWidget()) { + mac_need_scroll = TRUE; + } else { + QWidget *tlw = topLevelWidget(); + QPoint tlw_br = QPoint(tlw->width(), tlw->height()), + my_br = posInWindow(this) + QPoint(w, h); + if(my_br.x() >= tlw_br.x() - 3 && my_br.y() >= tlw_br.y() - 3) + mac_need_scroll = TRUE; + } + if(mac_need_scroll) { + WindowAttributes attr; + GetWindowAttributes((WindowPtr)handle(), &attr); + mac_need_scroll = (attr & kWindowResizableAttribute); + } + if(mac_need_scroll) { + showc = TRUE; + if(d->vMode == Auto) + showv = TRUE; + if(d->hMode == Auto) + showh = TRUE; + } +#endif + + // Given other scrollbar will be shown, NOW do we need one? + if ( showh && h-vsbExt-tmarg-bmarg < contentsHeight() ) { + needv=TRUE; + if (d->vMode == Auto) + showv=TRUE; + } + if ( showv && !d->inresize && w-hsbExt-lmarg-rmarg < d->contentsWidth() ) { + needh=TRUE; + if (d->hMode == Auto) + showh=TRUE; + } + } else { + // Scrollbars not needed, only show scrollbar that are always on. + needh = needv = FALSE; + showh = d->hMode == AlwaysOn; + showv = d->vMode == AlwaysOn; + } + + bool sc = d->signal_choke; + d->signal_choke=TRUE; + + // Hide unneeded scrollbar, calculate viewport size + if ( showh ) { + porth=h-hsbExt-tmarg-bmarg; + } else { + if (!needh) + d->hbar->setValue(0); + d->hbar->hide(); + porth=h-tmarg-bmarg; + } + if ( showv ) { + portw=w-vsbExt-lmarg-rmarg; + } else { + if (!needv) + d->vbar->setValue(0); + d->vbar->hide(); + portw=w-lmarg-rmarg; + } + + // Configure scrollbars that we will show + if ( needv ) { + d->vbar->setRange( 0, contentsHeight()-porth ); + d->vbar->setSteps( QScrollView::d->vbar->lineStep(), porth ); + } else { + d->vbar->setRange( 0, 0 ); + } + if ( needh ) { + d->hbar->setRange( 0, QMAX(0, d->contentsWidth()-portw) ); + d->hbar->setSteps( QScrollView::d->hbar->lineStep(), portw ); + } else { + d->hbar->setRange( 0, 0 ); + } + + // Position the scrollbars, viewport and corner widget. + int bottom; + bool reverse = QApplication::reverseLayout(); + int xoffset = ( reverse && (showv || cornerWidget() )) ? vsbExt : 0; + int xpos = reverse ? 0 : w - vsbExt; + bool frameContentsOnly = + style().styleHint(QStyle::SH_ScrollView_FrameOnlyAroundContents); + + if( ! frameContentsOnly ) { + if ( reverse ) + xpos += fw; + else + xpos -= fw; + } + if ( showh ) { + int right = ( showc || showv || cornerWidget() ) ? w-vsbExt : w; + if ( ! frameContentsOnly ) + setHBarGeometry( *d->hbar, fw + xoffset, h-hsbExt-fw, + right-fw-fw, hsbExt ); + else + setHBarGeometry( *d->hbar, 0 + xoffset, h-hsbExt, right, + hsbExt ); + bottom=h-hsbExt; + } else { + bottom=h; + } + if ( showv ) { + clipper()->setGeometry( lmarg + xoffset, tmarg, + w-vsbExt-lmarg-rmarg, + bottom-tmarg-bmarg ); + d->viewportResized( w-vsbExt-lmarg-rmarg, bottom-tmarg-bmarg ); + if ( ! frameContentsOnly ) + changeFrameRect(QRect(0, 0, w, h) ); + else + changeFrameRect(QRect(xoffset, 0, w-vsbExt, bottom)); + if (showc || cornerWidget()) { + if ( ! frameContentsOnly ) + setVBarGeometry( *d->vbar, xpos, + fw, vsbExt, + h-hsbExt-fw-fw ); + else + setVBarGeometry( *d->vbar, xpos, 0, + vsbExt, + h-hsbExt ); + } + else { + if ( ! frameContentsOnly ) + setVBarGeometry( *d->vbar, xpos, + fw, vsbExt, + bottom-fw-fw ); + else + setVBarGeometry( *d->vbar, xpos, 0, + vsbExt, bottom ); + } + } else { + if ( ! frameContentsOnly ) + changeFrameRect(QRect(0, 0, w, h)); + else + changeFrameRect(QRect(0, 0, w, bottom)); + clipper()->setGeometry( lmarg, tmarg, + w-lmarg-rmarg, bottom-tmarg-bmarg ); + d->viewportResized( w-lmarg-rmarg, bottom-tmarg-bmarg ); + } + + QWidget *corner = d->corner; + if ( !d->corner ) + corner = d->defaultCorner; + if ( ! frameContentsOnly ) + corner->setGeometry( xpos, + h-hsbExt-fw, + vsbExt, + hsbExt ); + else + corner->setGeometry( xpos, + h-hsbExt, + vsbExt, + hsbExt ); + + d->signal_choke=sc; + + if ( d->contentsX()+visibleWidth() > d->contentsWidth() ) { + int x; +#if 0 + if ( reverse ) + x =QMIN(0,d->contentsWidth()-visibleWidth()); + else +#endif + x =QMAX(0,d->contentsWidth()-visibleWidth()); + d->hbar->setValue(x); + // Do it even if it is recursive + moveContents( -x, -d->contentsY() ); + } + if ( d->contentsY()+visibleHeight() > contentsHeight() ) { + int y=QMAX(0,contentsHeight()-visibleHeight()); + d->vbar->setValue(y); + // Do it even if it is recursive + moveContents( -d->contentsX(), -y ); + } + + // Finally, show the scroll bars + if ( showh && ( d->hbar->isHidden() || !d->hbar->isVisible() ) ) + d->hbar->show(); + if ( showv && ( d->vbar->isHidden() || !d->vbar->isVisible() ) ) + d->vbar->show(); + + d->signal_choke=TRUE; + d->vbar->setValue( d->contentsY() ); + d->hbar->setValue( d->contentsX() ); + d->signal_choke=FALSE; + + QSize newVisibleSize( visibleWidth(), visibleHeight() ); + if ( d->clipped_viewport && oldVisibleSize != newVisibleSize ) { + QResizeEvent e( newVisibleSize, oldVisibleSize ); + viewportResizeEvent( &e ); + } +} + + +/*! + \reimp +*/ +void QScrollView::show() +{ + if ( isVisible() ) + return; + QWidget::show(); + updateScrollBars(); + d->hideOrShowAll(this); +} + +/*! + \reimp + */ +void QScrollView::resize( int w, int h ) +{ + QWidget::resize( w, h ); +} + +/*! + \reimp +*/ +void QScrollView::resize( const QSize& s ) +{ + resize( s.width(), s.height() ); +} + +/*! + \reimp +*/ +void QScrollView::resizeEvent( QResizeEvent* event ) +{ + QFrame::resizeEvent( event ); + +#if 0 + if ( QApplication::reverseLayout() ) { + d->fake_scroll = TRUE; + scrollBy( -event->size().width() + event->oldSize().width(), 0 ); + d->fake_scroll = FALSE; + } +#endif + + bool inresize = d->inresize; + d->inresize = TRUE; + updateScrollBars(); + d->inresize = inresize; + d->scrollbar_timer.start( 0, TRUE ); + + d->hideOrShowAll(this); +} + + + +/*! + \reimp +*/ +void QScrollView::mousePressEvent( QMouseEvent * e) //#### remove for 4.0 +{ + e->ignore(); +} + +/*! + \reimp +*/ +void QScrollView::mouseReleaseEvent( QMouseEvent *e ) //#### remove for 4.0 +{ + e->ignore(); +} + + +/*! + \reimp +*/ +void QScrollView::mouseDoubleClickEvent( QMouseEvent *e ) //#### remove for 4.0 +{ + e->ignore(); +} + +/*! + \reimp +*/ +void QScrollView::mouseMoveEvent( QMouseEvent *e ) //#### remove for 4.0 +{ + e->ignore(); +} + +/*! + \reimp +*/ +#ifndef QT_NO_WHEELEVENT +void QScrollView::wheelEvent( QWheelEvent *e ) +{ + QWheelEvent ce( viewport()->mapFromGlobal( e->globalPos() ), + e->globalPos(), e->delta(), e->state(), e->orientation()); + viewportWheelEvent(&ce); + if ( !ce.isAccepted() ) { + if ( e->orientation() == Horizontal && horizontalScrollBar() && horizontalScrollBar()->isEnabled() ) + QApplication::sendEvent( horizontalScrollBar(), e); + else if (e->orientation() == Vertical && verticalScrollBar() && verticalScrollBar()->isEnabled() ) + QApplication::sendEvent( verticalScrollBar(), e); + } else { + e->accept(); + } +} +#endif + +/*! + \reimp +*/ +void QScrollView::contextMenuEvent( QContextMenuEvent *e ) +{ + if ( e->reason() != QContextMenuEvent::Keyboard ) { + e->ignore(); + return; + } + + QContextMenuEvent ce( e->reason(), viewport()->mapFromGlobal( e->globalPos() ), + e->globalPos(), e->state() ); + viewportContextMenuEvent( &ce ); + if ( ce.isAccepted() ) + e->accept(); + else + e->ignore(); +} + +QScrollView::ScrollBarMode QScrollView::vScrollBarMode() const +{ + return d->vMode; +} + + +/*! + \enum QScrollView::ScrollBarMode + + This enum type describes the various modes of QScrollView's scroll + bars. + + \value Auto QScrollView shows a scroll bar when the content is + too large to fit and not otherwise. This is the default. + + \value AlwaysOff QScrollView never shows a scroll bar. + + \value AlwaysOn QScrollView always shows a scroll bar. + + (The modes for the horizontal and vertical scroll bars are + independent.) +*/ + + +/*! + \property QScrollView::vScrollBarMode + \brief the mode for the vertical scroll bar + + The default mode is \c QScrollView::Auto. + + \sa hScrollBarMode +*/ +void QScrollView::setVScrollBarMode( ScrollBarMode mode ) +{ + if (d->vMode != mode) { + d->vMode = mode; + updateScrollBars(); + } +} + + +/*! + \property QScrollView::hScrollBarMode + \brief the mode for the horizontal scroll bar + + The default mode is \c QScrollView::Auto. + + \sa vScrollBarMode +*/ +QScrollView::ScrollBarMode QScrollView::hScrollBarMode() const +{ + return d->hMode; +} + +void QScrollView::setHScrollBarMode( ScrollBarMode mode ) +{ + if (d->hMode != mode) { + d->hMode = mode; + updateScrollBars(); + } +} + + +/*! + Returns the widget in the corner between the two scroll bars. + + By default, no corner widget is present. +*/ +QWidget* QScrollView::cornerWidget() const +{ + return d->corner; +} + +/*! + Sets the widget in the \a corner between the two scroll bars. + + You will probably also want to set at least one of the scroll bar + modes to \c AlwaysOn. + + Passing 0 shows no widget in the corner. + + Any previous \a corner widget is hidden. + + You may call setCornerWidget() with the same widget at different + times. + + All widgets set here will be deleted by the QScrollView when it is + destroyed unless you separately reparent the widget after setting + some other corner widget (or 0). + + Any \e newly set widget should have no current parent. + + By default, no corner widget is present. + + \sa setVScrollBarMode(), setHScrollBarMode() +*/ +void QScrollView::setCornerWidget(QWidget* corner) +{ + QWidget* oldcorner = d->corner; + if (oldcorner != corner) { + if (oldcorner) oldcorner->hide(); + d->corner = corner; + + if ( corner && corner->parentWidget() != this ) { + // #### No clean way to get current WFlags + corner->reparent( this, (((QScrollView*)corner))->getWFlags(), + QPoint(0,0), FALSE ); + } + + updateScrollBars(); + if ( corner ) corner->show(); + } +} + + +void QScrollView::setResizePolicy( ResizePolicy r ) +{ + d->policy = r; +} + +/*! + \property QScrollView::resizePolicy + \brief the resize policy + + The default is \c Default. + + \sa ResizePolicy +*/ +QScrollView::ResizePolicy QScrollView::resizePolicy() const +{ + return d->policy; +} + +/*! + \reimp +*/ +void QScrollView::setEnabled( bool enable ) +{ + QFrame::setEnabled( enable ); +} + +/*! + Removes the \a child widget from the scrolled area. Note that this + happens automatically if the \a child is deleted. +*/ +void QScrollView::removeChild(QWidget* child) +{ + if ( !d || !child ) // First check in case we are destructing + return; + + QSVChildRec *r = d->rec(child); + if ( r ) d->deleteChildRec( r ); +} + +/*! + \reimp +*/ +void QScrollView::removeChild(QObject* child) +{ + QFrame::removeChild(child); +} + +/*! + Inserts the widget, \a child, into the scrolled area positioned at + (\a x, \a y). The position defaults to (0, 0). If the child is + already in the view, it is just moved. + + You may want to call enableClipper(TRUE) if you add a large number + of widgets. +*/ +void QScrollView::addChild(QWidget* child, int x, int y) +{ + if ( !child ) { +#if defined(QT_CHECK_NULL) + qWarning( "QScrollView::addChild(): Cannot add null child" ); +#endif + return; + } + child->polish(); + child->setBackgroundOrigin(WidgetOrigin); + + if ( child->parentWidget() == viewport() ) { + // May already be there + QSVChildRec *r = d->rec(child); + if (r) { + r->moveTo(this,x,y,d->clipped_viewport); + if ( d->policy > Manual ) { + d->autoResizeHint(this); + d->autoResize(this); // #### better to just deal with this one widget! + } + return; + } + } + + if ( d->children.isEmpty() && d->policy != Manual ) { + if ( d->policy == Default ) + setResizePolicy( AutoOne ); + child->installEventFilter( this ); + } else if ( d->policy == AutoOne ) { + child->removeEventFilter( this ); //#### ????? + setResizePolicy( Manual ); + } + if ( child->parentWidget() != viewport() ) { + child->reparent( viewport(), 0, QPoint(0,0), FALSE ); + } + d->addChildRec(child,x,y)->hideOrShow(this, d->clipped_viewport); + + if ( d->policy > Manual ) { + d->autoResizeHint(this); + d->autoResize(this); // #### better to just deal with this one widget! + } +} + +/*! + Repositions the \a child widget to (\a x, \a y). This function is + the same as addChild(). +*/ +void QScrollView::moveChild(QWidget* child, int x, int y) +{ + addChild(child,x,y); +} + +/*! + Returns the X position of the given \a child widget. Use this + rather than QWidget::x() for widgets added to the view. + + This function returns 0 if \a child has not been added to the view. +*/ +int QScrollView::childX(QWidget* child) +{ + QSVChildRec *r = d->rec(child); + return r ? r->x : 0; +} + +/*! + Returns the Y position of the given \a child widget. Use this + rather than QWidget::y() for widgets added to the view. + + This function returns 0 if \a child has not been added to the view. +*/ +int QScrollView::childY(QWidget* child) +{ + QSVChildRec *r = d->rec(child); + return r ? r->y : 0; +} + +/*! \fn bool QScrollView::childIsVisible(QWidget*) + \obsolete + + Returns TRUE if \a child is visible. This is equivalent + to child->isVisible(). +*/ + +/*! \fn void QScrollView::showChild(QWidget* child, bool y) + \obsolete + + Sets the visibility of \a child. Equivalent to + QWidget::show() or QWidget::hide(). +*/ + +/*! + This event filter ensures the scroll bars are updated when a + single contents widget is resized, shown, hidden or destroyed; it + passes mouse events to the QScrollView. The event is in \a e and + the object is in \a obj. +*/ + +bool QScrollView::eventFilter( QObject *obj, QEvent *e ) +{ + if ( !d ) + return FALSE; // we are destructing + if ( obj == d->viewport || obj == d->clipped_viewport ) { + switch ( e->type() ) { + /* Forward many events to viewport...() functions */ + case QEvent::Paint: + viewportPaintEvent( (QPaintEvent*)e ); + break; + case QEvent::Resize: + if ( !d->clipped_viewport ) + viewportResizeEvent( (QResizeEvent *)e ); + break; + case QEvent::MouseButtonPress: + viewportMousePressEvent( (QMouseEvent*)e ); + if ( ((QMouseEvent*)e)->isAccepted() ) + return TRUE; + break; + case QEvent::MouseButtonRelease: + viewportMouseReleaseEvent( (QMouseEvent*)e ); + if ( ((QMouseEvent*)e)->isAccepted() ) + return TRUE; + break; + case QEvent::MouseButtonDblClick: + viewportMouseDoubleClickEvent( (QMouseEvent*)e ); + if ( ((QMouseEvent*)e)->isAccepted() ) + return TRUE; + break; + case QEvent::MouseMove: + viewportMouseMoveEvent( (QMouseEvent*)e ); + if ( ((QMouseEvent*)e)->isAccepted() ) + return TRUE; + break; +#ifndef QT_NO_DRAGANDDROP + case QEvent::DragEnter: + viewportDragEnterEvent( (QDragEnterEvent*)e ); + break; + case QEvent::DragMove: { + if ( d->drag_autoscroll ) { + QPoint vp = ((QDragMoveEvent*) e)->pos(); + QRect inside_margin( autoscroll_margin, autoscroll_margin, + visibleWidth() - autoscroll_margin * 2, + visibleHeight() - autoscroll_margin * 2 ); + if ( !inside_margin.contains( vp ) ) { + startDragAutoScroll(); + // Keep sending move events + ( (QDragMoveEvent*)e )->accept( QRect(0,0,0,0) ); + } + } + viewportDragMoveEvent( (QDragMoveEvent*)e ); + } break; + case QEvent::DragLeave: + stopDragAutoScroll(); + viewportDragLeaveEvent( (QDragLeaveEvent*)e ); + break; + case QEvent::Drop: + stopDragAutoScroll(); + viewportDropEvent( (QDropEvent*)e ); + break; +#endif // QT_NO_DRAGANDDROP + case QEvent::ContextMenu: + viewportContextMenuEvent( (QContextMenuEvent*)e ); + if ( ((QContextMenuEvent*)e)->isAccepted() ) + return TRUE; + break; + case QEvent::ChildRemoved: + removeChild((QWidget*)((QChildEvent*)e)->child()); + break; + case QEvent::LayoutHint: + d->autoResizeHint(this); + break; + case QEvent::WindowActivate: + case QEvent::WindowDeactivate: + return TRUE; + default: + break; + } + } else if ( d && d->rec((QWidget*)obj) ) { // must be a child + if ( e->type() == QEvent::Resize ) + d->autoResize(this); + else if ( e->type() == QEvent::Move ) + d->autoMove(this); + } + return QFrame::eventFilter( obj, e ); // always continue with standard event processing +} + +/*! + This event handler is called whenever the QScrollView receives a + mousePressEvent(): the press position in \a e is translated to be a point + on the contents. +*/ +void QScrollView::contentsMousePressEvent( QMouseEvent* e ) +{ + e->ignore(); +} + +/*! + This event handler is called whenever the QScrollView receives a + mouseReleaseEvent(): the release position in \a e is translated to be a + point on the contents. +*/ +void QScrollView::contentsMouseReleaseEvent( QMouseEvent* e ) +{ + e->ignore(); +} + +/*! + This event handler is called whenever the QScrollView receives a + mouseDoubleClickEvent(): the click position in \a e is translated to be a + point on the contents. + + The default implementation generates a normal mouse press event. +*/ +void QScrollView::contentsMouseDoubleClickEvent( QMouseEvent* e ) +{ + contentsMousePressEvent(e); // try mouse press event +} + +/*! + This event handler is called whenever the QScrollView receives a + mouseMoveEvent(): the mouse position in \a e is translated to be a point + on the contents. +*/ +void QScrollView::contentsMouseMoveEvent( QMouseEvent* e ) +{ + e->ignore(); +} + +#ifndef QT_NO_DRAGANDDROP + +/*! + This event handler is called whenever the QScrollView receives a + dragEnterEvent(): the drag position is translated to be a point + on the contents. +*/ +void QScrollView::contentsDragEnterEvent( QDragEnterEvent * ) +{ +} + +/*! + This event handler is called whenever the QScrollView receives a + dragMoveEvent(): the drag position is translated to be a point on + the contents. +*/ +void QScrollView::contentsDragMoveEvent( QDragMoveEvent * ) +{ +} + +/*! + This event handler is called whenever the QScrollView receives a + dragLeaveEvent(): the drag position is translated to be a point + on the contents. +*/ +void QScrollView::contentsDragLeaveEvent( QDragLeaveEvent * ) +{ +} + +/*! + This event handler is called whenever the QScrollView receives a + dropEvent(): the drop position is translated to be a point on the + contents. +*/ +void QScrollView::contentsDropEvent( QDropEvent * ) +{ +} + +#endif // QT_NO_DRAGANDDROP + +/*! + This event handler is called whenever the QScrollView receives a + wheelEvent() in \a{e}: the mouse position is translated to be a + point on the contents. +*/ +#ifndef QT_NO_WHEELEVENT +void QScrollView::contentsWheelEvent( QWheelEvent * e ) +{ + e->ignore(); +} +#endif +/*! + This event handler is called whenever the QScrollView receives a + contextMenuEvent() in \a{e}: the mouse position is translated to + be a point on the contents. +*/ +void QScrollView::contentsContextMenuEvent( QContextMenuEvent *e ) +{ + e->ignore(); +} + +/*! + This is a low-level painting routine that draws the viewport + contents. Reimplement this if drawContents() is too high-level + (for example, if you don't want to open a QPainter on the + viewport). The paint event is passed in \a pe. +*/ +void QScrollView::viewportPaintEvent( QPaintEvent* pe ) +{ + QWidget* vp = viewport(); + + QPainter p(vp); + QRect r = pe->rect(); + + if ( d->clipped_viewport ) { + QRect rr( + -d->clipped_viewport->x(), -d->clipped_viewport->y(), + d->viewport->width(), d->viewport->height() + ); + r &= rr; + if ( r.isValid() ) { + int ex = r.x() + d->clipped_viewport->x() + d->contentsX(); + int ey = r.y() + d->clipped_viewport->y() + d->contentsY(); + int ew = r.width(); + int eh = r.height(); + drawContentsOffset(&p, + d->contentsX()+d->clipped_viewport->x(), + d->contentsY()+d->clipped_viewport->y(), + ex, ey, ew, eh); + } + } else { + r &= d->viewport->rect(); + int ex = r.x() + d->contentsX(); + int ey = r.y() + d->contentsY(); + int ew = r.width(); + int eh = r.height(); + drawContentsOffset(&p, d->contentsX(), d->contentsY(), ex, ey, ew, eh); + } +} + + +/*! + To provide simple processing of events on the contents, this + function receives all resize events sent to the viewport. + + \sa QWidget::resizeEvent() +*/ +void QScrollView::viewportResizeEvent( QResizeEvent* ) +{ +} + +/*! \internal + + To provide simple processing of events on the contents, this + function receives all mouse press events sent to the viewport, + translates the event and calls contentsMousePressEvent(). + + \sa contentsMousePressEvent(), QWidget::mousePressEvent() +*/ +void QScrollView::viewportMousePressEvent( QMouseEvent* e ) +{ + QMouseEvent ce(e->type(), viewportToContents(e->pos()), + e->globalPos(), e->button(), e->state()); + contentsMousePressEvent(&ce); + if ( !ce.isAccepted() ) + e->ignore(); +} + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all mouse release events sent to the viewport, translates + the event and calls contentsMouseReleaseEvent(). + + \sa QWidget::mouseReleaseEvent() +*/ +void QScrollView::viewportMouseReleaseEvent( QMouseEvent* e ) +{ + QMouseEvent ce(e->type(), viewportToContents(e->pos()), + e->globalPos(), e->button(), e->state()); + contentsMouseReleaseEvent(&ce); + if ( !ce.isAccepted() ) + e->ignore(); +} + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all mouse double click events sent to the viewport, + translates the event and calls contentsMouseDoubleClickEvent(). + + \sa QWidget::mouseDoubleClickEvent() +*/ +void QScrollView::viewportMouseDoubleClickEvent( QMouseEvent* e ) +{ + QMouseEvent ce(e->type(), viewportToContents(e->pos()), + e->globalPos(), e->button(), e->state()); + contentsMouseDoubleClickEvent(&ce); + if ( !ce.isAccepted() ) + e->ignore(); +} + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all mouse move events sent to the viewport, translates the + event and calls contentsMouseMoveEvent(). + + \sa QWidget::mouseMoveEvent() +*/ +void QScrollView::viewportMouseMoveEvent( QMouseEvent* e ) +{ + QMouseEvent ce(e->type(), viewportToContents(e->pos()), + e->globalPos(), e->button(), e->state()); + contentsMouseMoveEvent(&ce); + if ( !ce.isAccepted() ) + e->ignore(); +} + +#ifndef QT_NO_DRAGANDDROP + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all drag enter events sent to the viewport, translates the + event and calls contentsDragEnterEvent(). + + \sa QWidget::dragEnterEvent() +*/ +void QScrollView::viewportDragEnterEvent( QDragEnterEvent* e ) +{ + e->setPoint(viewportToContents(e->pos())); + contentsDragEnterEvent(e); + e->setPoint(contentsToViewport(e->pos())); +} + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all drag move events sent to the viewport, translates the + event and calls contentsDragMoveEvent(). + + \sa QWidget::dragMoveEvent() +*/ +void QScrollView::viewportDragMoveEvent( QDragMoveEvent* e ) +{ + e->setPoint(viewportToContents(e->pos())); + contentsDragMoveEvent(e); + e->setPoint(contentsToViewport(e->pos())); +} + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all drag leave events sent to the viewport and calls + contentsDragLeaveEvent(). + + \sa QWidget::dragLeaveEvent() +*/ +void QScrollView::viewportDragLeaveEvent( QDragLeaveEvent* e ) +{ + contentsDragLeaveEvent(e); +} + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all drop events sent to the viewport, translates the event + and calls contentsDropEvent(). + + \sa QWidget::dropEvent() +*/ +void QScrollView::viewportDropEvent( QDropEvent* e ) +{ + e->setPoint(viewportToContents(e->pos())); + contentsDropEvent(e); + e->setPoint(contentsToViewport(e->pos())); +} + +#endif // QT_NO_DRAGANDDROP + +/*!\internal + + To provide simple processing of events on the contents, this function + receives all wheel events sent to the viewport, translates the + event and calls contentsWheelEvent(). + + \sa QWidget::wheelEvent() +*/ +#ifndef QT_NO_WHEELEVENT +void QScrollView::viewportWheelEvent( QWheelEvent* e ) +{ + /* + Different than standard mouse events, because wheel events might + be sent to the focus widget if the widget-under-mouse doesn't want + the event itself. + */ + QWheelEvent ce( viewportToContents(e->pos()), + e->globalPos(), e->delta(), e->state(), e->orientation()); + contentsWheelEvent(&ce); + if ( ce.isAccepted() ) + e->accept(); + else + e->ignore(); +} +#endif + +/*! \internal + + To provide simple processing of events on the contents, this function + receives all context menu events sent to the viewport, translates the + event and calls contentsContextMenuEvent(). +*/ +void QScrollView::viewportContextMenuEvent( QContextMenuEvent *e ) +{ + QContextMenuEvent ce(e->reason(), viewportToContents(e->pos()), e->globalPos(), e->state() ); + contentsContextMenuEvent( &ce ); + if ( ce.isAccepted() ) + e->accept(); + else + e->ignore(); +} + +/*! + Returns the component horizontal scroll bar. It is made available + to allow accelerators, autoscrolling, etc. + + It should not be used for other purposes. + + This function never returns 0. +*/ +QScrollBar* QScrollView::horizontalScrollBar() const +{ + return d->hbar; +} + +/*! + Returns the component vertical scroll bar. It is made available to + allow accelerators, autoscrolling, etc. + + It should not be used for other purposes. + + This function never returns 0. +*/ +QScrollBar* QScrollView::verticalScrollBar() const { + return d->vbar; +} + + +/*! + Scrolls the content so that the point \a (x, y) is visible with at + least 50-pixel margins (if possible, otherwise centered). +*/ +void QScrollView::ensureVisible( int x, int y ) +{ + ensureVisible(x, y, 50, 50); +} + +/*! + \overload + + Scrolls the content so that the point \a (x, y) is visible with at + least the \a xmargin and \a ymargin margins (if possible, + otherwise centered). +*/ +void QScrollView::ensureVisible( int x, int y, int xmargin, int ymargin ) +{ + int pw=visibleWidth(); + int ph=visibleHeight(); + + int cx=-d->contentsX(); + int cy=-d->contentsY(); + int cw=d->contentsWidth(); + int ch=contentsHeight(); + + if ( pw < xmargin*2 ) + xmargin=pw/2; + if ( ph < ymargin*2 ) + ymargin=ph/2; + + if ( cw <= pw ) { + xmargin=0; + cx=0; + } + if ( ch <= ph ) { + ymargin=0; + cy=0; + } + + if ( x < -cx+xmargin ) + cx = -x+xmargin; + else if ( x >= -cx+pw-xmargin ) + cx = -x+pw-xmargin; + + if ( y < -cy+ymargin ) + cy = -y+ymargin; + else if ( y >= -cy+ph-ymargin ) + cy = -y+ph-ymargin; + + if ( cx > 0 ) + cx=0; + else if ( cx < pw-cw && cw>pw ) + cx=pw-cw; + + if ( cy > 0 ) + cy=0; + else if ( cy < ph-ch && ch>ph ) + cy=ph-ch; + + setContentsPos( -cx, -cy ); +} + +/*! + Scrolls the content so that the point \a (x, y) is in the top-left + corner. +*/ +void QScrollView::setContentsPos( int x, int y ) +{ +#if 0 + // bounds checking... + if ( QApplication::reverseLayout() ) + if ( x > d->contentsWidth() - visibleWidth() ) x = d->contentsWidth() - visibleWidth(); + else +#endif + if ( x < 0 ) x = 0; + if ( y < 0 ) y = 0; + // Choke signal handling while we update BOTH sliders. + d->signal_choke=TRUE; + moveContents( -x, -y ); + d->vbar->setValue( y ); + d->hbar->setValue( x ); + d->signal_choke=FALSE; +} + +/*! + Scrolls the content by \a dx to the left and \a dy upwards. +*/ +void QScrollView::scrollBy( int dx, int dy ) +{ + setContentsPos( QMAX( d->contentsX()+dx, 0 ), QMAX( d->contentsY()+dy, 0 ) ); +} + +/*! + Scrolls the content so that the point \a (x, y) is in the center + of visible area. +*/ +void QScrollView::center( int x, int y ) +{ + ensureVisible( x, y, 32000, 32000 ); +} + +/*! + \overload + + Scrolls the content so that the point \a (x, y) is visible with + the \a xmargin and \a ymargin margins (as fractions of visible + the area). + + For example: + \list + \i Margin 0.0 allows (x, y) to be on the edge of the visible area. + \i Margin 0.5 ensures that (x, y) is in middle 50% of the visible area. + \i Margin 1.0 ensures that (x, y) is in the center of the the visible area. + \endlist +*/ +void QScrollView::center( int x, int y, float xmargin, float ymargin ) +{ + int pw=visibleWidth(); + int ph=visibleHeight(); + ensureVisible( x, y, int( xmargin/2.0*pw+0.5 ), int( ymargin/2.0*ph+0.5 ) ); +} + + +/*! + \fn void QScrollView::contentsMoving(int x, int y) + + This signal is emitted just before the contents are moved to + position \a (x, y). + + \sa contentsX(), contentsY() +*/ + +/*! + Moves the contents by \a (x, y). +*/ +void QScrollView::moveContents(int x, int y) +{ + if ( -x+visibleWidth() > d->contentsWidth() ) +#if 0 + if( QApplication::reverseLayout() ) + x=QMAX(0,-d->contentsWidth()+visibleWidth()); + else +#endif + x=QMIN(0,-d->contentsWidth()+visibleWidth()); + if ( -y+visibleHeight() > contentsHeight() ) + y=QMIN(0,-contentsHeight()+visibleHeight()); + + int dx = x - d->vx; + int dy = y - d->vy; + + if (!dx && !dy) + return; // Nothing to do + + emit contentsMoving( -x, -y ); + + d->vx = x; + d->vy = y; + + if ( d->clipped_viewport || d->static_bg ) { + // Cheap move (usually) + d->moveAllBy(dx,dy); + } else if ( /*dx && dy ||*/ + ( QABS(dy) * 5 > visibleHeight() * 4 ) || + ( QABS(dx) * 5 > visibleWidth() * 4 ) + ) + { + // Big move + if ( viewport()->isUpdatesEnabled() ) + viewport()->update(); + d->moveAllBy(dx,dy); + } else if ( !d->fake_scroll || d->contentsWidth() > visibleWidth() ) { + // Small move + clipper()->scroll(dx,dy); + } + d->hideOrShowAll(this, TRUE ); +} + +#if (QT_VERSION-0 >= 0x040000) +#if defined(Q_CC_GNU) +#warning "Should rename contents{X,Y,Width,Height} to viewport{...}" +#endif +// Because it's the viewport rectangle that is "moving", not the contents. +#endif + +/*! + \property QScrollView::contentsX + \brief the X coordinate of the contents that are at the left edge of + the viewport. +*/ +int QScrollView::contentsX() const +{ + return d->contentsX(); +} + +/*! + \property QScrollView::contentsY + \brief the Y coordinate of the contents that are at the top edge of + the viewport. +*/ +int QScrollView::contentsY() const +{ + return d->contentsY(); +} + +/*! + \property QScrollView::contentsWidth + \brief the width of the contents area +*/ +int QScrollView::contentsWidth() const +{ + return d->contentsWidth(); +} + +/*! + \property QScrollView::contentsHeight + \brief the height of the contents area +*/ +int QScrollView::contentsHeight() const +{ + return d->vheight; +} + +/*! + Sets the size of the contents area to \a w pixels wide and \a h + pixels high and updates the viewport accordingly. +*/ +void QScrollView::resizeContents( int w, int h ) +{ + int ow = d->vwidth; + int oh = d->vheight; + d->vwidth = w; + d->vheight = h; + + d->scrollbar_timer.start( 0, TRUE ); + + if ( d->children.isEmpty() && d->policy == Default ) + setResizePolicy( Manual ); + + if ( ow > w ) { + // Swap + int t=w; + w=ow; + ow=t; + } + // Refresh area ow..w + if ( ow < visibleWidth() && w >= 0 ) { + if ( ow < 0 ) + ow = 0; + if ( w > visibleWidth() ) + w = visibleWidth(); + clipper()->update( d->contentsX()+ow, 0, w-ow, visibleHeight() ); + } + + if ( oh > h ) { + // Swap + int t=h; + h=oh; + oh=t; + } + // Refresh area oh..h + if ( oh < visibleHeight() && h >= 0 ) { + if ( oh < 0 ) + oh = 0; + if ( h > visibleHeight() ) + h = visibleHeight(); + clipper()->update( 0, d->contentsY()+oh, visibleWidth(), h-oh); + } +} + +/*! + Calls update() on a rectangle defined by \a x, \a y, \a w, \a h, + translated appropriately. If the rectangle is not visible, nothing + is repainted. + + \sa repaintContents() +*/ +void QScrollView::updateContents( int x, int y, int w, int h ) +{ + if ( testWState(WState_Visible|WState_BlockUpdates) != WState_Visible ) + return; + + QWidget* vp = viewport(); + + // Translate + x -= d->contentsX(); + y -= d->contentsY(); + + // Clip to QCOORD space + if ( x < 0 ) { + w += x; + x = 0; + } + if ( y < 0 ) { + h += y; + y = 0; + } + + if ( w < 0 || h < 0 ) + return; + if ( x > visibleWidth() || y > visibleHeight() ) + return; + + if ( w > visibleWidth() ) + w = visibleWidth(); + if ( h > visibleHeight() ) + h = visibleHeight(); + + if ( d->clipped_viewport ) { + // Translate clipper() to viewport() + x -= d->clipped_viewport->x(); + y -= d->clipped_viewport->y(); + } + + vp->update( x, y, w, h ); +} + +/*! + \overload + + Updates the contents in rectangle \a r +*/ +void QScrollView::updateContents( const QRect& r ) +{ + updateContents(r.x(), r.y(), r.width(), r.height()); +} + +/*! + \overload +*/ +void QScrollView::updateContents() +{ + updateContents( d->contentsX(), d->contentsY(), visibleWidth(), visibleHeight() ); +} + +/*! + \overload + + Repaints the contents of rectangle \a r. If \a erase is TRUE the + background is cleared using the background color. +*/ +void QScrollView::repaintContents( const QRect& r, bool erase ) +{ + repaintContents(r.x(), r.y(), r.width(), r.height(), erase); +} + + +/*! + \overload + + Repaints the contents. If \a erase is TRUE the background is + cleared using the background color. +*/ +void QScrollView::repaintContents( bool erase ) +{ + repaintContents( d->contentsX(), d->contentsY(), visibleWidth(), visibleHeight(), erase ); +} + + +/*! + Calls repaint() on a rectangle defined by \a x, \a y, \a w, \a h, + translated appropriately. If the rectangle is not visible, nothing + is repainted. If \a erase is TRUE the background is cleared using + the background color. + + \sa updateContents() +*/ +void QScrollView::repaintContents( int x, int y, int w, int h, bool erase ) +{ + if ( testWState(WState_Visible|WState_BlockUpdates) != WState_Visible ) + return; + + QWidget* vp = viewport(); + + // Translate logical to clipper() + x -= d->contentsX(); + y -= d->contentsY(); + + // Clip to QCOORD space + if ( x < 0 ) { + w += x; + x = 0; + } + if ( y < 0 ) { + h += y; + y = 0; + } + + if ( w < 0 || h < 0 ) + return; + if ( w > visibleWidth() ) + w = visibleWidth(); + if ( h > visibleHeight() ) + h = visibleHeight(); + + if ( d->clipped_viewport ) { + // Translate clipper() to viewport() + x -= d->clipped_viewport->x(); + y -= d->clipped_viewport->y(); + } + + vp->repaint( x, y, w, h, erase ); +} + + +/*! + For backward-compatibility only. It is easier to use + drawContents(QPainter*,int,int,int,int). + + The default implementation translates the painter appropriately + and calls drawContents(QPainter*,int,int,int,int). See + drawContents() for an explanation of the parameters \a p, \a + offsetx, \a offsety, \a clipx, \a clipy, \a clipw and \a cliph. +*/ +void QScrollView::drawContentsOffset(QPainter* p, int offsetx, int offsety, int clipx, int clipy, int clipw, int cliph) +{ + p->translate(-offsetx,-offsety); + drawContents(p, clipx, clipy, clipw, cliph); +} + +/*! + \fn void QScrollView::drawContents(QPainter* p, int clipx, int clipy, int clipw, int cliph) + + Reimplement this function if you are viewing a drawing area rather + than a widget. + + The function should draw the rectangle (\a clipx, \a clipy, \a + clipw, \a cliph) of the contents using painter \a p. The clip + rectangle is in the scrollview's coordinates. + + For example: + \code + { + // Fill a 40000 by 50000 rectangle at (100000,150000) + + // Calculate the coordinates... + int x1 = 100000, y1 = 150000; + int x2 = x1+40000-1, y2 = y1+50000-1; + + // Clip the coordinates so X/Windows will not have problems... + if (x1 < clipx) x1=clipx; + if (y1 < clipy) y1=clipy; + if (x2 > clipx+clipw-1) x2=clipx+clipw-1; + if (y2 > clipy+cliph-1) y2=clipy+cliph-1; + + // Paint using the small coordinates... + if ( x2 >= x1 && y2 >= y1 ) + p->fillRect(x1, y1, x2-x1+1, y2-y1+1, red); + } + \endcode + + The clip rectangle and translation of the painter \a p is already + set appropriately. +*/ +void QScrollView::drawContents(QPainter*, int, int, int, int) +{ +} + + +/*! + \reimp +*/ +void QScrollView::frameChanged() +{ + // slight ugle-hack - the listview header needs readjusting when + // changing the frame + if (QListView *lv = ::qt_cast<QListView *>(this)) + lv->triggerUpdate(); + QFrame::frameChanged(); + updateScrollBars(); +} + + +/*! + Returns the viewport widget of the scrollview. This is the widget + containing the contents widget or which is the drawing area. +*/ +QWidget* QScrollView::viewport() const +{ + if ( d->clipped_viewport ) + return d->clipped_viewport; + return d->viewport; +} + +/*! + Returns the clipper widget. Contents in the scrollview are + ultimately clipped to be inside the clipper widget. + + You should not need to use this function. + + \sa visibleWidth(), visibleHeight() +*/ +QWidget* QScrollView::clipper() const +{ + return d->viewport; +} + +/*! + \property QScrollView::visibleWidth + \brief the horizontal amount of the content that is visible +*/ +int QScrollView::visibleWidth() const +{ + return clipper()->width(); +} + +/*! + \property QScrollView::visibleHeight + \brief the vertical amount of the content that is visible +*/ +int QScrollView::visibleHeight() const +{ + return clipper()->height(); +} + + +void QScrollView::changeFrameRect(const QRect& r) +{ + QRect oldr = frameRect(); + if (oldr != r) { + QRect cr = contentsRect(); + QRegion fr( frameRect() ); + fr = fr.subtract( contentsRect() ); + setFrameRect( r ); + if ( isVisible() ) { + cr = cr.intersect( contentsRect() ); + fr = fr.unite( frameRect() ); + fr = fr.subtract( cr ); + if ( !fr.isEmpty() ) + QApplication::postEvent( this, new QPaintEvent( fr, FALSE ) ); + } + } +} + + +/*! + Sets the margins around the scrolling area to \a left, \a top, \a + right and \a bottom. This is useful for applications such as + spreadsheets with "locked" rows and columns. The marginal space is + \e inside the frameRect() and is left blank; reimplement + drawFrame() or put widgets in the unused area. + + By default all margins are zero. + + \sa frameChanged() +*/ +void QScrollView::setMargins(int left, int top, int right, int bottom) +{ + if ( left == d->l_marg && + top == d->t_marg && + right == d->r_marg && + bottom == d->b_marg ) + return; + + d->l_marg = left; + d->t_marg = top; + d->r_marg = right; + d->b_marg = bottom; + updateScrollBars(); +} + + +/*! + Returns the left margin. + + \sa setMargins() +*/ +int QScrollView::leftMargin() const +{ + return d->l_marg; +} + + +/*! + Returns the top margin. + + \sa setMargins() +*/ +int QScrollView::topMargin() const +{ + return d->t_marg; +} + + +/*! + Returns the right margin. + + \sa setMargins() +*/ +int QScrollView::rightMargin() const +{ + return d->r_marg; +} + + +/*! + Returns the bottom margin. + + \sa setMargins() +*/ +int QScrollView::bottomMargin() const +{ + return d->b_marg; +} + +/*! + \reimp +*/ +bool QScrollView::focusNextPrevChild( bool next ) +{ + // Makes sure that the new focus widget is on-screen, if + // necessary by scrolling the scroll view. + + // first set things up for the scan + QFocusData *f = focusData(); + QWidget *startingPoint = f->home(); + QWidget *candidate = 0; + QWidget *w = next ? f->next() : f->prev(); + QSVChildRec *r; + extern bool qt_tab_all_widgets; + uint focus_flag = qt_tab_all_widgets ? TabFocus : StrongFocus; + + // then scan for a possible focus widget candidate + while( !candidate && w != startingPoint ) { + if ( w != startingPoint && + (w->focusPolicy() & focus_flag) == focus_flag + && w->isEnabled() &&!w->focusProxy() && w->isVisible() ) + candidate = w; + w = next ? f->next() : f->prev(); + } + + // if we could not find one, maybe super or parentWidget() can? + if ( !candidate ) + return QFrame::focusNextPrevChild( next ); + + // we've found one. + r = d->ancestorRec( candidate ); + if ( r && ( r->child == candidate || + candidate->isVisibleTo( r->child ) ) ) { + QPoint cp = r->child->mapToGlobal(QPoint(0,0)); + QPoint cr = candidate->mapToGlobal(QPoint(0,0)) - cp; + ensureVisible( r->x+cr.x()+candidate->width()/2, + r->y+cr.y()+candidate->height()/2, + candidate->width()/2, + candidate->height()/2 ); + } + + candidate->setFocus(); + return TRUE; +} + + + +/*! + When a large numbers of child widgets are in a scrollview, + especially if they are close together, the scrolling performance + can suffer greatly. If \a y is TRUE the scrollview will use an + extra widget to group child widgets. + + Note that you may only call enableClipper() prior to adding + widgets. + + For a full discussion, see this class's \link #enableclipper + detailed description\endlink. +*/ +void QScrollView::enableClipper(bool y) +{ + if ( !d->clipped_viewport == !y ) + return; + if ( d->children.count() ) + qFatal("May only call QScrollView::enableClipper() before adding widgets"); + if ( y ) { + d->clipped_viewport = new QClipperWidget(clipper(), "qt_clipped_viewport", d->flags); + d->clipped_viewport->setGeometry(-coord_limit/2,-coord_limit/2, + coord_limit,coord_limit); + d->clipped_viewport->setBackgroundMode( d->viewport->backgroundMode() ); + d->viewport->setBackgroundMode(NoBackground); // no exposures for this + d->viewport->removeEventFilter( this ); + d->clipped_viewport->installEventFilter( this ); + d->clipped_viewport->show(); + } else { + delete d->clipped_viewport; + d->clipped_viewport = 0; + } +} + +/*! + Sets the scrollview to have a static background if \a y is TRUE, + or a scrolling background if \a y is FALSE. By default, the + background is scrolling. + + Be aware that this mode is quite slow, as a full repaint of the + visible area has to be triggered on every contents move. + + \sa hasStaticBackground() +*/ +void QScrollView::setStaticBackground(bool y) +{ + d->static_bg = y; +} + +/*! + Returns TRUE if QScrollView uses a static background; otherwise + returns FALSE. + + \sa setStaticBackground() +*/ +bool QScrollView::hasStaticBackground() const +{ + return d->static_bg; +} + +/*! + \overload + + Returns the point \a p translated to a point on the viewport() + widget. +*/ +QPoint QScrollView::contentsToViewport( const QPoint& p ) const +{ + if ( d->clipped_viewport ) { + return QPoint( p.x() - d->contentsX() - d->clipped_viewport->x(), + p.y() - d->contentsY() - d->clipped_viewport->y() ); + } else { + return QPoint( p.x() - d->contentsX(), + p.y() - d->contentsY() ); + } +} + +/*! + \overload + + Returns the point on the viewport \a vp translated to a point in + the contents. +*/ +QPoint QScrollView::viewportToContents( const QPoint& vp ) const +{ + if ( d->clipped_viewport ) { + return QPoint( vp.x() + d->contentsX() + d->clipped_viewport->x(), + vp.y() + d->contentsY() + d->clipped_viewport->y() ); + } else { + return QPoint( vp.x() + d->contentsX(), + vp.y() + d->contentsY() ); + } +} + + +/*! + Translates a point (\a x, \a y) in the contents to a point (\a vx, + \a vy) on the viewport() widget. +*/ +void QScrollView::contentsToViewport( int x, int y, int& vx, int& vy ) const +{ + const QPoint v = contentsToViewport(QPoint(x,y)); + vx = v.x(); + vy = v.y(); +} + +/*! + Translates a point (\a vx, \a vy) on the viewport() widget to a + point (\a x, \a y) in the contents. +*/ +void QScrollView::viewportToContents( int vx, int vy, int& x, int& y ) const +{ + const QPoint c = viewportToContents(QPoint(vx,vy)); + x = c.x(); + y = c.y(); +} + +/*! + \reimp +*/ +QSize QScrollView::sizeHint() const +{ + if ( d->use_cached_size_hint && d->cachedSizeHint.isValid() ) + return d->cachedSizeHint; + + constPolish(); + int f = 2 * frameWidth(); + int h = fontMetrics().height(); + QSize sz( f, f ); + if ( d->policy > Manual ) { + QSVChildRec *r = d->children.first(); + if ( r ) { + QSize cs = r->child->sizeHint(); + if ( cs.isValid() ) + sz += cs.boundedTo( r->child->maximumSize() ); + else + sz += r->child->size(); + } + } else { + sz += QSize( d->contentsWidth(), contentsHeight() ); + } + if (d->vMode == AlwaysOn) + sz.setWidth(sz.width() + d->vbar->sizeHint().width()); + if (d->hMode == AlwaysOn) + sz.setHeight(sz.height() + d->hbar->sizeHint().height()); + return sz.expandedTo( QSize(12 * h, 8 * h) ) + .boundedTo( QSize(36 * h, 24 * h) ); +} + + +/*! + \reimp +*/ +QSize QScrollView::minimumSizeHint() const +{ + int h = fontMetrics().height(); + if ( h < 10 ) + h = 10; + int f = 2 * frameWidth(); + return QSize( (6 * h) + f, (4 * h) + f ); +} + + +/*! + \reimp + + (Implemented to get rid of a compiler warning.) +*/ +void QScrollView::drawContents( QPainter * ) +{ +} + +#ifndef QT_NO_DRAGANDDROP + +/*! + \internal +*/ +void QScrollView::startDragAutoScroll() +{ + if ( !d->autoscroll_timer.isActive() ) { + d->autoscroll_time = initialScrollTime; + d->autoscroll_accel = initialScrollAccel; + d->autoscroll_timer.start( d->autoscroll_time ); + } +} + + +/*! + \internal +*/ +void QScrollView::stopDragAutoScroll() +{ + d->autoscroll_timer.stop(); +} + + +/*! + \internal +*/ +void QScrollView::doDragAutoScroll() +{ + QPoint p = d->viewport->mapFromGlobal( QCursor::pos() ); + + if ( d->autoscroll_accel-- <= 0 && d->autoscroll_time ) { + d->autoscroll_accel = initialScrollAccel; + d->autoscroll_time--; + d->autoscroll_timer.start( d->autoscroll_time ); + } + int l = QMAX( 1, ( initialScrollTime- d->autoscroll_time ) ); + + int dx = 0, dy = 0; + if ( p.y() < autoscroll_margin ) { + dy = -l; + } else if ( p.y() > visibleHeight() - autoscroll_margin ) { + dy = +l; + } + if ( p.x() < autoscroll_margin ) { + dx = -l; + } else if ( p.x() > visibleWidth() - autoscroll_margin ) { + dx = +l; + } + if ( dx || dy ) { + scrollBy(dx,dy); + } else { + stopDragAutoScroll(); + } +} + + +/*! + \property QScrollView::dragAutoScroll + \brief whether autoscrolling in drag move events is enabled + + If this property is set to TRUE (the default), the QScrollView + automatically scrolls the contents in drag move events if the user + moves the cursor close to a border of the view. Of course this + works only if the viewport accepts drops. Specifying FALSE + disables this autoscroll feature. + + \warning Enabling this property might not be enough to + effectively turn on autoscrolling. If you put a custom widget in + the QScrollView, you might need to call QDragEvent::ignore() on + the event in the dragEnterEvent() and dragMoveEvent() + reimplementations. +*/ + +void QScrollView::setDragAutoScroll( bool b ) +{ + d->drag_autoscroll = b; +} + +bool QScrollView::dragAutoScroll() const +{ + return d->drag_autoscroll; +} + +#endif // QT_NO_DRAGANDDROP + +/*!\internal + */ +void QScrollView::setCachedSizeHint( const QSize &sh ) const +{ + if ( isVisible() && !d->cachedSizeHint.isValid() ) + d->cachedSizeHint = sh; +} + +/*!\internal + */ +void QScrollView::disableSizeHintCaching() +{ + d->use_cached_size_hint = FALSE; +} + +/*!\internal + */ +QSize QScrollView::cachedSizeHint() const +{ + return d->use_cached_size_hint ? d->cachedSizeHint : QSize(); +} + +#endif // QT_NO_SCROLLVIEW diff --git a/src/widgets/qscrollview.h b/src/widgets/qscrollview.h new file mode 100644 index 0000000..e2ace97 --- /dev/null +++ b/src/widgets/qscrollview.h @@ -0,0 +1,269 @@ +/**************************************************************************** +** +** Definition of QScrollView class +** +** Created : 970523 +** +** 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. +** +**********************************************************************/ +#ifndef QSCROLLVIEW_H +#define QSCROLLVIEW_H + +#ifndef QT_H +#include "qframe.h" +#include "qscrollbar.h" +#endif // QT_H + +#ifndef QT_NO_SCROLLVIEW + +class QScrollViewData; + +class Q_EXPORT QScrollView : public QFrame +{ + Q_OBJECT + Q_ENUMS( ResizePolicy ScrollBarMode ) + Q_PROPERTY( ResizePolicy resizePolicy READ resizePolicy WRITE setResizePolicy ) + Q_PROPERTY( ScrollBarMode vScrollBarMode READ vScrollBarMode WRITE setVScrollBarMode ) + Q_PROPERTY( ScrollBarMode hScrollBarMode READ hScrollBarMode WRITE setHScrollBarMode ) + Q_PROPERTY( int visibleWidth READ visibleWidth ) + Q_PROPERTY( int visibleHeight READ visibleHeight ) + Q_PROPERTY( int contentsWidth READ contentsWidth ) + Q_PROPERTY( int contentsHeight READ contentsHeight ) + Q_PROPERTY( int contentsX READ contentsX ) + Q_PROPERTY( int contentsY READ contentsY ) +#ifndef QT_NO_DRAGANDDROP + Q_PROPERTY( bool dragAutoScroll READ dragAutoScroll WRITE setDragAutoScroll ) +#endif + +public: + QScrollView(QWidget* parent=0, const char* name=0, WFlags f=0); + ~QScrollView(); + + enum ResizePolicy { Default, Manual, AutoOne, AutoOneFit }; + virtual void setResizePolicy( ResizePolicy ); + ResizePolicy resizePolicy() const; + + void styleChange( QStyle & ); + void removeChild(QWidget* child); + virtual void addChild( QWidget* child, int x=0, int y=0 ); + virtual void moveChild( QWidget* child, int x, int y ); + int childX(QWidget* child); + int childY(QWidget* child); + bool childIsVisible(QWidget* child) { return child->isVisible(); } // obsolete functions + void showChild(QWidget* child, bool yes=TRUE) { + if ( yes ) + child->show(); + else + child->hide(); + } + + enum ScrollBarMode { Auto, AlwaysOff, AlwaysOn }; + + ScrollBarMode vScrollBarMode() const; + virtual void setVScrollBarMode( ScrollBarMode ); + + ScrollBarMode hScrollBarMode() const; + virtual void setHScrollBarMode( ScrollBarMode ); + + QWidget* cornerWidget() const; + virtual void setCornerWidget(QWidget*); + + // ### 4.0: Consider providing a factory function for scrollbars + // (e.g. make the two following functions virtual) + QScrollBar* horizontalScrollBar() const; + QScrollBar* verticalScrollBar() const; + QWidget* viewport() const; + QWidget* clipper() const; + + int visibleWidth() const; + int visibleHeight() const; + + int contentsWidth() const; + int contentsHeight() const; + int contentsX() const; + int contentsY() const; + + void resize( int w, int h ); + void resize( const QSize& ); + void show(); + + void updateContents( int x, int y, int w, int h ); + void updateContents( const QRect& r ); + void updateContents(); + void repaintContents( int x, int y, int w, int h, bool erase=TRUE ); + void repaintContents( const QRect& r, bool erase=TRUE ); + void repaintContents( bool erase=TRUE ); + void contentsToViewport( int x, int y, int& vx, int& vy ) const; + void viewportToContents( int vx, int vy, int& x, int& y ) const; + QPoint contentsToViewport( const QPoint& ) const; + QPoint viewportToContents( const QPoint& ) const; + void enableClipper( bool y ); + + void setStaticBackground( bool y ); + bool hasStaticBackground() const; + + QSize viewportSize( int, int ) const; + QSize sizeHint() const; + QSize minimumSizeHint() const; + + void removeChild(QObject* child); + + bool isHorizontalSliderPressed(); + bool isVerticalSliderPressed(); + +#ifndef QT_NO_DRAGANDDROP + virtual void setDragAutoScroll( bool b ); + bool dragAutoScroll() const; +#endif + +signals: + void contentsMoving(int x, int y); + void horizontalSliderPressed(); + void horizontalSliderReleased(); + void verticalSliderPressed(); + void verticalSliderReleased(); + +public slots: + virtual void resizeContents( int w, int h ); + void scrollBy( int dx, int dy ); + virtual void setContentsPos( int x, int y ); + void ensureVisible(int x, int y); + void ensureVisible(int x, int y, int xmargin, int ymargin); + void center(int x, int y); + void center(int x, int y, float xmargin, float ymargin); + + void updateScrollBars(); // ### virtual in 4.0 + void setEnabled( bool enable ); + +protected: + virtual void drawContents(QPainter*, int cx, int cy, int cw, int ch); + virtual void drawContentsOffset(QPainter*, int ox, int oy, + int cx, int cy, int cw, int ch); + + + virtual void contentsMousePressEvent( QMouseEvent* ); + virtual void contentsMouseReleaseEvent( QMouseEvent* ); + virtual void contentsMouseDoubleClickEvent( QMouseEvent* ); + virtual void contentsMouseMoveEvent( QMouseEvent* ); +#ifndef QT_NO_DRAGANDDROP + virtual void contentsDragEnterEvent( QDragEnterEvent * ); + virtual void contentsDragMoveEvent( QDragMoveEvent * ); + virtual void contentsDragLeaveEvent( QDragLeaveEvent * ); + virtual void contentsDropEvent( QDropEvent * ); +#endif +#ifndef QT_NO_WHEELEVENT + virtual void contentsWheelEvent( QWheelEvent * ); +#endif + virtual void contentsContextMenuEvent( QContextMenuEvent * ); + + + virtual void viewportPaintEvent( QPaintEvent* ); + virtual void viewportResizeEvent( QResizeEvent* ); + virtual void viewportMousePressEvent( QMouseEvent* ); + virtual void viewportMouseReleaseEvent( QMouseEvent* ); + virtual void viewportMouseDoubleClickEvent( QMouseEvent* ); + virtual void viewportMouseMoveEvent( QMouseEvent* ); +#ifndef QT_NO_DRAGANDDROP + virtual void viewportDragEnterEvent( QDragEnterEvent * ); + virtual void viewportDragMoveEvent( QDragMoveEvent * ); + virtual void viewportDragLeaveEvent( QDragLeaveEvent * ); + virtual void viewportDropEvent( QDropEvent * ); +#endif +#ifndef QT_NO_WHEELEVENT + virtual void viewportWheelEvent( QWheelEvent * ); +#endif + virtual void viewportContextMenuEvent( QContextMenuEvent * ); + + void frameChanged(); + + virtual void setMargins(int left, int top, int right, int bottom); + int leftMargin() const; + int topMargin() const; + int rightMargin() const; + int bottomMargin() const; + + bool focusNextPrevChild( bool next ); + + virtual void setHBarGeometry(QScrollBar& hbar, int x, int y, int w, int h); + virtual void setVBarGeometry(QScrollBar& vbar, int x, int y, int w, int h); + + void resizeEvent(QResizeEvent*); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseDoubleClickEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); +#ifndef QT_NO_WHEELEVENT + void wheelEvent( QWheelEvent * ); +#endif + void contextMenuEvent( QContextMenuEvent * ); + bool eventFilter( QObject *, QEvent *e ); + + void setCachedSizeHint( const QSize &sh ) const; + QSize cachedSizeHint() const; + void fontChange( const QFont & ); + +private: + void drawContents( QPainter* ); + void moveContents(int x, int y); + + QScrollViewData* d; + +private slots: + void hslide(int); + void vslide(int); + void hbarIsPressed(); + void hbarIsReleased(); + void vbarIsPressed(); + void vbarIsReleased(); +#ifndef QT_NO_DRAGANDDROP + void doDragAutoScroll(); + void startDragAutoScroll(); + void stopDragAutoScroll(); +#endif + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QScrollView( const QScrollView & ); + QScrollView &operator=( const QScrollView & ); +#endif + void changeFrameRect(const QRect&); + +public: + void disableSizeHintCaching(); + +}; + +#endif // QT_NO_SCROLLVIEW + +#endif // QSCROLLVIEW_H diff --git a/src/widgets/qslider.cpp b/src/widgets/qslider.cpp new file mode 100644 index 0000000..1573294 --- /dev/null +++ b/src/widgets/qslider.cpp @@ -0,0 +1,921 @@ +/**************************************************************************** +** +** Implementation of QSlider class +** +** Created : 961019 +** +** 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 "qslider.h" +#ifndef QT_NO_SLIDER +#include "qpainter.h" +#include "qdrawutil.h" +#include "qtimer.h" +#include "qbitmap.h" +#include "qapplication.h" +#include "qstyle.h" +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif + +static const int thresholdTime = 300; +static const int repeatTime = 100; + +struct QSliderPrivate +{ + // ### move these to QSlider in Qt 4.0 + int sliderStartVal; + QSliderPrivate() : sliderStartVal( 0 ) { } +}; + + +/*! + \class QSlider + \brief The QSlider widget provides a vertical or horizontal slider. + + \ingroup basic + \mainclass + + The slider is the classic widget for controlling a bounded value. + It lets the user move a slider along a horizontal or vertical + groove and translates the slider's position into an integer value + within the legal range. + + QSlider inherits QRangeControl, which provides the "integer" side + of the slider. setRange() and value() are likely to be used by + practically all slider users; see the \l QRangeControl + documentation for information about the many other functions that + class provides. + + The main functions offered by the slider itself are tickmark and + orientation control; you can use setTickmarks() to indicate where + you want the tickmarks to be, setTickInterval() to indicate how + many of them you want and setOrientation() to indicate whether the + slider is to be horizontal or vertical. + + A slider accepts focus on Tab and uses the mouse wheel and a + suitable keyboard interface. + + <img src=qslider-m.png> <img src=qslider-w.png> + + \important setRange + + \sa QScrollBar QSpinBox + \link guibooks.html#fowler GUI Design Handbook: Slider\endlink +*/ + + +/*! + \enum QSlider::TickSetting + + This enum specifies where the tickmarks are to be drawn relative + to the slider's groove and the handle the user moves. + + \value NoMarks do not draw any tickmarks. + \value Both draw tickmarks on both sides of the groove. + \value Above draw tickmarks above the (horizontal) slider + \value Below draw tickmarks below the (horizontal) slider + \value Left draw tickmarks to the left of the (vertical) slider + \value Right draw tickmarks to the right of the (vertical) slider +*/ + + +/*! + Constructs a vertical slider. + + The \a parent and \a name arguments are sent on to the QWidget + constructor. +*/ + +QSlider::QSlider( QWidget *parent, const char *name ) + : QWidget( parent, name ) +{ + orient = Vertical; + init(); +} + +/*! + Constructs a slider. + + The \a orientation must be \l Qt::Vertical or \l Qt::Horizontal. + + The \a parent and \a name arguments are sent on to the QWidget + constructor. +*/ + +QSlider::QSlider( Orientation orientation, QWidget *parent, const char *name ) + : QWidget( parent, name ) +{ + orient = orientation; + init(); +} + +/*! + Constructs a slider whose value can never be smaller than \a + minValue or greater than \a maxValue, whose page step size is \a + pageStep and whose value is initially \a value (which is + guaranteed to be in range using bound()). + + If \a orientation is \c Qt::Vertical the slider is vertical and if it + is \c Qt::Horizontal the slider is horizontal. + + The \a parent and \a name arguments are sent on to the QWidget + constructor. +*/ + +QSlider::QSlider( int minValue, int maxValue, int pageStep, + int value, Orientation orientation, + QWidget *parent, const char *name ) + : QWidget( parent, name ), + QRangeControl( minValue, maxValue, 1, pageStep, value ) +{ + orient = orientation; + init(); + sliderVal = value; +} + +/*! + Destructor. +*/ +QSlider::~QSlider() +{ + delete d; +} + +void QSlider::init() +{ + d = new QSliderPrivate; + timer = 0; + sliderPos = 0; + sliderVal = 0; + clickOffset = 0; + state = Idle; + track = TRUE; + ticks = NoMarks; + tickInt = 0; + setFocusPolicy( TabFocus ); + initTicks(); + + QSizePolicy sp( QSizePolicy::Expanding, QSizePolicy::Fixed ); + if ( orient == Vertical ) + sp.transpose(); + setSizePolicy( sp ); + clearWState( WState_OwnSizePolicy ); +} + + +/* + Does what's needed when someone changes the tickmark status. +*/ + +void QSlider::initTicks() +{ + tickOffset = style().pixelMetric( QStyle::PM_SliderTickmarkOffset, this ); +} + + +/*! + \property QSlider::tracking + \brief whether slider tracking is enabled + + If tracking is enabled (the default), the slider emits the + valueChanged() signal whenever the slider is being dragged. If + tracking is disabled, the slider emits the valueChanged() signal + when the user releases the mouse button (unless the value happens + to be the same as before). +*/ + +void QSlider::setTracking( bool enable ) +{ + track = enable; +} + + +/*! + \fn void QSlider::valueChanged( int value ) + + This signal is emitted when the slider value is changed, with the + new slider \a value as its argument. +*/ + +/*! + \fn void QSlider::sliderPressed() + + This signal is emitted when the user presses the slider with the + mouse. +*/ + +/*! + \fn void QSlider::sliderMoved( int value ) + + This signal is emitted when the slider is dragged, with the new + slider \a value as its argument. +*/ + +/*! + \fn void QSlider::sliderReleased() + + This signal is emitted when the user releases the slider with the mouse. +*/ + +/* + Calculates slider position corresponding to value \a v. +*/ + +int QSlider::positionFromValue( int v ) const +{ + int a = available(); + int x = QRangeControl::positionFromValue( v, a ); + if ( orient == Horizontal && QApplication::reverseLayout() ) + x = a - x; + return x; +} + +/* + Returns the available space in which the slider can move. +*/ + +int QSlider::available() const +{ + return style().pixelMetric( QStyle::PM_SliderSpaceAvailable, this ); +} + +/* + Calculates a value corresponding to slider position \a p. +*/ + +int QSlider::valueFromPosition( int p ) const +{ + int a = available(); + int x = QRangeControl::valueFromPosition( p, a ); + if ( orient == Horizontal && QApplication::reverseLayout() ) + x = maxValue() + minValue() - x; + return x; +} + +/*! + Implements the virtual QRangeControl function. +*/ + +void QSlider::rangeChange() +{ + int newPos = positionFromValue( value() ); + if ( newPos != sliderPos ) { + reallyMoveSlider( newPos ); + } +} + +/*! + Implements the virtual QRangeControl function. +*/ + +void QSlider::valueChange() +{ + if ( sliderVal != value() ) { + int newPos = positionFromValue( value() ); + sliderVal = value(); + reallyMoveSlider( newPos ); + } + emit valueChanged(value()); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged ); +#endif +} + + +/*! + \reimp +*/ +void QSlider::resizeEvent( QResizeEvent * ) +{ + rangeChange(); + initTicks(); +} + + +/*! + Reimplements the virtual function QWidget::setPalette(). + + Sets the background color to the mid color for Motif style sliders + using palette \a p. +*/ + +void QSlider::setPalette( const QPalette &p ) +{ + QWidget::setPalette( p ); +} + + + +/*! + \property QSlider::orientation + \brief the slider's orientation + + The orientation must be \l Qt::Vertical (the default) or \l + Qt::Horizontal. +*/ + +void QSlider::setOrientation( Orientation orientation ) +{ + if ( orientation == orient ) + return; + + if ( !testWState( WState_OwnSizePolicy ) ) { + QSizePolicy sp = sizePolicy(); + sp.transpose(); + setSizePolicy( sp ); + clearWState( WState_OwnSizePolicy ); + } + + orient = orientation; + + rangeChange(); + update(); +} + +/*! + \fn int QSlider::sliderStart() const + + Returns the start position of the slider. +*/ + + +/*! + Returns the slider handle rectangle. (This is the visual marker + that the user can move.) +*/ + +QRect QSlider::sliderRect() const +{ + return style().querySubControlMetrics( QStyle::CC_Slider, this, + QStyle::SC_SliderHandle ); +} + +/* + Performs the actual moving of the slider. +*/ + +void QSlider::reallyMoveSlider( int newPos ) +{ + QRegion oldR(sliderRect()); + sliderPos = newPos; + QRegion newR(sliderRect()); + + /* just the one repaint if no background */ + if (backgroundMode() == NoBackground) + repaint(newR | oldR, FALSE); + else { + repaint(oldR.subtract(newR)); + repaint(newR, FALSE); + } +} + + +/*! + \reimp +*/ +void QSlider::paintEvent( QPaintEvent * ) +{ + QPainter p( this ); + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (hasFocus()) + flags |= QStyle::Style_HasFocus; + + QStyle::SCFlags sub = QStyle::SC_SliderGroove | QStyle::SC_SliderHandle; + if ( tickmarks() != NoMarks ) + sub |= QStyle::SC_SliderTickmarks; + + style().drawComplexControl( QStyle::CC_Slider, &p, this, rect(), colorGroup(), + flags, sub, state == Dragging ? QStyle::SC_SliderHandle : QStyle::SC_None ); +} + + +/*! + \reimp +*/ +void QSlider::mousePressEvent( QMouseEvent *e ) +{ + int slideLength = style().pixelMetric( QStyle::PM_SliderLength, this ); + resetState(); + d->sliderStartVal = sliderVal; + QRect r = sliderRect(); + + if ( e->button() == RightButton ) + return; + + if ( r.contains( e->pos() ) ) { + state = Dragging; + clickOffset = (QCOORD)( goodPart( e->pos() ) - sliderPos ); + emit sliderPressed(); + } else if ( e->button() == MidButton ) { + int pos = goodPart( e->pos() ); + moveSlider( pos - slideLength / 2 ); + state = Dragging; + clickOffset = slideLength / 2; + } else if ( orient == Horizontal && e->pos().x() < r.left() //### goodPart + || orient == Vertical && e->pos().y() < r.top() ) { + if ( orient == Horizontal && QApplication::reverseLayout() ) { + state = TimingUp; + addPage(); + } else { + state = TimingDown; + subtractPage(); + } + if ( !timer ) + timer = new QTimer( this ); + connect( timer, SIGNAL(timeout()), SLOT(repeatTimeout()) ); + timer->start( thresholdTime, TRUE ); + } else if ( orient == Horizontal && e->pos().x() > r.right() //### goodPart + || orient == Vertical && e->pos().y() > r.bottom() ) { + if ( orient == Horizontal && QApplication::reverseLayout() ) { + state = TimingDown; + subtractPage(); + } else { + state = TimingUp; + addPage(); + } + if ( !timer ) + timer = new QTimer( this ); + connect( timer, SIGNAL(timeout()), SLOT(repeatTimeout()) ); + timer->start( thresholdTime, TRUE ); + } + update( sliderRect() ); +} + +/*! + \reimp +*/ +void QSlider::mouseMoveEvent( QMouseEvent *e ) +{ + if ( state != Dragging ) + return; + + QRect r = rect(); + int m = style().pixelMetric( QStyle::PM_MaximumDragDistance, + this ); + if ( m >= 0 ) { + if ( orientation() == Horizontal ) + r.setRect( r.x() - m, r.y() - 2*m/3, + r.width() + 2*m, r.height() + 3*m ); + else + r.setRect( r.x() - 2*m/3, r.y() - m, + r.width() + 3*m, r.height() + 2*m ); + if ( !r.contains( e->pos() ) ) { + moveSlider( positionFromValue(d->sliderStartVal) ); + return; + } + } + + int pos = goodPart( e->pos() ); + moveSlider( pos - clickOffset ); +} + +/*! + \reimp +*/ +#ifndef QT_NO_WHEELEVENT +void QSlider::wheelEvent( QWheelEvent * e ) +{ + if ( e->orientation() != orientation() && !rect().contains(e->pos()) ) + return; + + static float offset = 0; + static QSlider* offset_owner = 0; + if (offset_owner != this){ + offset_owner = this; + offset = 0; + } + offset += -e->delta()*QMAX(pageStep(),lineStep())/120; + if (QABS(offset)<1) + return; + setValue( value() + int(offset) ); + offset -= int(offset); + e->accept(); +} +#endif + +/*! + \reimp +*/ +void QSlider::mouseReleaseEvent( QMouseEvent * ) +{ + resetState(); + update( sliderRect() ); +} + +/*! + \reimp +*/ +void QSlider::focusInEvent( QFocusEvent * e) +{ + QWidget::focusInEvent( e ); +} + +/*! + \reimp +*/ +void QSlider::focusOutEvent( QFocusEvent * e ) +{ + QWidget::focusOutEvent( e ); +} + +/*! + Moves the left (or top) edge of the slider to position \a pos. The + slider is actually moved to the step position nearest the given \a + pos. +*/ + +void QSlider::moveSlider( int pos ) +{ + int a = available(); + int newPos = QMIN( a, QMAX( 0, pos ) ); + int newVal = valueFromPosition( newPos ); + if (style().styleHint(QStyle::SH_Slider_SnapToValue, this)) + newPos = positionFromValue( newVal ); + if ( sliderPos != newPos ) + reallyMoveSlider( newPos ); + if ( sliderVal != newVal ) { + sliderVal = newVal; + emit sliderMoved( sliderVal ); + } + if ( tracking() && sliderVal != value() ) + setValue( sliderVal ); + +} + + +/* + Resets all state information and stops the timer. +*/ + +void QSlider::resetState() +{ + if ( timer ) { + timer->stop(); + timer->disconnect(); + } + switch ( state ) { + case TimingUp: + case TimingDown: + break; + case Dragging: { + setValue( valueFromPosition( sliderPos ) ); + emit sliderReleased(); + break; + } + case Idle: + break; + default: + qWarning("QSlider: (%s) in wrong state", name( "unnamed" ) ); + } + state = Idle; +} + + +/*! + \reimp +*/ +void QSlider::keyPressEvent( QKeyEvent *e ) +{ + bool sloppy = bool(style().styleHint(QStyle::SH_Slider_SloppyKeyEvents, this)); + switch ( e->key() ) { + case Key_Left: + if ( sloppy || orient == Horizontal ) { + if (QApplication::reverseLayout()) + addLine(); + else + subtractLine(); + } + break; + case Key_Right: + if ( sloppy || orient == Horizontal ) { + if (QApplication::reverseLayout()) + subtractLine(); + else + addLine(); + } + break; + case Key_Up: + if ( sloppy || orient == Vertical ) + subtractLine(); + break; + case Key_Down: + if ( sloppy || orient == Vertical ) + addLine(); + break; + case Key_Prior: + subtractPage(); + break; + case Key_Next: + addPage(); + break; + case Key_Home: + setValue( minValue() ); + break; + case Key_End: + setValue( maxValue() ); + break; + default: + e->ignore(); + return; + } +} + +void QSlider::setValue( int value ) +{ + QRangeControl::setValue( value ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged ); +#endif +} + + +/*! \reimp +*/ + +void QSlider::addLine() +{ + QRangeControl::addLine(); +} + +/*! \reimp +*/ + +void QSlider::subtractLine() +{ + QRangeControl::subtractLine(); +} + +/*! + Moves the slider one pageStep() up or right. +*/ + +void QSlider::addStep() +{ + addPage(); +} + + +/*! + Moves the slider one pageStep() down or left. +*/ + +void QSlider::subtractStep() +{ + subtractPage(); +} + + +/* + Waits for autorepeat. +*/ + +void QSlider::repeatTimeout() +{ + Q_ASSERT( timer ); + timer->disconnect(); + if ( state == TimingDown ) + connect( timer, SIGNAL(timeout()), SLOT(subtractStep()) ); + else if ( state == TimingUp ) + connect( timer, SIGNAL(timeout()), SLOT(addStep()) ); + timer->start( repeatTime, FALSE ); +} + + +/* + Returns the relevant dimension of \a p. +*/ + +int QSlider::goodPart( const QPoint &p ) const +{ + return (orient == Horizontal) ? p.x() : p.y(); +} + +/*! + \reimp +*/ +QSize QSlider::sizeHint() const +{ + constPolish(); + const int length = 84, tickSpace = 5; + int thick = style().pixelMetric( QStyle::PM_SliderThickness, this ); + if ( ticks & Above ) + thick += tickSpace; + if ( ticks & Below ) + thick += tickSpace; + int w = thick, h = length; + if ( orient == Horizontal ) { + w = length; + h = thick; + } + return (style().sizeFromContents(QStyle::CT_Slider, this, + QSize(w, h)).expandedTo(QApplication::globalStrut())); +} + + + +/*! + \reimp +*/ + +QSize QSlider::minimumSizeHint() const +{ + QSize s = sizeHint(); + int length = style().pixelMetric(QStyle::PM_SliderLength, this); + if ( orient == Horizontal ) + s.setWidth( length ); + else + s.setHeight( length ); + + return s; +} + +/*! \fn void QSlider::setSizePolicy( QSizePolicy::SizeType, QSizePolicy::SizeType, bool ) + \reimp +*/ + +/*! \reimp */ +void QSlider::setSizePolicy( QSizePolicy sp ) +{ + // ## remove 4.0 + QWidget::setSizePolicy( sp ); +} + +/*! \reimp */ +QSizePolicy QSlider::sizePolicy() const +{ + // ### 4.0 remove this reimplementation + return QWidget::sizePolicy(); +} + +/*! + \property QSlider::tickmarks + \brief the tickmark settings for this slider + + The valid values are in \l{QSlider::TickSetting}. The default is + \c NoMarks. + + \sa tickInterval +*/ + +void QSlider::setTickmarks( TickSetting s ) +{ + ticks = s; + initTicks(); + update(); +} + + +/*! + \property QSlider::tickInterval + \brief the interval between tickmarks + + This is a value interval, not a pixel interval. If it is 0, the + slider will choose between lineStep() and pageStep(). The initial + value of tickInterval is 0. + + \sa QRangeControl::lineStep(), QRangeControl::pageStep() +*/ + +void QSlider::setTickInterval( int i ) +{ + tickInt = QMAX( 0, i ); + update(); +} + + +/*! + \reimp +*/ +void QSlider::styleChange( QStyle& old ) +{ + QWidget::styleChange( old ); +} + +/*! + \property QSlider::minValue + \brief the current minimum value of the slider + + When setting this property, the \l QSlider::maxValue is adjusted, + if necessary, to ensure that the range remains valid. + + \sa setRange() +*/ +int QSlider::minValue() const +{ + return QRangeControl::minValue(); +} + +/*! + \property QSlider::maxValue + \brief the current maximum value of the slider + + When setting this property, the \l QSlider::minValue is adjusted, + if necessary, to ensure that the range remains valid. + + \sa setRange() +*/ +int QSlider::maxValue() const +{ + return QRangeControl::maxValue(); +} + +void QSlider::setMinValue( int minVal ) +{ + QRangeControl::setMinValue( minVal ); +} + +void QSlider::setMaxValue( int maxVal ) +{ + QRangeControl::setMaxValue( maxVal ); +} + +/*! + \property QSlider::lineStep + \brief the current line step + + When setting lineStep, the virtual stepChange() function will be + called if the new line step is different from the previous + setting. + + \sa setSteps() QRangeControl::pageStep() setRange() +*/ +int QSlider::lineStep() const +{ + return QRangeControl::lineStep(); +} + +/*! + \property QSlider::pageStep + \brief the current page step + + When setting pageStep, the virtual stepChange() function will be + called if the new page step is different from the previous + setting. + + \sa QRangeControl::setSteps() setLineStep() setRange() +*/ + +int QSlider::pageStep() const +{ + return QRangeControl::pageStep(); +} + +void QSlider::setLineStep( int i ) +{ + setSteps( i, pageStep() ); +} + +void QSlider::setPageStep( int i ) +{ + setSteps( lineStep(), i ); +} + +/*! + \property QSlider::value + \brief the current slider value + + \sa QRangeControl::value() prevValue() +*/ + +int QSlider::value() const +{ + return QRangeControl::value(); +} + +#endif diff --git a/src/widgets/qslider.h b/src/widgets/qslider.h new file mode 100644 index 0000000..e51b550 --- /dev/null +++ b/src/widgets/qslider.h @@ -0,0 +1,199 @@ +/**************************************************************************** +** +** Definition of QSlider class +** +** Created : 961019 +** +** 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. +** +**********************************************************************/ + +#ifndef QSLIDER_H +#define QSLIDER_H + +#ifndef QT_H +#include "qwidget.h" +#include "qrangecontrol.h" +#endif // QT_H + +#ifndef QT_NO_SLIDER + +struct QSliderPrivate; + +class QTimer; + +class Q_EXPORT QSlider : public QWidget, public QRangeControl +{ + Q_OBJECT + Q_ENUMS( TickSetting ) + Q_PROPERTY( int minValue READ minValue WRITE setMinValue ) + Q_PROPERTY( int maxValue READ maxValue WRITE setMaxValue ) + Q_PROPERTY( int lineStep READ lineStep WRITE setLineStep ) + Q_PROPERTY( int pageStep READ pageStep WRITE setPageStep ) + Q_PROPERTY( int value READ value WRITE setValue ) + Q_PROPERTY( bool tracking READ tracking WRITE setTracking ) + Q_PROPERTY( Orientation orientation READ orientation WRITE setOrientation ) + Q_PROPERTY( TickSetting tickmarks READ tickmarks WRITE setTickmarks ) + Q_PROPERTY( int tickInterval READ tickInterval WRITE setTickInterval ) + +public: + enum TickSetting { NoMarks = 0, Above = 1, Left = Above, + Below = 2, Right = Below, Both = 3 }; + + QSlider( QWidget *parent, const char* name = 0 ); + QSlider( Orientation, QWidget *parent, const char* name = 0 ); + QSlider( int minValue, int maxValue, int pageStep, int value, Orientation, + QWidget *parent, const char* name = 0 ); + ~QSlider(); + + virtual void setOrientation( Orientation ); + Orientation orientation() const; + virtual void setTracking( bool enable ); + bool tracking() const; + virtual void setPalette( const QPalette & ); + + int sliderStart() const; + QRect sliderRect() const; + QSize sizeHint() const; + void setSizePolicy( QSizePolicy sp ); + void setSizePolicy( QSizePolicy::SizeType hor, QSizePolicy::SizeType ver, bool hfw = FALSE ); + + QSizePolicy sizePolicy() const; + QSize minimumSizeHint() const; + + virtual void setTickmarks( TickSetting ); + TickSetting tickmarks() const { return ticks; } + + virtual void setTickInterval( int ); + int tickInterval() const { return tickInt; } + + int minValue() const; + int maxValue() const; + void setMinValue( int ); + void setMaxValue( int ); + int lineStep() const; + int pageStep() const; + void setLineStep( int ); + void setPageStep( int ); + int value() const; + +public slots: + virtual void setValue( int ); + void addStep(); + void subtractStep(); + void addLine(); + void subtractLine(); + +signals: + void valueChanged( int value ); + void sliderPressed(); + void sliderMoved( int value ); + void sliderReleased(); + +protected: + void resizeEvent( QResizeEvent * ); + void paintEvent( QPaintEvent * ); + + void keyPressEvent( QKeyEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); +#ifndef QT_NO_WHEELEVENT + void wheelEvent( QWheelEvent * ); +#endif + void focusInEvent( QFocusEvent *e ); + void focusOutEvent( QFocusEvent *e ); + + void styleChange( QStyle& ); + + void valueChange(); + void rangeChange(); + +private slots: + void repeatTimeout(); + +private: + enum State { Idle, Dragging, TimingUp, TimingDown }; + + void init(); + int positionFromValue( int ) const; + int valueFromPosition( int ) const; + void moveSlider( int ); + void reallyMoveSlider( int ); + void resetState(); + int available() const; + int goodPart( const QPoint& ) const; + void initTicks(); + + QSliderPrivate *d; + QTimer *timer; + QCOORD sliderPos; + int sliderVal; + QCOORD clickOffset; + State state; + bool track; + QCOORD tickOffset; + TickSetting ticks; + int tickInt; + Orientation orient; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QSlider( const QSlider & ); + QSlider &operator=( const QSlider & ); +#endif +}; + +inline bool QSlider::tracking() const +{ + return track; +} + +inline QSlider::Orientation QSlider::orientation() const +{ + return orient; +} + +inline int QSlider::sliderStart() const +{ + return sliderPos; +} + +inline void QSlider::setSizePolicy( QSizePolicy::SizeType hor, QSizePolicy::SizeType ver, bool hfw ) +{ + QWidget::setSizePolicy( hor, ver, hfw ); +} + +#endif // QT_NO_SLIDER + +#endif // QSLIDER_H diff --git a/src/widgets/qspinbox.cpp b/src/widgets/qspinbox.cpp new file mode 100644 index 0000000..76b5906 --- /dev/null +++ b/src/widgets/qspinbox.cpp @@ -0,0 +1,1116 @@ +/**************************************************************************** +** +** Implementation of QSpinBox widget class +** +** Created : 970101 +** +** 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 "qspinbox.h" +#ifndef QT_NO_SPINBOX + +#include "qcursor.h" +#include "qpushbutton.h" +#include "qpainter.h" +#include "qbitmap.h" +#include "qlineedit.h" +#include "qvalidator.h" +#include "qpixmapcache.h" +#include "qapplication.h" +#include "qstyle.h" +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif + +static bool sumOutOfRange(int current, int add) +{ + if (add > 0 && INT_MAX - add < current) { + return true; + } + if (add < 0 && INT_MIN - add > current) { + return true; + } + return false; +} + +class QSpinBoxPrivate +{ +public: + QSpinBoxPrivate() {} + QSpinWidget* controls; + uint selreq : 1; +}; + +class QSpinBoxValidator : public QIntValidator +{ +public: + QSpinBoxValidator( QSpinBox *sb, const char *name ) + : QIntValidator( sb, name ), spinBox( sb ) { } + + virtual State validate( QString& str, int& pos ) const; + +private: + QSpinBox *spinBox; +}; + +QValidator::State QSpinBoxValidator::validate( QString& str, int& pos ) const +{ + QString pref = spinBox->prefix(); + QString suff = spinBox->suffix(); + QString suffStriped = suff.stripWhiteSpace(); + uint overhead = pref.length() + suff.length(); + State state = Invalid; + + ((QIntValidator *) this)->setRange( spinBox->minValue(), + spinBox->maxValue() ); + if ( overhead == 0 ) { + state = QIntValidator::validate( str, pos ); + } else { + bool stripedVersion = FALSE; + if ( str.length() >= overhead && str.startsWith(pref) + && (str.endsWith(suff) + || (stripedVersion = str.endsWith(suffStriped))) ) { + if ( stripedVersion ) + overhead = pref.length() + suffStriped.length(); + QString core = str.mid( pref.length(), str.length() - overhead ); + int corePos = pos - pref.length(); + state = QIntValidator::validate( core, corePos ); + pos = corePos + pref.length(); + str.replace( pref.length(), str.length() - overhead, core ); + } else { + state = QIntValidator::validate( str, pos ); + if ( state == Invalid ) { + // stripWhiteSpace(), cf. QSpinBox::interpretText() + QString special = spinBox->specialValueText().stripWhiteSpace(); + QString candidate = str.stripWhiteSpace(); + + if ( special.startsWith(candidate) ) { + if ( candidate.length() == special.length() ) { + state = Acceptable; + } else { + state = Intermediate; + } + } + } + } + } + return state; +} + +/*! + \class QSpinBox + \brief The QSpinBox class provides a spin box widget (spin button). + + \ingroup basic + \mainclass + + QSpinBox allows the user to choose a value either by clicking the + up/down buttons to increase/decrease the value currently displayed + or by typing the value directly into the spin box. If the value is + entered directly into the spin box, Enter (or Return) must be + pressed to apply the new value. The value is usually an integer. + + Every time the value changes QSpinBox emits the valueChanged() + signal. The current value can be fetched with value() and set + with setValue(). + + The spin box keeps the value within a numeric range, and to + multiples of the lineStep() size (see QRangeControl for details). + Clicking the up/down buttons or using the keyboard accelerator's + up and down arrows will increase or decrease the current value in + steps of size lineStep(). The minimum and maximum value and the + step size can be set using one of the constructors, and can be + changed later with setMinValue(), setMaxValue() and setLineStep(). + + Most spin boxes are directional, but QSpinBox can also operate as + a circular spin box, i.e. if the range is 0-99 and the current + value is 99, clicking "up" will give 0. Use setWrapping() if you + want circular behavior. + + The displayed value can be prepended and appended with arbitrary + strings indicating, for example, currency or the unit of + measurement. See setPrefix() and setSuffix(). The text in the spin + box is retrieved with text() (which includes any prefix() and + suffix()), or with cleanText() (which has no prefix(), no suffix() + and no leading or trailing whitespace). currentValueText() returns + the spin box's current value as text. + + Normally the spin box displays up and down arrows in the buttons. + You can use setButtonSymbols() to change the display to show + <b>+</b> and <b>-</b> symbols if you prefer. In either case the up + and down arrow keys work as expected. + + It is often desirable to give the user a special (often default) + choice in addition to the range of numeric values. See + setSpecialValueText() for how to do this with QSpinBox. + + The default \l QWidget::focusPolicy() is StrongFocus. + + If using prefix(), suffix() and specialValueText() don't provide + enough control, you can ignore them and subclass QSpinBox instead. + + QSpinBox can easily be subclassed to allow the user to input + things other than an integer value as long as the allowed input + can be mapped to a range of integers. This can be done by + overriding the virtual functions mapValueToText() and + mapTextToValue(), and setting another suitable validator using + setValidator(). + + For example, these functions could be changed so that the user + provided values from 0.0 to 10.0, or -1 to signify 'Auto', while + the range of integers used inside the program would be -1 to 100: + + \code + class MySpinBox : public QSpinBox + { + Q_OBJECT + public: + ... + + QString mapValueToText( int value ) + { + if ( value == -1 ) // special case + return QString( "Auto" ); + + return QString( "%1.%2" ) // 0.0 to 10.0 + .arg( value / 10 ).arg( value % 10 ); + } + + int mapTextToValue( bool *ok ) + { + if ( text() == "Auto" ) // special case + return -1; + + return (int) ( 10 * text().toFloat() ); // 0 to 100 + } + }; + \endcode + + <img src=qspinbox-m.png> <img src=qspinbox-w.png> + + \sa QScrollBar QSlider + \link guibooks.html#fowler GUI Design Handbook: Spin Box \endlink +*/ + + +/*! + Constructs a spin box with the default QRangeControl range and + step values. It is called \a name and has parent \a parent. + + \sa minValue(), maxValue(), setRange(), lineStep(), setSteps() +*/ + +QSpinBox::QSpinBox( QWidget * parent , const char *name ) + : QWidget( parent, name, WNoAutoErase ), + QRangeControl() +{ + initSpinBox(); +} + + +/*! + Constructs a spin box that allows values from \a minValue to \a + maxValue inclusive, with step amount \a step. The value is + initially set to \a minValue. + + The spin box is called \a name and has parent \a parent. + + \sa minValue(), maxValue(), setRange(), lineStep(), setSteps() +*/ + +QSpinBox::QSpinBox( int minValue, int maxValue, int step, QWidget* parent, + const char* name ) + : QWidget( parent, name, WNoAutoErase ), + QRangeControl( minValue, maxValue, step, step, minValue ) +{ + initSpinBox(); +} + +/* + \internal Initialization. +*/ + +void QSpinBox::initSpinBox() +{ + d = new QSpinBoxPrivate; + + d->controls = new QSpinWidget( this, "controls" ); + connect( d->controls, SIGNAL( stepUpPressed() ), SLOT( stepUp() ) ); + connect( d->controls, SIGNAL( stepDownPressed() ), SLOT( stepDown() ) ); + + wrap = FALSE; + edited = FALSE; + d->selreq = FALSE; + + validate = new QSpinBoxValidator( this, "validator" ); + vi = new QLineEdit( this, "qt_spinbox_edit" ); + d->controls->setEditWidget( vi ); + vi->setValidator( validate ); + vi->installEventFilter( this ); + vi->setFrame( FALSE ); + setFocusProxy( vi ); + + setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed ) ); + setBackgroundMode( PaletteBackground, PaletteBase ); + + updateDisplay(); + + connect( vi, SIGNAL(textChanged(const QString&)), SLOT(textChanged()) ); +} + +/*! + Destroys the spin box, freeing all memory and other resources. +*/ + +QSpinBox::~QSpinBox() +{ + delete d; +} + + +/*! + \property QSpinBox::text + \brief the spin box's text, including any prefix() and suffix() + + There is no default text. + + \sa value() +*/ + +QString QSpinBox::text() const +{ + return vi->text(); +} + + + +/*! + \property QSpinBox::cleanText + \brief the spin box's text with no prefix(), suffix() or leading + or trailing whitespace. + + \sa text, prefix, suffix +*/ + +QString QSpinBox::cleanText() const +{ + QString s = QString(text()).stripWhiteSpace(); + if ( !prefix().isEmpty() ) { + QString px = QString(prefix()).stripWhiteSpace(); + int len = px.length(); + if ( len && s.left(len) == px ) // Remove _only_ if it is the prefix + s.remove( (uint)0, len ); + } + if ( !suffix().isEmpty() ) { + QString sx = QString(suffix()).stripWhiteSpace(); + int len = sx.length(); + if ( len && s.right(len) == sx ) // Remove _only_ if it is the suffix + s.truncate( s.length() - len ); + } + return s.stripWhiteSpace(); +} + + +/*! + \property QSpinBox::specialValueText + \brief the special-value text + + If set, the spin box will display this text instead of a numeric + value whenever the current value is equal to minVal(). Typical use + is to indicate that this choice has a special (default) meaning. + + For example, if your spin box allows the user to choose the margin + width in a print dialog and your application is able to + automatically choose a good margin width, you can set up the spin + box like this: + \code + QSpinBox marginBox( -1, 20, 1, parent, "marginBox" ); + marginBox->setSuffix( " mm" ); + marginBox->setSpecialValueText( "Auto" ); + \endcode + The user will then be able to choose a margin width from 0-20 + millimeters or select "Auto" to leave it to the application to + choose. Your code must then interpret the spin box value of -1 as + the user requesting automatic margin width. + + All values are displayed with the prefix() and suffix() (if set), + \e except for the special value, which only shows the special + value text. + + To turn off the special-value text display, call this function + with an empty string. The default is no special-value text, i.e. + the numeric value is shown as usual. + + If no special-value text is set, specialValueText() returns + QString::null. +*/ + +void QSpinBox::setSpecialValueText( const QString &text ) +{ + specText = text; + updateDisplay(); +} + + +QString QSpinBox::specialValueText() const +{ + if ( specText.isEmpty() ) + return QString::null; + else + return specText; +} + + +/*! + \property QSpinBox::prefix + \brief the spin box's prefix + + The prefix is prepended to the start of the displayed value. + Typical use is to display a unit of measurement or a currency + symbol. For example: + + \code + sb->setPrefix( "$" ); + \endcode + + To turn off the prefix display, set this property to an empty + string. The default is no prefix. The prefix is not displayed for + the minValue() if specialValueText() is not empty. + + If no prefix is set, prefix() returns QString::null. + + \sa suffix() +*/ + +void QSpinBox::setPrefix( const QString &text ) +{ + pfix = text; + updateDisplay(); +} + + +QString QSpinBox::prefix() const +{ + if ( pfix.isEmpty() ) + return QString::null; + else + return pfix; +} + + +/*! + \property QSpinBox::suffix + \brief the suffix of the spin box + + The suffix is appended to the end of the displayed value. Typical + use is to display a unit of measurement or a currency symbol. For + example: + + \code + sb->setSuffix( " km" ); + \endcode + + To turn off the suffix display, set this property to an empty + string. The default is no suffix. The suffix is not displayed for + the minValue() if specialValueText() is not empty. + + If no suffix is set, suffix() returns a QString::null. + + \sa prefix() +*/ + +void QSpinBox::setSuffix( const QString &text ) +{ + sfix = text; + updateDisplay(); +} + +QString QSpinBox::suffix() const +{ + if ( sfix.isEmpty() ) + return QString::null; + else + return sfix; +} + + +/*! + \property QSpinBox::wrapping + \brief whether it is possible to step the value from the highest + value to the lowest value and vice versa + + By default, wrapping is turned off. + + If you have a range of 0..100 and wrapping is off when the user + reaches 100 and presses the Up Arrow nothing will happen; but if + wrapping is on the value will change from 100 to 0, then to 1, + etc. When wrapping is on, navigating past the highest value takes + you to the lowest and vice versa. + + \sa minValue, maxValue, setRange() +*/ + +void QSpinBox::setWrapping( bool on ) +{ + wrap = on; + updateDisplay(); +} + +bool QSpinBox::wrapping() const +{ + return wrap; +} + +/*! + \reimp +*/ +QSize QSpinBox::sizeHint() const +{ + constPolish(); + QSize sz = vi->sizeHint(); + int h = sz.height(); + QFontMetrics fm( font() ); + int w = 35; + int wx = fm.width( ' ' )*2; + QString s; + s = prefix() + ( (QSpinBox*)this )->mapValueToText( minValue() ) + suffix(); + w = QMAX( w, fm.width( s ) + wx); + s = prefix() + ( (QSpinBox*)this )->mapValueToText( maxValue() ) + suffix(); + w = QMAX(w, fm.width( s ) + wx ); + if ( !specialValueText().isEmpty() ) { + s = specialValueText(); + w = QMAX( w, fm.width( s ) + wx ); + } + return style().sizeFromContents(QStyle::CT_SpinBox, this, + QSize( w + d->controls->downRect().width(), + h + style().pixelMetric( QStyle::PM_DefaultFrameWidth ) * 2). + expandedTo( QApplication::globalStrut() )); +} + + +/*! + \reimp +*/ +QSize QSpinBox::minimumSizeHint() const +{ + int w = vi->minimumSizeHint().width() + d->controls->downRect().width(); + int h = QMAX( vi->minimumSizeHint().height(), d->controls->minimumSizeHint().height() ); + return QSize( w, h ); +} + +// Does the layout of the lineedit and the buttons + +void QSpinBox::arrangeWidgets() +{ + d->controls->arrange(); +} + +/*! + \property QSpinBox::value + \brief the value of the spin box + + \sa QRangeControl::setValue() +*/ + +void QSpinBox::setValue( int value ) +{ + edited = FALSE; // we ignore anything entered and not yet interpreted + QRangeControl::setValue( value ); + updateDisplay(); +} + +int QSpinBox::value() const +{ + QSpinBox * that = (QSpinBox *) this; + if ( edited ) { + that->edited = FALSE; // avoid recursion + that->interpretText(); + } + return QRangeControl::value(); +} + + +/*! + Increases the spin box's value by one lineStep(), wrapping as + necessary if wrapping() is TRUE. This is the same as clicking on + the pointing-up button and can be used for keyboard accelerators, + for example. + + \sa stepDown(), addLine(), lineStep(), setSteps(), setValue(), value() +*/ + +void QSpinBox::stepUp() +{ + if ( edited ) + interpretText(); + if ( wrapping() && ( value()+lineStep() > maxValue() || sumOutOfRange(value(), lineStep() ) ) ) { + setValue( minValue() ); + } else { + addLine(); + } +} + + +/*! + Decreases the spin box's value one lineStep(), wrapping as + necessary if wrapping() is TRUE. This is the same as clicking on + the pointing-down button and can be used for keyboard + accelerators, for example. + + \sa stepUp(), subtractLine(), lineStep(), setSteps(), setValue(), value() +*/ + +void QSpinBox::stepDown() +{ + if ( edited ) + interpretText(); + if ( wrapping() && ( value()-lineStep() < minValue() || sumOutOfRange(value(), -lineStep() ) ) ) { + setValue( maxValue() ); + } else { + subtractLine(); + } +} + + +/*! + \fn void QSpinBox::valueChanged( int value ) + + This signal is emitted every time the value of the spin box + changes; the new value is passed in \a value. This signal will be + emitted as a result of a call to setValue(), or because the user + changed the value by using a keyboard accelerator or mouse click, + etc. + + Note that the valueChanged() signal is emitted \e every time, not + just for the "last" step; i.e. if the user clicks "up" three + times, this signal is emitted three times. + + \sa value() +*/ + + +/*! + \fn void QSpinBox::valueChanged( const QString& valueText ) + + \overload + + This signal is emitted whenever the valueChanged( int ) signal is + emitted, i.e. every time the value of the spin box changes + (whatever the cause, e.g. by setValue(), by a keyboard + accelerator, by mouse clicks, etc.). + + The \a valueText parameter is the same string that is displayed in + the edit field of the spin box. + + \sa value() prefix() suffix() specialValueText() +*/ + + + +/*! + Intercepts and handles the events coming to the embedded QLineEdit + that have special meaning for the QSpinBox. The object is passed + as \a o and the event is passed as \a ev. +*/ + +bool QSpinBox::eventFilter( QObject* o, QEvent* ev ) +{ + if (o != vi) + return QWidget::eventFilter(o,ev); + + if ( ev->type() == QEvent::KeyPress ) { + QKeyEvent* k = (QKeyEvent*)ev; + + bool retval = FALSE; // workaround for MSVC++ optimization bug + if( (k->key() == Key_Tab) || (k->key() == Key_BackTab) ){ + if ( k->state() & Qt::ControlButton ) + return FALSE; + if ( edited ) + interpretText(); + qApp->sendEvent( this, ev ); + retval = TRUE; + } if ( k->key() == Key_Up ) { + stepUp(); + retval = TRUE; + } else if ( k->key() == Key_Down ) { + stepDown(); + retval = TRUE; + } else if ( k->key() == Key_Enter || k->key() == Key_Return ) { + interpretText(); + return FALSE; + } + if ( retval ) + return retval; + } else if ( ev->type() == QEvent::FocusOut || ev->type() == QEvent::Hide ) { + if ( edited ) { + interpretText(); + } + return FALSE; + } + return FALSE; +} + +/*! + \reimp + */ +void QSpinBox::setEnabled( bool enabled ) +{ + QWidget::setEnabled( enabled ); + updateDisplay(); +} + +/*! + \reimp +*/ +void QSpinBox::leaveEvent( QEvent* ) +{ +} + + +/*! + \reimp +*/ +void QSpinBox::resizeEvent( QResizeEvent* ) +{ + d->controls->resize( width(), height() ); +} + +/*! + \reimp +*/ +#ifndef QT_NO_WHEELEVENT +void QSpinBox::wheelEvent( QWheelEvent * e ) +{ + e->accept(); + static float offset = 0; + static QSpinBox* offset_owner = 0; + if (offset_owner != this) { + offset_owner = this; + offset = 0; + } + offset += -e->delta()/120; + if (QABS(offset) < 1) + return; + int ioff = int(offset); + int i; + for (i=0; i<QABS(ioff); i++) + offset > 0 ? stepDown() : stepUp(); + offset -= ioff; +} +#endif + +/*! + This virtual function is called by QRangeControl whenever the + value has changed. The QSpinBox reimplementation updates the + display and emits the valueChanged() signals; if you need + additional processing, either reimplement this or connect to one + of the valueChanged() signals. +*/ + +void QSpinBox::valueChange() +{ + d->selreq = hasFocus(); + updateDisplay(); + d->selreq = FALSE; + emit valueChanged( value() ); + emit valueChanged( currentValueText() ); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ValueChanged ); +#endif +} + + +/*! + This virtual function is called by QRangeControl whenever the + range has changed. It adjusts the default validator and updates + the display; if you need additional processing, you can + reimplement this function. +*/ + +void QSpinBox::rangeChange() +{ + updateDisplay(); +} + + +/*! + Sets the validator to \a v. The validator controls what keyboard + input is accepted when the user is editing in the value field. The + default is to use a suitable QIntValidator. + + Use setValidator(0) to turn off input validation (entered input + will still be kept within the spin box's range). +*/ + +void QSpinBox::setValidator( const QValidator* v ) +{ + if ( vi ) + vi->setValidator( v ); +} + + +/*! + Returns the validator that constrains editing for this spin box if + there is any; otherwise returns 0. + + \sa setValidator() QValidator +*/ + +const QValidator * QSpinBox::validator() const +{ + return vi ? vi->validator() : 0; +} + +/*! + Updates the contents of the embedded QLineEdit to reflect the + current value using mapValueToText(). Also enables/disables the + up/down push buttons accordingly. + + \sa mapValueToText() +*/ +void QSpinBox::updateDisplay() +{ + vi->setUpdatesEnabled( FALSE ); + vi->setText( currentValueText() ); + if ( d->selreq && isVisible() && ( hasFocus() || vi->hasFocus() ) ) { + selectAll(); + } else { + if ( !suffix().isEmpty() && vi->text().endsWith(suffix()) ) + vi->setCursorPosition( vi->text().length() - suffix().length() ); + } + vi->setUpdatesEnabled( TRUE ); + vi->repaint( FALSE ); // immediate repaint needed for some reason + edited = FALSE; + + bool upEnabled = isEnabled() && ( wrapping() || value() < maxValue() ); + bool downEnabled = isEnabled() && ( wrapping() || value() > minValue() ); + + d->controls->setUpEnabled( upEnabled ); + d->controls->setDownEnabled( downEnabled ); + vi->setEnabled( isEnabled() ); + repaint( FALSE ); +} + + +/*! + QSpinBox calls this after the user has manually edited the + contents of the spin box (i.e. by typing in the embedded + QLineEdit, rather than using the up/down buttons/keys). + + The default implementation of this function interprets the new + text using mapTextToValue(). If mapTextToValue() is successful, it + changes the spin box's value; if not, the value is left unchanged. + + \sa editor() +*/ + +void QSpinBox::interpretText() +{ + bool ok = TRUE; + bool done = FALSE; + int newVal = 0; + if ( !specialValueText().isEmpty() ) { + QString s = text().stripWhiteSpace(); + QString t = specialValueText().stripWhiteSpace(); + if ( s == t ) { + newVal = minValue(); + done = TRUE; + } + } + if ( !done ) + newVal = mapTextToValue( &ok ); + if ( ok ) + setValue( newVal ); + updateDisplay(); // sometimes redundant +} + + +/*! + Returns the geometry of the "up" button. +*/ + +QRect QSpinBox::upRect() const +{ + return d->controls->upRect(); +} + + +/*! + Returns the geometry of the "down" button. +*/ + +QRect QSpinBox::downRect() const +{ + return d->controls->downRect(); +} + + +/*! + Returns a pointer to the embedded QLineEdit. +*/ + +QLineEdit* QSpinBox::editor() const +{ + return vi; +} + + +/*! + This slot is called whenever the user edits the spin box's text. +*/ + +void QSpinBox::textChanged() +{ + edited = TRUE; // this flag is cleared in updateDisplay() +} + + +/*! + This virtual function is used by the spin box whenever it needs to + display value \a v. The default implementation returns a string + containing \a v printed in the standard way. Reimplementations may + return anything. (See the example in the detailed description.) + + Note that Qt does not call this function for specialValueText() + and that neither prefix() nor suffix() are included in the return + value. + + If you reimplement this, you may also need to reimplement + mapTextToValue(). + + \sa updateDisplay(), mapTextToValue() +*/ + +QString QSpinBox::mapValueToText( int v ) +{ + QString s; + s.setNum( v ); + return s; +} + + +/*! + This virtual function is used by the spin box whenever it needs to + interpret text entered by the user as a value. The text is + available as text() and as cleanText(), and this function must + parse it if possible. If \a ok is not 0: if it parses the text + successfully, \a *ok is set to TRUE; otherwise \a *ok is set to + FALSE. + + Subclasses that need to display spin box values in a non-numeric + way need to reimplement this function. + + Note that Qt handles specialValueText() separately; this function + is only concerned with the other values. + + The default implementation tries to interpret the text() as an + integer in the standard way and returns the integer value. + + \sa interpretText(), mapValueToText() +*/ + +int QSpinBox::mapTextToValue( bool* ok ) +{ + QString s = text(); + int newVal = s.toInt( ok ); + if ( !(*ok) && !( !prefix() && !suffix() ) ) {// Try removing any pre/suffix + s = cleanText(); + newVal = s.toInt( ok ); + } + return newVal; +} + + +/*! + Returns the full text calculated from the current value, including + any prefix and suffix. If there is special value text and the + value is minValue() the specialValueText() is returned. +*/ + +QString QSpinBox::currentValueText() +{ + QString s; + if ( (value() == minValue()) && !specialValueText().isEmpty() ) { + s = specialValueText(); + } else { + s = prefix(); + s.append( mapValueToText( value() ) ); + s.append( suffix() ); + } + return s; +} + +/*! + \reimp +*/ + +void QSpinBox::styleChange( QStyle& old ) +{ + arrangeWidgets(); + QWidget::styleChange( old ); +} + + +/*! + \enum QSpinBox::ButtonSymbols + + This enum type determines what the buttons in a spin box show. + + \value UpDownArrows the buttons show little arrows in the classic + style. + + \value PlusMinus the buttons show <b>+</b> and <b>-</b> symbols. + + \sa QSpinBox::buttonSymbols +*/ + +/*! + \property QSpinBox::buttonSymbols + + \brief the current button symbol mode + + The possible values can be either \c UpDownArrows or \c PlusMinus. + The default is \c UpDownArrows. + + \sa ButtonSymbols +*/ + +void QSpinBox::setButtonSymbols( ButtonSymbols newSymbols ) +{ + if ( buttonSymbols() == newSymbols ) + return; + + switch ( newSymbols ) { + case UpDownArrows: + d->controls->setButtonSymbols( QSpinWidget::UpDownArrows ); + break; + case PlusMinus: + d->controls->setButtonSymbols( QSpinWidget::PlusMinus ); + break; + } + // repaint( FALSE ); +} + +QSpinBox::ButtonSymbols QSpinBox::buttonSymbols() const +{ + switch( d->controls->buttonSymbols() ) { + case QSpinWidget::UpDownArrows: + return UpDownArrows; + case QSpinWidget::PlusMinus: + return PlusMinus; + } + return UpDownArrows; +} + +/*! + \property QSpinBox::minValue + + \brief the minimum value of the spin box + + When setting this property, \l QSpinBox::maxValue is adjusted, if + necessary, to ensure that the range remains valid. + + \sa setRange() setSpecialValueText() +*/ + +int QSpinBox::minValue() const +{ + return QRangeControl::minValue(); +} + +void QSpinBox::setMinValue( int minVal ) +{ + QRangeControl::setMinValue( minVal ); +} + +/*! + \property QSpinBox::maxValue + \brief the maximum value of the spin box + + When setting this property, \l QSpinBox::minValue is adjusted, if + necessary, to ensure that the range remains valid. + + \sa setRange() setSpecialValueText() +*/ + +int QSpinBox::maxValue() const +{ + return QRangeControl::maxValue(); +} + +void QSpinBox::setMaxValue( int maxVal ) +{ + QRangeControl::setMaxValue( maxVal ); +} + +/*! + \property QSpinBox::lineStep + \brief the line step + + When the user uses the arrows to change the spin box's value the + value will be incremented/decremented by the amount of the line + step. + + The setLineStep() function calls the virtual stepChange() function + if the new line step is different from the previous setting. + + \sa QRangeControl::setSteps() setRange() +*/ + +int QSpinBox::lineStep() const +{ + return QRangeControl::lineStep(); +} + +void QSpinBox::setLineStep( int i ) +{ + setSteps( i, pageStep() ); +} + +/*! + Selects all the text in the spin box's editor. +*/ + +void QSpinBox::selectAll() +{ + int overhead = prefix().length() + suffix().length(); + if ( !overhead || currentValueText() == specialValueText() ) { + vi->selectAll(); + } else { + vi->setSelection( prefix().length(), vi->text().length() - overhead ); + } +} + +#endif diff --git a/src/widgets/qspinbox.h b/src/widgets/qspinbox.h new file mode 100644 index 0000000..622537f --- /dev/null +++ b/src/widgets/qspinbox.h @@ -0,0 +1,172 @@ +/**************************************************************************** +** +** Definition of QSpinBox widget class +** +** Created : 970101 +** +** 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. +** +**********************************************************************/ + +#ifndef QSPINBOX_H +#define QSPINBOX_H + +#ifndef QT_H +#include "qwidget.h" +#include "qrangecontrol.h" +#endif // QT_H + +#ifndef QT_NO_SPINBOX + +class QLineEdit; +class QValidator; +class QSpinBoxPrivate; + +class Q_EXPORT QSpinBox: public QWidget, public QRangeControl +{ + Q_OBJECT + Q_ENUMS( ButtonSymbols ) + Q_PROPERTY( QString text READ text ) + Q_PROPERTY( QString prefix READ prefix WRITE setPrefix ) + Q_PROPERTY( QString suffix READ suffix WRITE setSuffix ) + Q_PROPERTY( QString cleanText READ cleanText ) + Q_PROPERTY( QString specialValueText READ specialValueText WRITE setSpecialValueText ) + Q_PROPERTY( bool wrapping READ wrapping WRITE setWrapping ) + Q_PROPERTY( ButtonSymbols buttonSymbols READ buttonSymbols WRITE setButtonSymbols ) + Q_PROPERTY( int maxValue READ maxValue WRITE setMaxValue ) + Q_PROPERTY( int minValue READ minValue WRITE setMinValue ) + Q_PROPERTY( int lineStep READ lineStep WRITE setLineStep ) + Q_PROPERTY( int value READ value WRITE setValue ) + +public: + QSpinBox( QWidget* parent=0, const char* name=0 ); + QSpinBox( int minValue, int maxValue, int step = 1, + QWidget* parent=0, const char* name=0 ); + ~QSpinBox(); + + QString text() const; + + virtual QString prefix() const; + virtual QString suffix() const; + virtual QString cleanText() const; + + virtual void setSpecialValueText( const QString &text ); + QString specialValueText() const; + + virtual void setWrapping( bool on ); + bool wrapping() const; + + enum ButtonSymbols { UpDownArrows, PlusMinus }; + virtual void setButtonSymbols( ButtonSymbols ); + ButtonSymbols buttonSymbols() const; + + virtual void setValidator( const QValidator* v ); + const QValidator * validator() const; + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + int minValue() const; + int maxValue() const; + void setMinValue( int ); + void setMaxValue( int ); + int lineStep() const; + void setLineStep( int ); + int value() const; + + QRect upRect() const; + QRect downRect() const; + +public slots: + virtual void setValue( int value ); + virtual void setPrefix( const QString &text ); + virtual void setSuffix( const QString &text ); + virtual void stepUp(); + virtual void stepDown(); + virtual void setEnabled( bool enabled ); + virtual void selectAll(); + +signals: + void valueChanged( int value ); + void valueChanged( const QString &valueText ); + +protected: + virtual QString mapValueToText( int value ); + virtual int mapTextToValue( bool* ok ); + QString currentValueText(); + + virtual void updateDisplay(); + virtual void interpretText(); + + QLineEdit* editor() const; + + virtual void valueChange(); + virtual void rangeChange(); + + bool eventFilter( QObject* obj, QEvent* ev ); + void resizeEvent( QResizeEvent* ev ); +#ifndef QT_NO_WHEELEVENT + void wheelEvent( QWheelEvent * ); +#endif + void leaveEvent( QEvent* ); + + void styleChange( QStyle& ); + +protected slots: + void textChanged(); + +private: + void initSpinBox(); + QSpinBoxPrivate* d; + QLineEdit* vi; + QValidator* validate; + QString pfix; + QString sfix; + QString specText; + + uint wrap : 1; + uint edited : 1; + + void arrangeWidgets(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QSpinBox( const QSpinBox& ); + QSpinBox& operator=( const QSpinBox& ); +#endif + +}; + +#endif // QT_NO_SPINBOX + +#endif // QSPINBOX_H diff --git a/src/widgets/qspinwidget.cpp b/src/widgets/qspinwidget.cpp new file mode 100644 index 0000000..427f710 --- /dev/null +++ b/src/widgets/qspinwidget.cpp @@ -0,0 +1,465 @@ +/**************************************************************************** +** +** Implementation of QSpinWidget class +** +** 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 "qrangecontrol.h" + +#ifndef QT_NO_SPINWIDGET + +#include "qrect.h" +#include "qtimer.h" +#include "qstyle.h" +#include "qpainter.h" + +class QSpinWidgetPrivate +{ +public: + QSpinWidgetPrivate() + : upEnabled( TRUE ), + downEnabled( TRUE ), + theButton( 0 ), + buttonDown( 0 ), + timerUp( 0 ), + bsyms( QSpinWidget::UpDownArrows ), + ed ( 0 ) {} + uint upEnabled :1; + uint downEnabled :1; + uint theButton :2; + uint buttonDown :2; + uint timerUp : 1; + QRect up; + QRect down; + QTimer auRepTimer; + QSpinWidget::ButtonSymbols bsyms; + QWidget *ed; + void startTimer( int msec ) { auRepTimer.start( msec, TRUE ); } + void startTimer( bool up, int msec ) { timerUp = up; startTimer( msec ); } + void stopTimer() { auRepTimer.stop(); } +}; + +/*! + + \class QSpinWidget qspinwidget.h + \brief The QSpinWidget class is an internal range control related class. + + \internal + + Constructs an empty range control widget with parent \a parent + called \a name. + +*/ + +QSpinWidget::QSpinWidget( QWidget* parent, const char* name ) + : QWidget( parent, name ) +{ + d = new QSpinWidgetPrivate(); + connect( &d->auRepTimer, SIGNAL( timeout() ), this, SLOT( timerDone() ) ); + setFocusPolicy( StrongFocus ); + + arrange(); + updateDisplay(); +} + + +/*! Destroys the object and frees any allocated resources. + +*/ + +QSpinWidget::~QSpinWidget() +{ + delete d; +} + +/*! */ +QWidget * QSpinWidget::editWidget() +{ + return d->ed; +} + +/*! + Sets the editing widget to \a w. +*/ +void QSpinWidget::setEditWidget( QWidget * w ) +{ + if ( w ) { + if (w->parentWidget() != this) + w->reparent( this, QPoint( 0, 0 ) ); + setFocusProxy( w ); + } + d->ed = w; + arrange(); + updateDisplay(); +} + +/*! \reimp + +*/ + +void QSpinWidget::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() != LeftButton ) { + d->stopTimer(); + d->buttonDown = 0; + d->theButton = 0; + repaint( d->down.unite( d->up ), FALSE ); + return; + } + + uint oldButtonDown = d->buttonDown; + + if ( d->down.contains( e->pos() ) && d->downEnabled ) + d->buttonDown = 1; + else if ( d->up.contains( e->pos() ) && d->upEnabled ) + d->buttonDown = 2; + else + d->buttonDown = 0; + + d->theButton = d->buttonDown; + if ( oldButtonDown != d->buttonDown ) { + if ( !d->buttonDown ) { + repaint( d->down.unite( d->up ), FALSE ); + } else if ( d->buttonDown & 1 ) { + repaint( d->down, FALSE ); + stepDown(); + d->startTimer( FALSE, 300 ); + } else if ( d->buttonDown & 2 ) { + repaint( d->up, FALSE ); + stepUp(); + d->startTimer( TRUE, 300 ); + } + } +} + +/*! + +*/ + +void QSpinWidget::arrange() +{ + d->up = QStyle::visualRect( style().querySubControlMetrics( QStyle::CC_SpinWidget, this, + QStyle::SC_SpinWidgetUp ), this ); + d->down = QStyle::visualRect( style().querySubControlMetrics( QStyle::CC_SpinWidget, this, + QStyle::SC_SpinWidgetDown ), this ); + if ( d->ed ) { + QRect r = QStyle::visualRect( style().querySubControlMetrics( QStyle::CC_SpinWidget, this, + QStyle::SC_SpinWidgetEditField ), this ); + d->ed->setGeometry( r ); + } +} + +/*! + +*/ + +void QSpinWidget::stepUp() +{ + emit stepUpPressed(); +} + +void QSpinWidget::resizeEvent( QResizeEvent* ) +{ + arrange(); +} + +/*! + +*/ + +void QSpinWidget::stepDown() +{ + emit stepDownPressed(); +} + + +void QSpinWidget::timerDone() +{ + // we use a double timer to make it possible for users to do + // something with 0-timer on valueChanged. + QTimer::singleShot( 1, this, SLOT( timerDoneEx() ) ); +} + +void QSpinWidget::timerDoneEx() +{ + if ( !d->buttonDown ) + return; + if ( d->timerUp ) + stepUp(); + else + stepDown(); + d->startTimer( 100 ); +} + + +void QSpinWidget::windowActivationChange( bool oldActive ) +{ + //was active, but lost focus + if ( oldActive && d->buttonDown ) { + d->stopTimer(); + d->buttonDown = 0; + d->theButton = 0; + } + QWidget::windowActivationChange( oldActive ); +} + + + +/*! + The event is passed in \a e. +*/ + +void QSpinWidget::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( e->button() != LeftButton ) + return; + + uint oldButtonDown = d->theButton; + d->theButton = 0; + if ( oldButtonDown != d->theButton ) { + if ( oldButtonDown & 1 ) + repaint( d->down, FALSE ); + else if ( oldButtonDown & 2 ) + repaint( d->up, FALSE ); + } + d->stopTimer(); + d->buttonDown = 0; +} + + +/*! + The event is passed in \a e. +*/ + +void QSpinWidget::mouseMoveEvent( QMouseEvent *e ) +{ + if ( !(e->state() & LeftButton ) ) + return; + + uint oldButtonDown = d->theButton; + if ( oldButtonDown & 1 && !d->down.contains( e->pos() ) ) { + d->stopTimer(); + d->theButton = 0; + repaint( d->down, FALSE ); + } else if ( oldButtonDown & 2 && !d->up.contains( e->pos() ) ) { + d->stopTimer(); + d->theButton = 0; + repaint( d->up, FALSE ); + } else if ( !oldButtonDown && d->up.contains( e->pos() ) && d->buttonDown & 2 ) { + d->startTimer( 500 ); + d->theButton = 2; + repaint( d->up, FALSE ); + } else if ( !oldButtonDown && d->down.contains( e->pos() ) && d->buttonDown & 1 ) { + d->startTimer( 500 ); + d->theButton = 1; + repaint( d->down, FALSE ); + } +} + + +/*! + The event is passed in \a e. +*/ +#ifndef QT_NO_WHEELEVENT +void QSpinWidget::wheelEvent( QWheelEvent *e ) +{ + e->accept(); + static float offset = 0; + static QSpinWidget* offset_owner = 0; + if ( offset_owner != this ) { + offset_owner = this; + offset = 0; + } + offset += -e->delta()/120; + if ( QABS( offset ) < 1 ) + return; + int ioff = int(offset); + int i; + for( i=0; i < QABS( ioff ); i++ ) + offset > 0 ? stepDown() : stepUp(); + offset -= ioff; +} +#endif + +/*! + +*/ +void QSpinWidget::paintEvent( QPaintEvent * ) +{ + QPainter p( this ); + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (hasFocus() || focusProxy() && focusProxy()->hasFocus()) + flags |= QStyle::Style_HasFocus; + + QStyle::SCFlags active; + if ( d->theButton & 1 ) + active = QStyle::SC_SpinWidgetDown; + else if ( d->theButton & 2 ) + active = QStyle::SC_SpinWidgetUp; + else + active = QStyle::SC_None; + + QRect fr = QStyle::visualRect( + style().querySubControlMetrics( QStyle::CC_SpinWidget, this, + QStyle::SC_SpinWidgetFrame ), this ); + style().drawComplexControl( QStyle::CC_SpinWidget, &p, this, + fr, colorGroup(), + flags, + (uint)QStyle::SC_All, + active ); +} + + +/*! + The previous style is passed in \a old. +*/ + +void QSpinWidget::styleChange( QStyle& old ) +{ + arrange(); + QWidget::styleChange( old ); +} + +/*! +*/ + +QRect QSpinWidget::upRect() const +{ + return d->up; +} + +/*! +*/ + +QRect QSpinWidget::downRect() const +{ + return d->down; +} + +/*! +*/ + +void QSpinWidget::updateDisplay() +{ + if ( !isEnabled() ) { + d->upEnabled = FALSE; + d->downEnabled = FALSE; + } + if ( d->theButton & 1 && ( d->downEnabled ) == 0 ) { + d->theButton &= ~1; + d->buttonDown &= ~1; + } + + if ( d->theButton & 2 && ( d->upEnabled ) == 0 ) { + d->theButton &= ~2; + d->buttonDown &= ~2; + } + repaint( FALSE ); +} + + +/*! + The previous enabled state is passed in \a old. +*/ + +void QSpinWidget::enableChanged( bool ) +{ + d->upEnabled = isEnabled(); + d->downEnabled = isEnabled(); + updateDisplay(); +} + + +/*! + Sets up-enabled to \a on. +*/ + +void QSpinWidget::setUpEnabled( bool on ) +{ + if ( (bool)d->upEnabled != on ) { + d->upEnabled = on; + updateDisplay(); + } +} + +/*! +*/ + +bool QSpinWidget::isUpEnabled() const +{ + return d->upEnabled; +} + +/*! + Sets down-enabled to \a on. +*/ + +void QSpinWidget::setDownEnabled( bool on ) +{ + if ( (bool)d->downEnabled != on ) { + d->downEnabled = on; + updateDisplay(); + } +} + +/*! +*/ + +bool QSpinWidget::isDownEnabled() const +{ + return d->downEnabled; +} + +/*! + Sets the button symbol to \a bs. +*/ + +void QSpinWidget::setButtonSymbols( ButtonSymbols bs ) +{ + d->bsyms = bs; +} + +/*! +*/ + +QSpinWidget::ButtonSymbols QSpinWidget::buttonSymbols() const +{ + return d->bsyms; +} + +#endif diff --git a/src/widgets/qsplashscreen.cpp b/src/widgets/qsplashscreen.cpp new file mode 100644 index 0000000..bb8687d --- /dev/null +++ b/src/widgets/qsplashscreen.cpp @@ -0,0 +1,271 @@ +/**************************************************************************** +** +** Definition of QSplashScreen class +** +** Copyright (C) 2005-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 "qsplashscreen.h" + +#ifndef QT_NO_SPLASHSCREEN + +#include "qapplication.h" +#include "qpainter.h" +#include "qpixmap.h" + +class QSplashScreenPrivate +{ +public: + QPixmap pixmap; + QString currStatus; + QColor currColor; + int currAlign; +}; + +/*! + \class QSplashScreen qsplashscreen.h + \brief The QSplashScreen widget provides a splash screen that can + be shown during application startup. + + \ingroup misc + \mainclass + + A splash screen is a widget that is usually displayed when an + application is being started. Splash screens are often used for + applications that have long start up times (e.g. database or + networking applications that take time to establish connections) to + provide the user with feedback that the application is loading. + + The splash screen appears centered on the screen. It may be useful to add + the \c WStyle_StaysOnTop if you desire to keep above all the windows in the + GUI. + + Some X11 window managers do not support the "stays on top" flag. A + solution is to set up a timer that periodically calls raise() on + the splash screen to simulate the "stays on top" effect. + + The most common usage is to show a splash screen before the main + widget is displayed on the screen. This is illustrated in the + following code snippet. + + \code + int main( int argc, char **argv ) + { + QApplication app( argc, argv ); + QPixmap pixmap( "splash.png" ); + QSplashScreen *splash = new QSplashScreen( pixmap ); + splash->show(); + QMainWindow *mainWin = new QMainWindow; + ... + app.setMainWidget( mainWin ); + mainWin->show(); + splash->finish( mainWin ); + delete splash; + return app.exec(); + } + \endcode + + It is sometimes useful to update the splash screen with messages, + for example, announcing connections established or modules loaded + as the application starts up. QSplashScreen supports this with the + message() function. If you wish to do your own drawing you can + get a pointer to the pixmap used in the splash screen with pixmap(). + Alternatively, you can subclass QSplashScreen and reimplement + drawContents(). + + The user can hide the splash screen by clicking on it with the + mouse. Since the splash screen is typically displayed before the + event loop has started running, it is necessary to periodically + call QApplication::processEvents() to receive the mouse clicks. + + \code + QPixmap pixmap( "splash.png" ); + QSplashScreen *splash = new QSplashScreen( pixmap ); + splash->show(); + ... // Loading some items + splash->message( "Loaded modules" ); + qApp->processEvents(); + ... // Establishing connections + splash->message( "Established connections" ); + qApp->processEvents(); + \endcode + +*/ + +/*! + Construct a splash screen that will display the \a pixmap. + + There should be no need to set the widget flags, \a f, except + perhaps \c WDestructiveClose or \c WStyle_StaysOnTop. +*/ +QSplashScreen::QSplashScreen( const QPixmap &pixmap, WFlags f ) + : QWidget( 0, 0, WStyle_Customize | WStyle_Splash | f ) +{ + d = new QSplashScreenPrivate(); + d->pixmap = pixmap; + setPixmap( d->pixmap ); // Does an implicit repaint +} + +/*! + Destructor. +*/ +QSplashScreen::~QSplashScreen() +{ + delete d; +} + +/*! + \reimp +*/ +void QSplashScreen::mousePressEvent( QMouseEvent * ) +{ + hide(); +} + +/*! + This overrides QWidget::repaint(). It differs from the standard + repaint function in that it also calls QApplication::flush() to + ensure the updates are displayed, even when there is no event loop + present. +*/ +void QSplashScreen::repaint() +{ + drawContents(); + QWidget::repaint(); + QApplication::flush(); +} + +/*! + \fn QSplashScreen::messageChanged( const QString &message ) + + This signal is emitted when the message on the splash screen + changes. \a message is the new message and is a null-string + when the message has been removed. + + \sa message(), clear() +*/ + + + +/*! + Draws the \a message text onto the splash screen with color \a + color and aligns the text according to the flags in \a alignment. + + \sa Qt::AlignmentFlags clear() +*/ +void QSplashScreen::message( const QString &message, int alignment, + const QColor &color ) +{ + d->currStatus = message; + d->currAlign = alignment; + d->currColor = color; + emit messageChanged( d->currStatus ); + repaint(); +} + +/*! + Removes the message being displayed on the splash screen + + \sa message() + */ +void QSplashScreen::clear() +{ + d->currStatus = QString::null; + emit messageChanged( d->currStatus ); + repaint(); +} + +/*! + Makes the splash screen wait until the widget \a mainWin is displayed + before calling close() on itself. +*/ +void QSplashScreen::finish( QWidget *mainWin ) +{ + if ( mainWin ) { +#if defined(Q_WS_X11) + extern void qt_wait_for_window_manager( QWidget *mainWin ); + qt_wait_for_window_manager( mainWin ); +#endif + } + close(); +} + +/*! + Sets the pixmap that will be used as the splash screen's image to + \a pixmap. +*/ +void QSplashScreen::setPixmap( const QPixmap &pixmap ) +{ + d->pixmap = pixmap; + QRect r(0, 0, d->pixmap.size().width(), d->pixmap.size().height()); + resize( d->pixmap.size() ); + move( QApplication::desktop()->screenGeometry().center() - r.center() ); + repaint(); +} + +/*! + Returns the pixmap that is used in the splash screen. The image + does not have any of the text drawn by message() calls. +*/ +QPixmap* QSplashScreen::pixmap() const +{ + return &( d->pixmap ); +} + +/*! + \internal +*/ +void QSplashScreen::drawContents() +{ + QPixmap textPix = d->pixmap; + QPainter painter( &textPix, this ); + drawContents( &painter ); + setErasePixmap( textPix ); +} + +/*! + Draw the contents of the splash screen using painter \a painter. + The default implementation draws the message passed by message(). + Reimplement this function if you want to do your own drawing on + the splash screen. +*/ +void QSplashScreen::drawContents( QPainter *painter ) +{ + painter->setPen( d->currColor ); + QRect r = rect(); + r.setRect( r.x() + 5, r.y() + 5, r.width() - 10, r.height() - 10 ); + painter->drawText( r, d->currAlign, d->currStatus ); +} + +#endif //QT_NO_SPLASHSCREEN diff --git a/src/widgets/qsplashscreen.h b/src/widgets/qsplashscreen.h new file mode 100644 index 0000000..eb23a57 --- /dev/null +++ b/src/widgets/qsplashscreen.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Definition of QSplashScreen class +** +** Copyright (C) 2005-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. +** +**********************************************************************/ + +#ifndef QSPLASHSCREEN_H +#define QSPLASHSCREEN_H + +#ifndef QT_H +#include "qpixmap.h" +#include "qwidget.h" +#endif // QT_H + +#ifndef QT_NO_SPLASHSCREEN +class QSplashScreenPrivate; + +class Q_EXPORT QSplashScreen : public QWidget +{ + Q_OBJECT +public: + QSplashScreen( const QPixmap &pixmap = QPixmap(), WFlags f = 0 ); + virtual ~QSplashScreen(); + + void setPixmap( const QPixmap &pixmap ); + QPixmap* pixmap() const; + void finish( QWidget *w ); + void repaint(); + +public slots: + void message( const QString &str, int flags = AlignLeft, + const QColor &color = black ); + void clear(); + +signals: + void messageChanged( const QString &str ); + +protected: + virtual void drawContents( QPainter *painter ); + void mousePressEvent( QMouseEvent * ); + +private: + void drawContents(); + + QSplashScreenPrivate *d; +}; +#endif //QT_NO_SPLASHSCREEN +#endif diff --git a/src/widgets/qsplitter.cpp b/src/widgets/qsplitter.cpp new file mode 100644 index 0000000..4810911 --- /dev/null +++ b/src/widgets/qsplitter.cpp @@ -0,0 +1,1429 @@ +/**************************************************************************** +** +** Implementation of QSplitter class +** +** Created : 980105 +** +** 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 "qsplitter.h" +#ifndef QT_NO_SPLITTER + +#include "qlayout.h" +#include "../kernel/qlayoutengine_p.h" +#include "qapplication.h" +#include "qbitmap.h" +#include "qdrawutil.h" +#include "qmemarray.h" +#include "qobjectlist.h" +#include "qpainter.h" +#include "qptrlist.h" +#include "qstyle.h" + +class QSplitterHandle : public QWidget +{ + Q_OBJECT +public: + QSplitterHandle( Orientation o, + QSplitter *parent, const char* name=0 ); + void setOrientation( Orientation o ); + Orientation orientation() const { return orient; } + + bool opaque() const { return s->opaqueResize(); } + + QSize sizeHint() const; + + int id() const { return myId; } // d->list.at(id())->wid == this + void setId( int i ) { myId = i; } + +protected: + void paintEvent( QPaintEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + +private: + Orientation orient; + bool opaq; + int myId; + + QSplitter *s; +}; + +#include "qsplitter.moc" + +const uint Default = 2; + +static int mouseOffset; +static int opaqueOldPos = -1; // this assumes that there's only one mouse + +static QPoint toggle( QWidget *w, QPoint pos ) +{ + QSize minS = qSmartMinSize( w ); + return -pos - QPoint( minS.width(), minS.height() ); +} + +static bool isCollapsed( QWidget *w ) +{ + return w->x() < 0 || w->y() < 0; +} + +static QPoint topLeft( QWidget *w ) +{ + if ( isCollapsed(w) ) { + return toggle( w, w->pos() ); + } else { + return w->pos(); + } +} + +static QPoint bottomRight( QWidget *w ) +{ + if ( isCollapsed(w) ) { + return toggle( w, w->pos() ) - QPoint( 1, 1 ); + } else { + return w->geometry().bottomRight(); + } +} + +QSplitterHandle::QSplitterHandle( Orientation o, QSplitter *parent, + const char * name ) + : QWidget( parent, name ) +{ + s = parent; + setOrientation( o ); +} + +QSize QSplitterHandle::sizeHint() const +{ + int hw = s->handleWidth(); + return parentWidget()->style().sizeFromContents( QStyle::CT_Splitter, s, + QSize(hw, hw) ) + .expandedTo( QApplication::globalStrut() ); +} + +void QSplitterHandle::setOrientation( Orientation o ) +{ + orient = o; +#ifndef QT_NO_CURSOR + setCursor( o == QSplitter::Horizontal ? splitHCursor : splitVCursor ); +#endif +} + +void QSplitterHandle::mouseMoveEvent( QMouseEvent *e ) +{ + if ( !(e->state()&LeftButton) ) + return; + QCOORD pos = s->pick( parentWidget()->mapFromGlobal(e->globalPos()) ) + - mouseOffset; + if ( opaque() ) { + s->moveSplitter( pos, id() ); + } else { + s->setRubberband( s->adjustPos(pos, id()) ); + } +} + +void QSplitterHandle::mousePressEvent( QMouseEvent *e ) +{ + if ( e->button() == LeftButton ) + mouseOffset = s->pick( e->pos() ); +} + +void QSplitterHandle::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( !opaque() && e->button() == LeftButton ) { + QCOORD pos = s->pick( parentWidget()->mapFromGlobal(e->globalPos()) ) + - mouseOffset; + s->setRubberband( -1 ); + s->moveSplitter( pos, id() ); + } +} + +void QSplitterHandle::paintEvent( QPaintEvent * ) +{ + QPainter p( this ); + parentWidget()->style().drawPrimitive( QStyle::PE_Splitter, &p, rect(), + colorGroup(), + (orientation() == Horizontal ? + QStyle::Style_Horizontal : 0) ); +} + +class QSplitterLayoutStruct : public Qt +{ +public: + QCOORD sizer; + uint isHandle : 1; + uint collapsible : 2; + uint resizeMode : 2; + QWidget *wid; + + QSplitterLayoutStruct() + : sizer( -1 ), collapsible( Default ) { } + QCOORD getSizer( Orientation orient ); +}; + +QCOORD QSplitterLayoutStruct::getSizer( Orientation orient ) +{ + if ( sizer == -1 ) { + QSize s = wid->sizeHint(); + if ( !s.isValid() || wid->testWState(WState_Resized) ) + s = wid->size(); + sizer = ( orient == Horizontal ) ? s.width() : s.height(); + } + return sizer; +} + +class QSplitterPrivate +{ +public: + QSplitterPrivate() + : opaque( FALSE ), firstShow( TRUE ), childrenCollapsible( TRUE ), + handleWidth( 0 ) { } + + QPtrList<QSplitterLayoutStruct> list; + bool opaque : 8; + bool firstShow : 8; + bool childrenCollapsible : 8; + int handleWidth; +}; + + +/*! + \class QSplitter + \brief The QSplitter class implements a splitter widget. + + \ingroup organizers + \mainclass + + A splitter lets the user control the size of child widgets by + dragging the boundary between the children. Any number of widgets + may be controlled by a single splitter. + + To show a QListBox, a QListView and a QTextEdit side by side: + \code + QSplitter *split = new QSplitter( parent ); + QListBox *lb = new QListBox( split ); + QListView *lv = new QListView( split ); + QTextEdit *ed = new QTextEdit( split ); + \endcode + + QSplitter lays out its children horizontally (side by side); you + can use setOrientation(QSplitter::Vertical) to lay out the + children vertically. + + By default, all widgets can be as large or as small as the user + wishes, between the \l minimumSizeHint() (or \l minimumSize()) + and \l maximumSize() of the widgets. Use setResizeMode() to + specify that a widget should keep its size when the splitter is + resized, or set the stretch component of the \l sizePolicy. + + Although QSplitter normally resizes the children only at the end + of a resize operation, if you call setOpaqueResize(TRUE) the + widgets are resized as often as possible. + + The initial distribution of size between the widgets is determined + by the initial size of each widget. You can also use setSizes() to + set the sizes of all the widgets. The function sizes() returns the + sizes set by the user. + + If you hide() a child its space will be distributed among the + other children. It will be reinstated when you show() it again. It + is also possible to reorder the widgets within the splitter using + moveToFirst() and moveToLast(). + + <img src=qsplitter-m.png> <img src=qsplitter-w.png> + + \sa QTabBar +*/ + + +/*! + Constructs a horizontal splitter with the \a parent and \a name + arguments being passed on to the QFrame constructor. +*/ + +QSplitter::QSplitter( QWidget *parent, const char *name ) + : QFrame( parent, name, WPaintUnclipped ) +{ + orient = Horizontal; + init(); +} + + +/*! + Constructs a splitter with orientation \a o with the \a parent and + \a name arguments being passed on to the QFrame constructor. +*/ + +QSplitter::QSplitter( Orientation o, QWidget *parent, const char *name ) + : QFrame( parent, name, WPaintUnclipped ) +{ + orient = o; + init(); +} + + +/*! + Destroys the splitter and any children. +*/ + +QSplitter::~QSplitter() +{ + delete d; +} + + +void QSplitter::init() +{ + d = new QSplitterPrivate; + d->list.setAutoDelete( TRUE ); + QSizePolicy sp( QSizePolicy::Expanding, QSizePolicy::Preferred ); + if ( orient == Vertical ) + sp.transpose(); + setSizePolicy( sp ); + clearWState( WState_OwnSizePolicy ); +} + +/*! + \fn void QSplitter::refresh() + + Updates the splitter's state. You should not need to call this + function. +*/ + + +/*! + \property QSplitter::orientation + \brief the orientation of the splitter + + By default the orientation is horizontal (the widgets are side by + side). The possible orientations are \c Horizontal and + \c Vertical. +*/ + +void QSplitter::setOrientation( Orientation o ) +{ + if ( orient == o ) + return; + + if ( !testWState( WState_OwnSizePolicy ) ) { + QSizePolicy sp = sizePolicy(); + sp.transpose(); + setSizePolicy( sp ); + clearWState( WState_OwnSizePolicy ); + } + + orient = o; + + QSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( s->isHandle ) + ((QSplitterHandle*)s->wid)->setOrientation( o ); + s = d->list.next(); + } + recalc( isVisible() ); +} + +/*! + \property QSplitter::childrenCollapsible + \brief whether child widgets can be resized down to size 0 by the user + + By default, children are collapsible. It is possible to enable + and disable the collapsing of individual children; see + setCollapsible(). +*/ + +void QSplitter::setChildrenCollapsible( bool collapse ) +{ + d->childrenCollapsible = collapse; +} + +bool QSplitter::childrenCollapsible() const +{ + return d->childrenCollapsible; +} + +/*! + Sets whether the child widget \a w is collapsible to \a collapse. + + By default, children are collapsible, meaning that the user can + resize them down to size 0, even if they have a non-zero + minimumSize() or minimumSizeHint(). This behavior can be changed + on a per-widget basis by calling this function, or globally for + all the widgets in the splitter by setting the \l + childrenCollapsible property. + + \sa childrenCollapsible +*/ + +void QSplitter::setCollapsible( QWidget *w, bool collapse ) +{ + findWidget( w )->collapsible = collapse ? 1 : 0; +} + +/*! + \reimp +*/ +void QSplitter::resizeEvent( QResizeEvent * ) +{ + doResize(); +} + +QSplitterLayoutStruct *QSplitter::findWidget( QWidget *w ) +{ + processChildEvents(); + QSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( s->wid == w ) + return s; + s = d->list.next(); + } + return addWidget( w ); +} + +/* + Inserts the widget \a w at the end (or at the beginning if \a + prepend is TRUE) of the splitter's list of widgets. + + It is the responsibility of the caller to make sure that \a w is + not already in the splitter and to call recalcId() if needed. (If + \a prepend is TRUE, then recalcId() is very probably needed.) +*/ + +QSplitterLayoutStruct *QSplitter::addWidget( QWidget *w, bool prepend ) +{ + QSplitterLayoutStruct *s; + QSplitterHandle *newHandle = 0; + if ( d->list.count() > 0 ) { + s = new QSplitterLayoutStruct; + s->resizeMode = KeepSize; + QString tmp = "qt_splithandle_"; + tmp += w->name(); + newHandle = new QSplitterHandle( orientation(), this, tmp ); + s->wid = newHandle; + newHandle->setId( d->list.count() ); + s->isHandle = TRUE; + s->sizer = pick( newHandle->sizeHint() ); + if ( prepend ) + d->list.prepend( s ); + else + d->list.append( s ); + } + s = new QSplitterLayoutStruct; + s->resizeMode = DefaultResizeMode; + s->wid = w; + s->isHandle = FALSE; + if ( prepend ) + d->list.prepend( s ); + else + d->list.append( s ); + if ( newHandle && isVisible() ) + newHandle->show(); // will trigger sending of post events + return s; +} + + +/*! + Tells the splitter that the child widget described by \a c has + been inserted or removed. +*/ + +void QSplitter::childEvent( QChildEvent *c ) +{ + if ( c->type() == QEvent::ChildInserted ) { + if ( !c->child()->isWidgetType() ) + return; + + if ( ((QWidget*)c->child())->testWFlags( WType_TopLevel ) ) + return; + + QSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( s->wid == c->child() ) + return; + s = d->list.next(); + } + addWidget( (QWidget*)c->child() ); + recalc( isVisible() ); + } else if ( c->type() == QEvent::ChildRemoved ) { + QSplitterLayoutStruct *prev = 0; + if ( d->list.count() > 1 ) + prev = d->list.at( 1 ); // yes, this is correct + QSplitterLayoutStruct *curr = d->list.first(); + while ( curr ) { + if ( curr->wid == c->child() ) { + d->list.removeRef( curr ); + if ( prev && prev->isHandle ) { + QWidget *w = prev->wid; + d->list.removeRef( prev ); + delete w; // will call childEvent() + } + recalcId(); + doResize(); + return; + } + prev = curr; + curr = d->list.next(); + } + } +} + + +/*! + Displays a rubber band at position \a p. If \a p is negative, the + rubber band is removed. +*/ + +void QSplitter::setRubberband( int p ) +{ + QPainter paint( this ); + paint.setPen( gray ); + paint.setBrush( gray ); + paint.setRasterOp( XorROP ); + QRect r = contentsRect(); + const int rBord = 3; // customizable? + int hw = handleWidth(); + if ( orient == Horizontal ) { + if ( opaqueOldPos >= 0 ) + paint.drawRect( opaqueOldPos + hw / 2 - rBord, r.y(), + 2 * rBord, r.height() ); + if ( p >= 0 ) + paint.drawRect( p + hw / 2 - rBord, r.y(), 2 * rBord, r.height() ); + } else { + if ( opaqueOldPos >= 0 ) + paint.drawRect( r.x(), opaqueOldPos + hw / 2 - rBord, + r.width(), 2 * rBord ); + if ( p >= 0 ) + paint.drawRect( r.x(), p + hw / 2 - rBord, r.width(), 2 * rBord ); + } + opaqueOldPos = p; +} + + +/*! + \reimp +*/ + +bool QSplitter::event( QEvent *e ) +{ + switch ( e->type() ) { + case QEvent::Show: + if ( !d->firstShow ) + break; + d->firstShow = FALSE; + // fall through + case QEvent::LayoutHint: + recalc( isVisible() ); + break; + default: + ; + } + return QWidget::event( e ); +} + + +/*! + \obsolete + + Draws the splitter handle in the rectangle described by \a x, \a y, + \a w, \a h using painter \a p. + \sa QStyle::drawPrimitive() +*/ + +// ### Remove this in 4.0 + +void QSplitter::drawSplitter( QPainter *p, + QCOORD x, QCOORD y, QCOORD w, QCOORD h ) +{ + style().drawPrimitive(QStyle::PE_Splitter, p, QRect(x, y, w, h), colorGroup(), + (orientation() == Horizontal ? + QStyle::Style_Horizontal : 0)); +} + + +/*! + Returns the ID of the widget to the right of or below the widget + \a w, or 0 if there is no such widget (i.e. it is either not in + this QSplitter or \a w is at the end). +*/ + +int QSplitter::idAfter( QWidget* w ) const +{ + QSplitterLayoutStruct *s = d->list.first(); + bool seen_w = FALSE; + while ( s ) { + if ( s->isHandle && seen_w ) + return d->list.at(); + if ( !s->isHandle && s->wid == w ) + seen_w = TRUE; + s = d->list.next(); + } + return 0; +} + + +/*! + Moves the left/top edge of the splitter handle with ID \a id as + close as possible to position \a p, which is the distance from the + left (or top) edge of the widget. + + For Arabic, Hebrew and other right-to-left languages the layout is + reversed. \a p is then the distance from the right (or top) edge + of the widget. + + \sa idAfter() +*/ +void QSplitter::moveSplitter( QCOORD p, int id ) +{ + QSplitterLayoutStruct *s = d->list.at( id ); + int farMin; + int min; + int max; + int farMax; + + p = adjustPos( p, id, &farMin, &min, &max, &farMax ); + int oldP = pick( s->wid->pos() ); + + if ( QApplication::reverseLayout() && orient == Horizontal ) { + int q = p + s->wid->width(); + doMove( FALSE, q, id - 1, -1, (q > oldP), (p > max) ); + doMove( TRUE, q, id, -1, (q > oldP), (p < min) ); + } else { + doMove( FALSE, p, id, +1, (p < oldP), (p > max) ); + doMove( TRUE, p, id - 1, +1, (p < oldP), (p < min) ); + } + storeSizes(); +} + + +void QSplitter::setGeo( QWidget *w, int p, int s, bool splitterMoved ) +{ + QRect r; + if ( orient == Horizontal ) { + if ( QApplication::reverseLayout() && orient == Horizontal + && !splitterMoved ) + p = contentsRect().width() - p - s; + r.setRect( p, contentsRect().y(), s, contentsRect().height() ); + } else { + r.setRect( contentsRect().x(), p, contentsRect().width(), s ); + } + + /* + Hide the child widget, but without calling hide() so that the + splitter handle is still shown. + */ + if ( !w->isHidden() && s <= 0 && pick(qSmartMinSize(w)) > 0 ) + r.moveTopLeft( toggle(w, r.topLeft()) ); + w->setGeometry( r ); +} + + +void QSplitter::doMove( bool backwards, int pos, int id, int delta, bool upLeft, + bool mayCollapse ) +{ + if ( id < 0 || id >= (int) d->list.count() ) + return; + + QSplitterLayoutStruct *s = d->list.at( id ); + QWidget *w = s->wid; + + int nextId = backwards ? id - delta : id + delta; + + if ( w->isHidden() ) { + doMove( backwards, pos, nextId, delta, upLeft, TRUE ); + } else { + if ( s->isHandle ) { + int dd = s->getSizer( orient ); + int nextPos = backwards ? pos - dd : pos + dd; + int left = backwards ? pos - dd : pos; + setGeo( w, left, dd, TRUE ); + doMove( backwards, nextPos, nextId, delta, upLeft, mayCollapse ); + } else { + int dd = backwards ? pos - pick( topLeft(w) ) + : pick( bottomRight(w) ) - pos + 1; + if ( dd > 0 || (!isCollapsed(w) && !mayCollapse) ) { + dd = QMAX( pick(qSmartMinSize(w)), + QMIN(dd, pick(w->maximumSize())) ); + } else { + dd = 0; + } + setGeo( w, backwards ? pos - dd : pos, dd, TRUE ); + doMove( backwards, backwards ? pos - dd : pos + dd, nextId, delta, + upLeft, TRUE ); + } + } +} + +int QSplitter::findWidgetJustBeforeOrJustAfter( int id, int delta, int &collapsibleSize ) +{ + id += delta; + do { + QWidget *w = d->list.at( id )->wid; + if ( !w->isHidden() ) { + if ( collapsible(d->list.at(id)) ) + collapsibleSize = pick( qSmartMinSize(w) ); + return id; + } + id += 2 * delta; // go to previous (or next) widget, skip the handle + } while ( id >= 0 && id < (int)d->list.count() ); + + return -1; +} + +void QSplitter::getRange( int id, int *farMin, int *min, int *max, int *farMax ) +{ + int n = d->list.count(); + if ( id <= 0 || id >= n - 1 ) + return; + + int collapsibleSizeBefore = 0; + int idJustBefore = findWidgetJustBeforeOrJustAfter( id, -1, collapsibleSizeBefore ); + + int collapsibleSizeAfter = 0; + int idJustAfter = findWidgetJustBeforeOrJustAfter( id, +1, collapsibleSizeAfter ); + + int minBefore = 0; + int minAfter = 0; + int maxBefore = 0; + int maxAfter = 0; + int i; + + for ( i = 0; i < id; i++ ) + addContribution( i, &minBefore, &maxBefore, i == idJustBefore ); + for ( i = id; i < n; i++ ) + addContribution( i, &minAfter, &maxAfter, i == idJustAfter ); + + QRect r = contentsRect(); + int farMinVal; + int minVal; + int maxVal; + int farMaxVal; + + int smartMinBefore = QMAX( minBefore, pick(r.size()) - maxAfter ); + int smartMaxBefore = QMIN( maxBefore, pick(r.size()) - minAfter ); + + if ( orient == Vertical || !QApplication::reverseLayout() ) { + minVal = pick( r.topLeft() ) + smartMinBefore; + maxVal = pick( r.topLeft() ) + smartMaxBefore; + + farMinVal = minVal; + if ( minBefore - collapsibleSizeBefore >= pick(r.size()) - maxAfter ) + farMinVal -= collapsibleSizeBefore; + farMaxVal = maxVal; + if ( pick(r.size()) - (minAfter - collapsibleSizeAfter) <= maxBefore ) + farMaxVal += collapsibleSizeAfter; + } else { + int hw = handleWidth(); + minVal = r.width() - smartMaxBefore - hw; + maxVal = r.width() - smartMinBefore - hw; + + farMinVal = minVal; + if ( pick(r.size()) - (minAfter - collapsibleSizeAfter) <= maxBefore ) + farMinVal -= collapsibleSizeAfter; + farMaxVal = maxVal; + if ( minBefore - collapsibleSizeBefore >= pick(r.size()) - maxAfter ) + farMaxVal += collapsibleSizeBefore; + } + + if ( farMin ) + *farMin = farMinVal; + if ( min ) + *min = minVal; + if ( max ) + *max = maxVal; + if ( farMax ) + *farMax = farMaxVal; +} + +/*! + Returns the valid range of the splitter with ID \a id in \a *min + and \a *max if \a min and \a max are not 0. + + \sa idAfter() +*/ + +void QSplitter::getRange( int id, int *min, int *max ) +{ + getRange( id, min, 0, 0, max ); +} + + +/*! + Returns the closest legal position to \a pos of the widget with ID + \a id. + + \sa idAfter() +*/ + +int QSplitter::adjustPos( int pos, int id ) +{ + int x, i, n, u; + return adjustPos( pos, id, &u, &n, &i, &x ); +} + +int QSplitter::adjustPos( int pos, int id, int *farMin, int *min, int *max, + int *farMax ) +{ + const int Threshold = 40; + + getRange( id, farMin, min, max, farMax ); + + if ( pos >= *min ) { + if ( pos <= *max ) { + return pos; + } else { + int delta = pos - *max; + int width = *farMax - *max; + + if ( delta > width / 2 && delta >= QMIN(Threshold, width) ) { + return *farMax; + } else { + return *max; + } + } + } else { + int delta = *min - pos; + int width = *min - *farMin; + + if ( delta > width / 2 && delta >= QMIN(Threshold, width) ) { + return *farMin; + } else { + return *min; + } + } +} + +bool QSplitter::collapsible( QSplitterLayoutStruct *s ) +{ + if (pick(qSmartMinSize(s->wid)) == 1) + return FALSE; + if ( s->collapsible != Default ) { + return (bool) s->collapsible; + } else { + return d->childrenCollapsible; + } +} + +void QSplitter::doResize() +{ + QRect r = contentsRect(); + int n = d->list.count(); + QMemArray<QLayoutStruct> a( n ); + + for ( int pass = 0; pass < 2; pass++ ) { + int numAutoWithStretch = 0; + int numAutoWithoutStretch = 0; + + for ( int i = 0; i < n; i++ ) { + a[i].init(); + QSplitterLayoutStruct *s = d->list.at( i ); + if ( s->wid->isHidden() || isCollapsed(s->wid) ) { + a[i].maximumSize = 0; + } else if ( s->isHandle ) { + a[i].sizeHint = a[i].minimumSize = a[i].maximumSize = s->sizer; + a[i].empty = FALSE; + } else { + int mode = s->resizeMode; + int stretch = 1; + + if ( mode == DefaultResizeMode ) { + QSizePolicy p = s->wid->sizePolicy(); + int sizePolicyStretch = + pick( QSize(p.horStretch(), p.verStretch()) ); + if ( sizePolicyStretch > 0 ) { + mode = Stretch; + stretch = sizePolicyStretch; + numAutoWithStretch++; + } else { + /* + Do things differently on the second pass, + if there's one. A second pass is necessary + if it was found out during the first pass + that all DefaultResizeMode items are + KeepSize items. In that case, we make them + all Stretch items instead, for a more Qt + 3.0-compatible behavior. + */ + mode = ( pass == 0 ) ? KeepSize : Stretch; + numAutoWithoutStretch++; + } + } + + a[i].minimumSize = pick( qSmartMinSize(s->wid) ); + a[i].maximumSize = pick( s->wid->maximumSize() ); + a[i].empty = FALSE; + + if ( mode == Stretch ) { + if ( s->getSizer(orient) > 1 ) + stretch *= s->getSizer( orient ); + // QMIN(): ad hoc work-around for layout engine limitation + a[i].stretch = QMIN( stretch, 8192 ); + a[i].sizeHint = a[i].minimumSize; + } else if ( mode == KeepSize ) { + a[i].sizeHint = s->getSizer( orient ); + } else { // mode == FollowSizeHint + a[i].sizeHint = pick( s->wid->sizeHint() ); + } + } + } + + // a second pass would yield the same results + if ( numAutoWithStretch > 0 || numAutoWithoutStretch == 0 ) + break; + } + + qGeomCalc( a, 0, n, pick( r.topLeft() ), pick( r.size() ), 0 ); + + for ( int i = 0; i < n; i++ ) { + QSplitterLayoutStruct *s = d->list.at(i); + setGeo( s->wid, a[i].pos, a[i].size, FALSE ); + } +} + +void QSplitter::recalc( bool update ) +{ + int fi = 2 * frameWidth(); + int maxl = fi; + int minl = fi; + int maxt = QWIDGETSIZE_MAX; + int mint = fi; + int n = d->list.count(); + bool first = TRUE; + + /* + Splitter handles before the first visible widget or right + before a hidden widget must be hidden. + */ + for ( int i = 0; i < n; i++ ) { + QSplitterLayoutStruct *s = d->list.at( i ); + if ( !s->isHandle ) { + QSplitterLayoutStruct *p = 0; + if ( i > 0 ) + p = d->list.at( i - 1 ); + + // may trigger new recalc + if ( p && p->isHandle ) + p->wid->setHidden( first || s->wid->isHidden() ); + + if ( !s->wid->isHidden() ) + first = FALSE; + } + } + + bool empty = TRUE; + for ( int j = 0; j < n; j++ ) { + QSplitterLayoutStruct *s = d->list.at( j ); + if ( !s->wid->isHidden() ) { + empty = FALSE; + if ( s->isHandle ) { + minl += s->getSizer( orient ); + maxl += s->getSizer( orient ); + } else { + QSize minS = qSmartMinSize( s->wid ); + minl += pick( minS ); + maxl += pick( s->wid->maximumSize() ); + mint = QMAX( mint, trans(minS) ); + int tm = trans( s->wid->maximumSize() ); + if ( tm > 0 ) + maxt = QMIN( maxt, tm ); + } + } + } + if ( empty ) { + if ( ::qt_cast<QSplitter*>(parentWidget()) ) { + // nested splitters; be nice + maxl = maxt = 0; + } else { + // QSplitter with no children yet + maxl = QWIDGETSIZE_MAX; + } + } else { + maxl = QMIN( maxl, QWIDGETSIZE_MAX ); + } + if ( maxt < mint ) + maxt = mint; + + if ( orient == Horizontal ) { + setMaximumSize( maxl, maxt ); + setMinimumSize( minl, mint ); + } else { + setMaximumSize( maxt, maxl ); + setMinimumSize( mint, minl ); + } + if ( update ) + doResize(); + else + d->firstShow = TRUE; +} + +/*! + \enum QSplitter::ResizeMode + + This enum type describes how QSplitter will resize each of its + child widgets. + + \value Auto The widget will be resized according to the stretch + factors set in its sizePolicy(). + + \value Stretch The widget will be resized when the splitter + itself is resized. + + \value KeepSize QSplitter will try to keep the widget's size + unchanged. + + \value FollowSizeHint QSplitter will resize the widget when the + widget's size hint changes. +*/ + +/*! + Sets resize mode of widget \a w to \a mode. (The default is \c + Auto.) +*/ + +void QSplitter::setResizeMode( QWidget *w, ResizeMode mode ) +{ + findWidget( w )->resizeMode = mode; +} + + +/*! + \property QSplitter::opaqueResize + \brief whether resizing is opaque + + Opaque resizing is off by default. +*/ + +bool QSplitter::opaqueResize() const +{ + return d->opaque; +} + + +void QSplitter::setOpaqueResize( bool on ) +{ + d->opaque = on; +} + + +/*! + Moves widget \a w to the leftmost/top position. +*/ + +void QSplitter::moveToFirst( QWidget *w ) +{ + processChildEvents(); + bool found = FALSE; + QSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( s->wid == w ) { + found = TRUE; + QSplitterLayoutStruct *p = d->list.prev(); + if ( p ) { // not already at first place + d->list.take(); // take p + d->list.take(); // take s + d->list.prepend( p ); + d->list.prepend( s ); + } + break; + } + s = d->list.next(); + } + if ( !found ) + addWidget( w, TRUE ); + recalcId(); +} + + +/*! + Moves widget \a w to the rightmost/bottom position. +*/ + +void QSplitter::moveToLast( QWidget *w ) +{ + processChildEvents(); + bool found = FALSE; + QSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( s->wid == w ) { + found = TRUE; + d->list.take(); // take s + QSplitterLayoutStruct *p = d->list.current(); + if ( p ) { // the splitter handle after s + d->list.take(); // take p + d->list.append( p ); + } + d->list.append( s ); + break; + } + s = d->list.next(); + } + if ( !found ) + addWidget( w ); + recalcId(); +} + + +void QSplitter::recalcId() +{ + int n = d->list.count(); + for ( int i = 0; i < n; i++ ) { + QSplitterLayoutStruct *s = d->list.at( i ); + if ( s->isHandle ) + ((QSplitterHandle*)s->wid)->setId( i ); + } +} + + +/*! + \reimp +*/ +QSize QSplitter::sizeHint() const +{ + constPolish(); + int l = 0; + int t = 0; + if ( children() ) { + const QObjectList * c = children(); + QObjectListIt it( *c ); + QObject * o; + + while( (o = it.current()) != 0 ) { + ++it; + if ( o->isWidgetType() && !((QWidget*)o)->isHidden() ) { + QSize s = ((QWidget*)o)->sizeHint(); + if ( s.isValid() ) { + l += pick( s ); + t = QMAX( t, trans( s ) ); + } + } + } + } + return orientation() == Horizontal ? QSize( l, t ) : QSize( t, l ); +} + + +/*! + \reimp +*/ + +QSize QSplitter::minimumSizeHint() const +{ + constPolish(); + int l = 0; + int t = 0; + if ( children() ) { + const QObjectList * c = children(); + QObjectListIt it( *c ); + QObject * o; + + while ( (o = it.current()) != 0 ) { + ++it; + if ( o->isWidgetType() && !((QWidget*)o)->isHidden() ) { + QSize s = qSmartMinSize( (QWidget*)o ); + if ( s.isValid() ) { + l += pick( s ); + t = QMAX( t, trans( s ) ); + } + } + } + } + return orientation() == Horizontal ? QSize( l, t ) : QSize( t, l ); +} + + +void QSplitter::storeSizes() +{ + QSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( !s->isHandle ) + s->sizer = pick( s->wid->size() ); + s = d->list.next(); + } +} + + +void QSplitter::addContribution( int id, int *min, int *max, + bool mayCollapse ) +{ + QSplitterLayoutStruct *s = d->list.at( id ); + if ( !s->wid->isHidden() ) { + if ( s->isHandle ) { + *min += s->getSizer( orient ); + *max += s->getSizer( orient ); + } else { + if ( mayCollapse || !isCollapsed(s->wid) ) + *min += pick( qSmartMinSize(s->wid) ); + *max += pick( s->wid->maximumSize() ); + } + } +} + + +/*! + Returns a list of the size parameters of all the widgets in this + splitter. + + If the splitter's orientation is horizontal, the list is a list of + widget widths; if the orientation is vertical, the list is a list + of widget heights. + + Giving the values to another splitter's setSizes() function will + produce a splitter with the same layout as this one. + + Note that if you want to iterate over the list, you should iterate + over a copy, e.g. + \code + QValueList<int> list = mySplitter.sizes(); + QValueList<int>::Iterator it = list.begin(); + while( it != list.end() ) { + myProcessing( *it ); + ++it; + } + \endcode + + \sa setSizes() +*/ + +QValueList<int> QSplitter::sizes() const +{ + if ( !testWState(WState_Polished) ) + constPolish(); + + QValueList<int> list; + QSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( !s->isHandle ) + list.append( isCollapsed(s->wid) ? 0 : pick(s->wid->size())); + s = d->list.next(); + } + return list; +} + +/*! + Sets the size parameters to the values given in the \a list. If + the splitter is horizontal, the values set the widths of each + widget going from left to right. If the splitter is vertical, the + values set the heights of each widget going from top to bottom. + Extra values in the \a list are ignored. + + If \a list contains too few values, the result is undefined but + the program will still be well-behaved. + + Note that the values in \a list should be the height/width that + the widgets should be resized to. + + \sa sizes() +*/ + +void QSplitter::setSizes( QValueList<int> list ) +{ + processChildEvents(); + QValueList<int>::Iterator it = list.begin(); + QSplitterLayoutStruct *s = d->list.first(); + while ( s && it != list.end() ) { + if ( !s->isHandle ) { + s->sizer = QMAX( *it, 0 ); + int smartMinSize = pick( qSmartMinSize(s->wid) ); + // Make sure that we reset the collapsed state. + if ( s->sizer == 0 ) { + if ( collapsible(s) && smartMinSize > 0 ) { + s->wid->move( -1, -1 ); + } else { + s->sizer = smartMinSize; + s->wid->move( 0, 0 ); + } + } else { + if ( s->sizer < smartMinSize ) + s->sizer = smartMinSize; + s->wid->move( 0, 0 ); + } + ++it; + } + s = d->list.next(); + } + doResize(); +} + +/*! + \property QSplitter::handleWidth + \brief the width of the splitter handle +*/ + +int QSplitter::handleWidth() const +{ + if ( d->handleWidth > 0 ) { + return d->handleWidth; + } else { + return style().pixelMetric( QStyle::PM_SplitterWidth, this ); + } +} + +void QSplitter::setHandleWidth( int width ) +{ + d->handleWidth = width; + updateHandles(); +} + +/*! + Processes all posted child events, ensuring that the internal state of + the splitter is kept consistent. +*/ + +void QSplitter::processChildEvents() +{ + QApplication::sendPostedEvents( this, QEvent::ChildInserted ); +} + +/*! + \reimp +*/ + +void QSplitter::styleChange( QStyle& old ) +{ + updateHandles(); + QFrame::styleChange( old ); +} + +void QSplitter::updateHandles() +{ + int hw = handleWidth(); + QSplitterLayoutStruct *s = d->list.first(); + while ( s ) { + if ( s->isHandle ) + s->sizer = hw; + s = d->list.next(); + } + recalc( isVisible() ); +} + +#ifndef QT_NO_TEXTSTREAM +/*! + \relates QSplitter + + Writes the sizes and the hidden state of the widgets in the + splitter \a splitter to the text stream \a ts. + + \sa operator>>(), sizes(), QWidget::isHidden() +*/ + +QTextStream& operator<<( QTextStream& ts, const QSplitter& splitter ) +{ + QSplitterLayoutStruct *s = splitter.d->list.first(); + bool first = TRUE; + ts << "["; + + while ( s != 0 ) { + if ( !s->isHandle ) { + if ( !first ) + ts << ","; + + if ( s->wid->isHidden() ) { + ts << "H"; + } else if ( isCollapsed(s->wid) ) { + ts << 0; + } else { + ts << s->getSizer( splitter.orientation() ); + } + first = FALSE; + } + s = splitter.d->list.next(); + } + ts << "]" << endl; + return ts; +} + +/*! + \relates QSplitter + + Reads the sizes and the hidden state of the widgets in the + splitter \a splitter from the text stream \a ts. The sizes must + have been previously written by the operator<<() function. + + \sa operator<<(), setSizes(), QWidget::hide() +*/ + +QTextStream& operator>>( QTextStream& ts, QSplitter& splitter ) +{ +#undef SKIP_SPACES +#define SKIP_SPACES() \ + while ( line[i].isSpace() ) \ + i++ + + splitter.processChildEvents(); + QSplitterLayoutStruct *s = splitter.d->list.first(); + QString line = ts.readLine(); + int i = 0; + + SKIP_SPACES(); + if ( line[i] == '[' ) { + i++; + SKIP_SPACES(); + while ( line[i] != ']' ) { + while ( s != 0 && s->isHandle ) + s = splitter.d->list.next(); + if ( s == 0 ) + break; + + if ( line[i].upper() == 'H' ) { + s->wid->hide(); + i++; + } else { + s->wid->show(); + int dim = 0; + while ( line[i].digitValue() >= 0 ) { + dim *= 10; + dim += line[i].digitValue(); + i++; + } + s->sizer = dim; + if ( dim == 0 ) + splitter.setGeo( s->wid, 0, 0, FALSE ); + } + SKIP_SPACES(); + if ( line[i] == ',' ) { + i++; + } else { + break; + } + SKIP_SPACES(); + s = splitter.d->list.next(); + } + } + splitter.doResize(); + return ts; +} +#endif + +#endif diff --git a/src/widgets/qsplitter.h b/src/widgets/qsplitter.h new file mode 100644 index 0000000..97b95ad --- /dev/null +++ b/src/widgets/qsplitter.h @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Definition of QSplitter class +** +** Created : 980105 +** +** 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. +** +**********************************************************************/ + +#ifndef QSPLITTER_H +#define QSPLITTER_H + +#ifndef QT_H +#include "qframe.h" +#include "qvaluelist.h" +#endif // QT_H + +#ifndef QT_NO_SPLITTER + +class QSplitterHandle; +class QSplitterPrivate; +class QSplitterLayoutStruct; +class QTextStream; + +class Q_EXPORT QSplitter : public QFrame +{ + Q_OBJECT + Q_PROPERTY( Orientation orientation READ orientation WRITE setOrientation ) + Q_PROPERTY( bool opaqueResize READ opaqueResize WRITE setOpaqueResize ) + Q_PROPERTY( int handleWidth READ handleWidth WRITE setHandleWidth ) + Q_PROPERTY( bool childrenCollapsible READ childrenCollapsible WRITE setChildrenCollapsible ) + +public: + // ### Qt 4.0: remove Auto from public API + enum ResizeMode { Stretch, KeepSize, FollowSizeHint, Auto }; + + QSplitter( QWidget* parent = 0, const char* name = 0 ); + QSplitter( Orientation, QWidget* parent = 0, const char* name = 0 ); + ~QSplitter(); + + virtual void setOrientation( Orientation ); + Orientation orientation() const { return orient; } + + // ### Qt 4.0: make setChildrenCollapsible() and setCollapsible() virtual + + void setChildrenCollapsible( bool ); + bool childrenCollapsible() const; + + void setCollapsible( QWidget *w, bool ); + virtual void setResizeMode( QWidget *w, ResizeMode ); + virtual void setOpaqueResize( bool = TRUE ); + bool opaqueResize() const; + + void moveToFirst( QWidget * ); + void moveToLast( QWidget * ); + + void refresh() { recalc( TRUE ); } + QSize sizeHint() const; + QSize minimumSizeHint() const; + + QValueList<int> sizes() const; + void setSizes( QValueList<int> ); + + int handleWidth() const; + void setHandleWidth( int ); + +protected: + void childEvent( QChildEvent * ); + + bool event( QEvent * ); + void resizeEvent( QResizeEvent * ); + + int idAfter( QWidget* ) const; + + void moveSplitter( QCOORD pos, int id ); + virtual void drawSplitter( QPainter*, QCOORD x, QCOORD y, + QCOORD w, QCOORD h ); + void styleChange( QStyle& ); + int adjustPos( int, int ); + virtual void setRubberband( int ); + void getRange( int id, int *, int * ); + +private: + enum { DefaultResizeMode = 3 }; + + void init(); + void recalc( bool update = FALSE ); + void doResize(); + void storeSizes(); + void getRange( int id, int *, int *, int *, int * ); + void addContribution( int, int *, int *, bool ); + int adjustPos( int, int, int *, int *, int *, int * ); + bool collapsible( QSplitterLayoutStruct * ); + void processChildEvents(); + QSplitterLayoutStruct *findWidget( QWidget * ); + QSplitterLayoutStruct *addWidget( QWidget *, bool prepend = FALSE ); + void recalcId(); + void doMove( bool backwards, int pos, int id, int delta, bool upLeft, + bool mayCollapse ); + void setGeo( QWidget *w, int pos, int size, bool splitterMoved ); + int findWidgetJustBeforeOrJustAfter( int id, int delta, int &collapsibleSize ); + void updateHandles(); + + inline QCOORD pick( const QPoint &p ) const + { return orient == Horizontal ? p.x() : p.y(); } + inline QCOORD pick( const QSize &s ) const + { return orient == Horizontal ? s.width() : s.height(); } + + inline QCOORD trans( const QPoint &p ) const + { return orient == Vertical ? p.x() : p.y(); } + inline QCOORD trans( const QSize &s ) const + { return orient == Vertical ? s.width() : s.height(); } + + QSplitterPrivate *d; + + Orientation orient; + friend class QSplitterHandle; + +#ifndef QT_NO_TEXTSTREAM + friend Q_EXPORT QTextStream& operator<<( QTextStream&, const QSplitter& ); + friend Q_EXPORT QTextStream& operator>>( QTextStream&, QSplitter& ); +#endif + +private: +#if defined(Q_DISABLE_COPY) + QSplitter( const QSplitter & ); + QSplitter& operator=( const QSplitter & ); +#endif +}; + +#ifndef QT_NO_TEXTSTREAM +Q_EXPORT QTextStream& operator<<( QTextStream&, const QSplitter& ); +Q_EXPORT QTextStream& operator>>( QTextStream&, QSplitter& ); +#endif + +#endif // QT_NO_SPLITTER + +#endif // QSPLITTER_H diff --git a/src/widgets/qstatusbar.cpp b/src/widgets/qstatusbar.cpp new file mode 100644 index 0000000..9a57704 --- /dev/null +++ b/src/widgets/qstatusbar.cpp @@ -0,0 +1,526 @@ +/**************************************************************************** +** +** Implementation of QStatusBar class +** +** Created : 980119 +** +** 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 "qstatusbar.h" +#ifndef QT_NO_STATUSBAR + +#include "qptrlist.h" +#include "qlayout.h" +#include "qpainter.h" +#include "qtimer.h" +#include "qdrawutil.h" +#include "qstyle.h" +#include "qsizegrip.h" + +/*! + \class QStatusBar qstatusbar.h + \brief The QStatusBar class provides a horizontal bar suitable for + presenting status information. + + \ingroup application + \ingroup helpsystem + \mainclass + + Each status indicator falls into one of three categories: + + \list + \i \e Temporary - briefly occupies most of the status bar. Used + to explain tool tip texts or menu entries, for example. + \i \e Normal - occupies part of the status bar and may be hidden + by temporary messages. Used to display the page and line + number in a word processor, for example. + \i \e Permanent - is never hidden. Used for important mode + indications, for example, some applications put a Caps Lock + indicator in the status bar. + \endlist + + QStatusBar lets you display all three types of indicators. + + To display a \e temporary message, call message() (perhaps by + connecting a suitable signal to it). To remove a temporary + message, call clear(). There are two variants of message(): one + that displays the message until the next clear() or message() and + one that has a time limit: + + \code + connect( loader, SIGNAL(progressMessage(const QString&)), + statusBar(), SLOT(message(const QString&)) ); + + statusBar()->message("Loading..."); // Initial message + loader.loadStuff(); // Emits progress messages + statusBar()->message("Done.", 2000); // Final message for 2 seconds + \endcode + + \e Normal and \e Permanent messages are displayed by creating a + small widget and then adding it to the status bar with + addWidget(). Widgets like QLabel, QProgressBar or even QToolButton + are useful for adding to status bars. removeWidget() is used to + remove widgets. + + \code + statusBar()->addWidget(new MyReadWriteIndication(statusBar())); + \endcode + + By default QStatusBar provides a QSizeGrip in the lower-right + corner. You can disable it with setSizeGripEnabled(FALSE); + + <img src=qstatusbar-m.png> <img src=qstatusbar-w.png> + + \sa QToolBar QMainWindow QLabel + \link guibooks.html#fowler GUI Design Handbook: Status Bar.\endlink +*/ + + +class QStatusBarPrivate +{ +public: + QStatusBarPrivate() {} + + struct SBItem { + SBItem( QWidget* widget, int stretch, bool permanent ) + : s( stretch ), w( widget ), p( permanent ) {} + int s; + QWidget * w; + bool p; + }; + + QPtrList<SBItem> items; + QString tempItem; + + QBoxLayout * box; + QTimer * timer; + +#ifndef QT_NO_SIZEGRIP + QSizeGrip * resizer; +#endif + + int savedStrut; +}; + + +/*! + Constructs a status bar called \a name with parent \a parent and + with a size grip. + + \sa setSizeGripEnabled() +*/ +QStatusBar::QStatusBar( QWidget * parent, const char *name ) + : QWidget( parent, name ) +{ + d = new QStatusBarPrivate; + d->items.setAutoDelete( TRUE ); + d->box = 0; + d->timer = 0; + +#ifndef QT_NO_SIZEGRIP + d->resizer = 0; + setSizeGripEnabled(TRUE); // causes reformat() +#else + reformat(); +#endif +} + + +/*! + Destroys the status bar and frees any allocated resources and + child widgets. +*/ +QStatusBar::~QStatusBar() +{ + delete d; + d = 0; +} + + +/*! + Adds \a widget to this status bar. \a widget is reparented if it + isn't already a child of the QStatusBar. + + \a widget is permanently visible if \a permanent is TRUE and may + be obscured by temporary messages if \a permanent is FALSE. The + default is FALSE. + + If \a permanent is TRUE, \a widget is located at the far right of + the status bar. If \a permanent is FALSE (the default), \a widget + is located just to the left of the first permanent widget. + + \a stretch is used to compute a suitable size for \a widget as the + status bar grows and shrinks. The default of 0 uses a minimum of + space. + + This function may cause some flicker. + + \sa removeWidget() +*/ + +void QStatusBar::addWidget( QWidget * widget, int stretch, bool permanent ) +{ + if ( !widget ) { +#if defined(QT_CHECK_NULL) + qWarning( "QStatusBar::addWidget(): Cannot add null widget" ); +#endif + return; + } + + if ( widget->parentWidget() != this ) + widget->reparent( this, QPoint(0, 0), TRUE ); + + QStatusBarPrivate::SBItem* item + = new QStatusBarPrivate::SBItem( widget, stretch, permanent ); + + d->items.last(); + while( !permanent && d->items.current() && d->items.current()->p ) + d->items.prev(); + + d->items.insert( d->items.at() >= 0 ? d->items.at()+1 : 0, item ); + + if ( !d->tempItem.isEmpty() && !permanent ) + widget->hide(); + + reformat(); +} + + +/*! + Removes \a widget from the status bar. + + This function may cause some flicker. + + Note that \a widget is not deleted. + + \sa addWidget() +*/ + +void QStatusBar::removeWidget( QWidget* widget ) +{ + if ( !widget ) + return; + bool found = FALSE; + QStatusBarPrivate::SBItem* item = d->items.first(); + while ( item && !found ) { + if ( item->w == widget ) { + d->items.remove(); + found = TRUE; + } + item = d->items.next(); + } + + if ( found ) + reformat(); +#if defined(QT_DEBUG) + else + qDebug( "QStatusBar::removeWidget(): Widget not found." ); +#endif +} + +/*! + \property QStatusBar::sizeGripEnabled + \brief whether the QSizeGrip in the bottom right of the status bar is enabled + + Enables or disables the QSizeGrip in the bottom right of the + status bar. By default, the size grip is enabled. +*/ + +bool QStatusBar::isSizeGripEnabled() const +{ +#ifdef QT_NO_SIZEGRIP + return FALSE; +#else + return !!d->resizer; +#endif +} + +void QStatusBar::setSizeGripEnabled(bool enabled) +{ +#ifndef QT_NO_SIZEGRIP + if ( !enabled != !d->resizer ) { + if ( enabled ) { + d->resizer = new QSizeGrip( this, "QStatusBar::resizer" ); + } else { + delete d->resizer; + d->resizer = 0; + } + reformat(); + if ( d->resizer && isVisible() ) + d->resizer->show(); + } +#endif +} + + +/*! + Changes the status bar's appearance to account for item changes. + Special subclasses may need this, but geometry management will + usually take care of any necessary rearrangements. +*/ +void QStatusBar::reformat() +{ + if ( d->box ) + delete d->box; + + QBoxLayout *vbox; + if ( isSizeGripEnabled() ) { + d->box = new QHBoxLayout( this ); + vbox = new QVBoxLayout( d->box ); + } else { + vbox = d->box = new QVBoxLayout( this ); + } + vbox->addSpacing( 3 ); + QBoxLayout* l = new QHBoxLayout( vbox ); + l->addSpacing( 3 ); + l->setSpacing( 4 ); + + int maxH = fontMetrics().height(); + + QStatusBarPrivate::SBItem* item = d->items.first(); + while ( item && !item->p ) { + l->addWidget( item->w, item->s ); + int itemH = QMIN(item->w->sizeHint().height(), + item->w->maximumHeight()); + maxH = QMAX( maxH, itemH ); + item = d->items.next(); + } + + l->addStretch( 0 ); + + while ( item ) { + l->addWidget( item->w, item->s ); + int itemH = QMIN(item->w->sizeHint().height(), + item->w->maximumHeight()); + maxH = QMAX( maxH, itemH ); + item = d->items.next(); + } + l->addSpacing( 4 ); +#ifndef QT_NO_SIZEGRIP + if ( d->resizer ) { + maxH = QMAX( maxH, d->resizer->sizeHint().height() ); + d->box->addSpacing( 1 ); + d->box->addWidget( d->resizer, 0, AlignBottom ); + } +#endif + l->addStrut( maxH ); + d->savedStrut = maxH; + vbox->addSpacing( 2 ); + d->box->activate(); + repaint(); +} + + + + +/*! + Hides the normal status indicators and displays \a message until + clear() or another message() is called. + + \sa clear() +*/ +void QStatusBar::message( const QString &message ) +{ + if ( d->tempItem == message ) + return; + d->tempItem = message; + if ( d->timer ) { + delete d->timer; + d->timer = 0; + } + hideOrShow(); +} + + +/*! + \overload + + Hides the normal status indications and displays \a message for \a + ms milli-seconds or until clear() or another message() is called, + whichever occurs first. +*/ +void QStatusBar::message( const QString &message, int ms ) +{ + d->tempItem = message; + + if ( !d->timer ) { + d->timer = new QTimer( this ); + connect( d->timer, SIGNAL(timeout()), this, SLOT(clear()) ); + } + if ( ms > 0 ) { + d->timer->start( ms ); + } else if ( d->timer ) { + delete d->timer; + d->timer = 0; + } + + hideOrShow(); +} + + +/*! + Removes any temporary message being shown. + + \sa message() +*/ + +void QStatusBar::clear() +{ + if ( d->tempItem.isEmpty() ) + return; + if ( d->timer ) { + delete d->timer; + d->timer = 0; + } + d->tempItem = QString::null; + hideOrShow(); +} + +/*! + \fn QStatusBar::messageChanged( const QString &message ) + + This signal is emitted when the temporary status messages + changes. \a message is the new temporary message, and is a + null-string when the message has been removed. + + \sa message(), clear() +*/ + +/*! + Ensures that the right widgets are visible. Used by message() and + clear(). +*/ +void QStatusBar::hideOrShow() +{ + bool haveMessage = !d->tempItem.isEmpty(); + + QStatusBarPrivate::SBItem* item = d->items.first(); + + while( item && !item->p ) { + if ( haveMessage ) + item->w->hide(); + else + item->w->show(); + item = d->items.next(); + } + + emit messageChanged( d->tempItem ); + repaint(); +} + + +/*! + Shows the temporary message, if appropriate. +*/ +void QStatusBar::paintEvent( QPaintEvent * ) +{ + bool haveMessage = !d->tempItem.isEmpty(); + + QPainter p( this ); + QStatusBarPrivate::SBItem* item = d->items.first(); + +#ifndef QT_NO_SIZEGRIP + int psx = ( d->resizer && d->resizer->isVisible() ) ? d->resizer->x() : width()-12; +#else + int psx = width() - 12; +#endif + + while ( item ) { + if ( !haveMessage || item->p ) + if ( item->w->isVisible() ) { + if ( item->p && item->w->x()-1 < psx ) + psx = item->w->x()-1; + style().drawPrimitive( QStyle::PE_StatusBarSection, &p, + QRect(item->w->x() - 1, item->w->y() - 1, + item->w->width()+2, item->w->height()+2), + colorGroup(), QStyle::Style_Default, + QStyleOption(item->w) ); + } + item = d->items.next(); + } + if ( haveMessage ) { + p.setPen( colorGroup().foreground() ); + p.drawText( 6, 0, psx, height(), AlignVCenter | SingleLine, d->tempItem ); + } +} + +/*! + \reimp +*/ +void QStatusBar::resizeEvent( QResizeEvent * e ) +{ + QWidget::resizeEvent( e ); +} + +/*! + \reimp +*/ + +bool QStatusBar::event( QEvent *e ) +{ + if ( e->type() == QEvent::LayoutHint ) { + // Calculate new strut height and call reformat() if it has changed + int maxH = fontMetrics().height(); + + QStatusBarPrivate::SBItem* item = d->items.first(); + while ( item ) { + int itemH = QMIN(item->w->sizeHint().height(), + item->w->maximumHeight()); + maxH = QMAX( maxH, itemH ); + item = d->items.next(); + } + +#ifndef QT_NO_SIZEGRIP + if ( d->resizer ) + maxH = QMAX( maxH, d->resizer->sizeHint().height() ); +#endif + + if ( maxH != d->savedStrut ) + reformat(); + else + update(); + } + if ( e->type() == QEvent::ChildRemoved ) { + QStatusBarPrivate::SBItem* item = d->items.first(); + while ( item ) { + if ( item->w == ( (QChildEvent*)e )->child() ) + d->items.removeRef( item ); + item = d->items.next(); + } + } + return QWidget::event( e ); +} + +#endif diff --git a/src/widgets/qstatusbar.h b/src/widgets/qstatusbar.h new file mode 100644 index 0000000..12d50f2 --- /dev/null +++ b/src/widgets/qstatusbar.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Definition of QStatusBar class +** +** Created : 980316 +** +** 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. +** +**********************************************************************/ + +#ifndef QSTATUSBAR_H +#define QSTATUSBAR_H + +#ifndef QT_H +#include "qwidget.h" +#endif // QT_H + +#ifndef QT_NO_STATUSBAR + + +class QStatusBarPrivate; + + +class Q_EXPORT QStatusBar: public QWidget +{ + Q_OBJECT + Q_PROPERTY( bool sizeGripEnabled READ isSizeGripEnabled WRITE setSizeGripEnabled ) + +public: + QStatusBar( QWidget* parent=0, const char* name=0 ); + virtual ~QStatusBar(); + + virtual void addWidget( QWidget *, int stretch = 0, bool = FALSE ); + virtual void removeWidget( QWidget * ); + + void setSizeGripEnabled(bool); + bool isSizeGripEnabled() const; + +public slots: + void message( const QString &); + void message( const QString &, int ); + void clear(); + +signals: + void messageChanged( const QString &text ); + +protected: + void paintEvent( QPaintEvent * ); + void resizeEvent( QResizeEvent * ); + + void reformat(); + void hideOrShow(); + bool event( QEvent *); + +private: + QStatusBarPrivate * d; +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QStatusBar( const QStatusBar & ); + QStatusBar& operator=( const QStatusBar & ); +#endif +}; + +#endif // QT_NO_STATUSBAR + +#endif // QSTATUSBAR_H diff --git a/src/widgets/qsyntaxhighlighter.cpp b/src/widgets/qsyntaxhighlighter.cpp new file mode 100644 index 0000000..220bdc7 --- /dev/null +++ b/src/widgets/qsyntaxhighlighter.cpp @@ -0,0 +1,221 @@ +/**************************************************************************** +** +** Implementation of the QSyntaxHighlighter class +** +** Created : 990101 +** +** 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 "qsyntaxhighlighter.h" +#include "private/qsyntaxhighlighter_p.h" + +#ifndef QT_NO_SYNTAXHIGHLIGHTER +#include "../kernel/qrichtext_p.h" +#include "qtextedit.h" +#include "qtimer.h" + +/*! + \class QSyntaxHighlighter qsyntaxhighlighter.h + \brief The QSyntaxHighlighter class is a base class for + implementing QTextEdit syntax highlighters. + + \ingroup basic + \ingroup text + + A syntax highligher automatically highlights parts of the text in + a QTextEdit. Syntax highlighters are often used when the user is + entering text in a specific format (for example, source code) and + help the user to read the text and identify syntax errors. + + To provide your own syntax highlighting for QTextEdit, you must + subclass QSyntaxHighlighter and reimplement highlightParagraph(). + + When you create an instance of your QSyntaxHighlighter subclass, + pass it the QTextEdit that you want the syntax highlighting to be + applied to. After this your highlightParagraph() function will be + called automatically whenever necessary. Use your + highlightParagraph() function to apply formatting (e.g. setting + the font and color) to the text that is passed to it. +*/ + +/*! + Constructs the QSyntaxHighlighter and installs it on \a textEdit. + + It is the caller's responsibility to delete the + QSyntaxHighlighter when it is no longer needed. +*/ + +QSyntaxHighlighter::QSyntaxHighlighter( QTextEdit *textEdit ) + : para( 0 ), edit( textEdit ), d( new QSyntaxHighlighterPrivate ) +{ + textEdit->document()->setPreProcessor( new QSyntaxHighlighterInternal( this ) ); + textEdit->document()->invalidate(); + QTimer::singleShot( 0, textEdit->viewport(), SLOT( update() ) ); +} + +/*! + Destructor. Uninstalls this syntax highlighter from the textEdit() +*/ + +QSyntaxHighlighter::~QSyntaxHighlighter() +{ + delete d; + textEdit()->document()->setPreProcessor( 0 ); +} + +/*! + \fn int QSyntaxHighlighter::highlightParagraph( const QString &text, int endStateOfLastPara ) + + This function is called when necessary by the rich text engine, + i.e. on paragraphs which have changed. + + In your reimplementation you should parse the paragraph's \a text + and call setFormat() as often as necessary to apply any font and + color changes that you require. Your function must return a value + which indicates the paragraph's end state: see below. + + Some syntaxes can have constructs that span paragraphs. For + example, a C++ syntax highlighter should be able to cope with + \c{/}\c{*...*}\c{/} comments that span paragraphs. To deal + with these cases it is necessary to know the end state of the + previous paragraph (e.g. "in comment"). + + If your syntax does not have paragraph spanning constructs, simply + ignore the \a endStateOfLastPara parameter and always return 0. + + Whenever highlightParagraph() is called it is passed a value for + \a endStateOfLastPara. For the very first paragraph this value is + always -2. For any other paragraph the value is the value returned + by the most recent highlightParagraph() call that applied to the + preceding paragraph. + + The value you return is up to you. We recommend only returning 0 + (to signify that this paragraph's syntax highlighting does not + affect the following paragraph), or a positive integer (to signify + that this paragraph has ended in the middle of a paragraph + spanning construct). + + To find out which paragraph is highlighted, call + currentParagraph(). + + For example, if you're writing a simple C++ syntax highlighter, + you might designate 1 to signify "in comment". For a paragraph + that ended in the middle of a comment you'd return 1, and for + other paragraphs you'd return 0. In your parsing code if \a + endStateOfLastPara was 1, you would highlight the text as a C++ + comment until you reached the closing \c{*}\c{/}. +*/ + +/*! + This function is applied to the syntax highlighter's current + paragraph (the text of which is passed to the highlightParagraph() + function). + + The specified \a font and \a color are applied to the text from + position \a start for \a count characters. (If \a count is 0, + nothing is done.) +*/ + +void QSyntaxHighlighter::setFormat( int start, int count, const QFont &font, const QColor &color ) +{ + if ( !para || count <= 0 ) + return; + QTextFormat *f = 0; + f = para->document()->formatCollection()->format( font, color ); + para->setFormat( start, count, f ); + f->removeRef(); +} + +/*! \overload */ + +void QSyntaxHighlighter::setFormat( int start, int count, const QColor &color ) +{ + if ( !para || count <= 0 ) + return; + QTextFormat *f = 0; + QFont fnt = textEdit()->QWidget::font(); + f = para->document()->formatCollection()->format( fnt, color ); + para->setFormat( start, count, f ); + f->removeRef(); +} + +/*! \overload */ + +void QSyntaxHighlighter::setFormat( int start, int count, const QFont &font ) +{ + if ( !para || count <= 0 ) + return; + QTextFormat *f = 0; + QColor c = textEdit()->viewport()->paletteForegroundColor(); + f = para->document()->formatCollection()->format( font, c ); + para->setFormat( start, count, f ); + f->removeRef(); +} + +/*! + \fn QTextEdit *QSyntaxHighlighter::textEdit() const + + Returns the QTextEdit on which this syntax highlighter is + installed +*/ + +/*! Redoes the highlighting of the whole document. +*/ + +void QSyntaxHighlighter::rehighlight() +{ + QTextParagraph *s = edit->document()->firstParagraph(); + while ( s ) { + s->invalidate( 0 ); + s->state = -1; + s->needPreProcess = TRUE; + s = s->next(); + } + edit->repaintContents( FALSE ); +} + +/*! + Returns the id of the paragraph which is highlighted, or -1 of no + paragraph is currently highlighted. + + Usually this function is called from within highlightParagraph(). +*/ + +int QSyntaxHighlighter::currentParagraph() const +{ + return d->currentParagraph; +} + +#endif diff --git a/src/widgets/qsyntaxhighlighter.h b/src/widgets/qsyntaxhighlighter.h new file mode 100644 index 0000000..5dbefea --- /dev/null +++ b/src/widgets/qsyntaxhighlighter.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Definition of the QSyntaxHighlighter class +** +** Created : 022407 +** +** 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. +** +**********************************************************************/ + +#ifndef QSYNTAXHIGHLIGHTER_H +#define QSYNTAXHIGHLIGHTER_H + +#ifndef QT_H +#include "qfont.h" +#include "qcolor.h" +#include "qstring.h" +#endif // QT_H + +class QTextEdit; +class QSyntaxHighlighterInternal; +class QSyntaxHighlighterPrivate; +class QTextParagraph; + +class Q_EXPORT QSyntaxHighlighter : public Qt +{ + friend class QSyntaxHighlighterInternal; + +public: + QSyntaxHighlighter( QTextEdit *textEdit ); + virtual ~QSyntaxHighlighter(); + + virtual int highlightParagraph( const QString &text, int endStateOfLastPara ) = 0; + + void setFormat( int start, int count, const QFont &font, const QColor &color ); + void setFormat( int start, int count, const QColor &color ); + void setFormat( int start, int count, const QFont &font ); + QTextEdit *textEdit() const { return edit; } + + void rehighlight(); + + int currentParagraph() const; + +private: + QTextParagraph *para; + QTextEdit *edit; + QSyntaxHighlighterPrivate *d; + +}; + +#endif diff --git a/src/widgets/qsyntaxhighlighter_p.h b/src/widgets/qsyntaxhighlighter_p.h new file mode 100644 index 0000000..9d1ad4d --- /dev/null +++ b/src/widgets/qsyntaxhighlighter_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Definition of the internal QSyntaxHighlighterInternal class +** +** Created : 031111 +** +** 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. +** +**********************************************************************/ + +#ifndef QSYNTAXHIGHLIGHTER_P_H +#define QSYNTAXHIGHLIGHTER_P_H + +#ifndef QT_NO_SYNTAXHIGHLIGHTER +#include "qsyntaxhighlighter.h" +#include "private/qrichtext_p.h" + +class QSyntaxHighlighterPrivate +{ +public: + QSyntaxHighlighterPrivate() : + currentParagraph( -1 ) + {} + + int currentParagraph; +}; + +class QSyntaxHighlighterInternal : public QTextPreProcessor +{ +public: + QSyntaxHighlighterInternal( QSyntaxHighlighter *h ) : highlighter( h ) {} + void process( QTextDocument *doc, QTextParagraph *p, int, bool invalidate ) { + if ( p->prev() && p->prev()->endState() == -1 ) + process( doc, p->prev(), 0, FALSE ); + + highlighter->para = p; + QString text = p->string()->toString(); + int endState = p->prev() ? p->prev()->endState() : -2; + int oldEndState = p->endState(); + highlighter->d->currentParagraph = p->paragId(); + p->setEndState( highlighter->highlightParagraph( text, endState ) ); + highlighter->d->currentParagraph = -1; + highlighter->para = 0; + + p->setFirstPreProcess( FALSE ); + QTextParagraph *op = p; + p = p->next(); + if ( (!!oldEndState || !!op->endState()) && oldEndState != op->endState() && + invalidate && p && !p->firstPreProcess() && p->endState() != -1 ) { + while ( p ) { + if ( p->endState() == -1 ) + return; + p->setEndState( -1 ); + p = p->next(); + } + } + } + QTextFormat *format( int ) { return 0; } + +private: + QSyntaxHighlighter *highlighter; + + friend class QTextEdit; +}; + +#endif // QT_NO_SYNTAXHIGHLIGHTER +#endif // QSYNTAXHIGHLIGHTER_P_H diff --git a/src/widgets/qt_widgets.pri b/src/widgets/qt_widgets.pri new file mode 100644 index 0000000..a4917e9 --- /dev/null +++ b/src/widgets/qt_widgets.pri @@ -0,0 +1,139 @@ +# Qt widgets module + +widgets { + WIDGETS_P = widgets + + HEADERS += $$WIDGETS_H/qbuttongroup.h \ + $$WIDGETS_H/qbutton.h \ + $$WIDGETS_P/qdialogbuttons_p.h \ + $$WIDGETS_H/qcheckbox.h \ + $$WIDGETS_H/qcombobox.h \ + $$WIDGETS_P/qwidgetresizehandler_p.h \ + $$WIDGETS_H/qdial.h \ + $$WIDGETS_H/qdockarea.h \ + $$WIDGETS_H/qdockwindow.h \ + $$WIDGETS_H/qframe.h \ + $$WIDGETS_H/qgrid.h \ + $$WIDGETS_H/qgridview.h \ + $$WIDGETS_H/qgroupbox.h \ + $$WIDGETS_H/qhbuttongroup.h \ + $$WIDGETS_H/qheader.h \ + $$WIDGETS_H/qhgroupbox.h \ + $$WIDGETS_H/qhbox.h \ + $$WIDGETS_H/qlabel.h \ + $$WIDGETS_H/qlcdnumber.h \ + $$WIDGETS_H/qlineedit.h \ + $$WIDGETS_H/qlistbox.h \ + $$WIDGETS_H/qlistview.h \ + $$WIDGETS_H/qmainwindow.h \ + $$WIDGETS_H/qmenubar.h \ + $$WIDGETS_H/qmenudata.h \ + $$WIDGETS_H/qmultilineedit.h \ + $$WIDGETS_H/qpopupmenu.h \ + $$WIDGETS_H/qprogressbar.h \ + $$WIDGETS_H/qpushbutton.h \ + $$WIDGETS_H/qradiobutton.h \ + $$WIDGETS_H/qrangecontrol.h \ + $$WIDGETS_H/qscrollbar.h \ + $$WIDGETS_H/qscrollview.h \ + $$WIDGETS_H/qslider.h \ + $$WIDGETS_H/qsplashscreen.h \ + $$WIDGETS_H/qspinbox.h \ + $$WIDGETS_H/qsplitter.h \ + $$WIDGETS_H/qstatusbar.h \ + $$WIDGETS_H/qtabbar.h \ + $$WIDGETS_H/qsyntaxhighlighter.h \ + $$WIDGETS_P/qsyntaxhighlighter_p.h \ + $$WIDGETS_H/qtabwidget.h \ + $$WIDGETS_P/qtitlebar_p.h \ + $$WIDGETS_H/qtoolbar.h \ + $$WIDGETS_H/qtoolbox.h \ + $$WIDGETS_H/qtoolbutton.h \ + $$WIDGETS_H/qtooltip.h \ + $$WIDGETS_H/qvalidator.h \ + $$WIDGETS_H/qvbox.h \ + $$WIDGETS_H/qvbuttongroup.h \ + $$WIDGETS_H/qvgroupbox.h \ + $$WIDGETS_H/qwhatsthis.h \ + $$WIDGETS_H/qwidgetstack.h \ + $$WIDGETS_H/qaction.h \ + $$WIDGETS_H/qdatetimeedit.h \ + $$WIDGETS_H/qtextview.h \ + $$WIDGETS_H/qtextbrowser.h \ + $$WIDGETS_H/qtextedit.h \ + $$WIDGETS_P/qwidgetinterface_p.h \ + $$WIDGETS_H/qwidgetplugin.h + + SOURCES += $$WIDGETS_CPP/qbuttongroup.cpp \ + $$WIDGETS_CPP/qbutton.cpp \ + $$WIDGETS_CPP/qdialogbuttons.cpp \ + $$WIDGETS_CPP/qcheckbox.cpp \ + $$WIDGETS_CPP/qcombobox.cpp \ + $$WIDGETS_CPP/qwidgetresizehandler.cpp \ + $$WIDGETS_CPP/qdial.cpp \ + $$WIDGETS_CPP/qdockarea.cpp \ + $$WIDGETS_CPP/qdockwindow.cpp \ + $$WIDGETS_CPP/qframe.cpp \ + $$WIDGETS_CPP/qgrid.cpp \ + $$WIDGETS_CPP/qgridview.cpp \ + $$WIDGETS_CPP/qgroupbox.cpp \ + $$WIDGETS_CPP/qhbuttongroup.cpp \ + $$WIDGETS_CPP/qheader.cpp \ + $$WIDGETS_CPP/qhgroupbox.cpp \ + $$WIDGETS_CPP/qhbox.cpp \ + $$WIDGETS_CPP/qlabel.cpp \ + $$WIDGETS_CPP/qlcdnumber.cpp \ + $$WIDGETS_CPP/qlineedit.cpp \ + $$WIDGETS_CPP/qlistbox.cpp \ + $$WIDGETS_CPP/qlistview.cpp \ + $$WIDGETS_CPP/qmainwindow.cpp \ + $$WIDGETS_CPP/qmenubar.cpp \ + $$WIDGETS_CPP/qmenudata.cpp \ + $$WIDGETS_CPP/qmultilineedit.cpp \ + $$WIDGETS_CPP/qpopupmenu.cpp \ + $$WIDGETS_CPP/qprogressbar.cpp \ + $$WIDGETS_CPP/qpushbutton.cpp \ + $$WIDGETS_CPP/qradiobutton.cpp \ + $$WIDGETS_CPP/qrangecontrol.cpp \ + $$WIDGETS_CPP/qscrollbar.cpp \ + $$WIDGETS_CPP/qscrollview.cpp \ + $$WIDGETS_CPP/qslider.cpp \ + $$WIDGETS_CPP/qsplashscreen.cpp \ + $$WIDGETS_CPP/qspinbox.cpp \ + $$WIDGETS_CPP/qspinwidget.cpp \ + $$WIDGETS_CPP/qsplitter.cpp \ + $$WIDGETS_CPP/qstatusbar.cpp \ + $$WIDGETS_CPP/qsyntaxhighlighter.cpp \ + $$WIDGETS_CPP/qtabbar.cpp \ + $$WIDGETS_CPP/qtabwidget.cpp \ + $$WIDGETS_CPP/qtitlebar.cpp \ + $$WIDGETS_CPP/qtoolbar.cpp \ + $$WIDGETS_CPP/qtoolbox.cpp \ + $$WIDGETS_CPP/qtoolbutton.cpp \ + $$WIDGETS_CPP/qtooltip.cpp \ + $$WIDGETS_CPP/qvalidator.cpp \ + $$WIDGETS_CPP/qvbox.cpp \ + $$WIDGETS_CPP/qvbuttongroup.cpp \ + $$WIDGETS_CPP/qvgroupbox.cpp \ + $$WIDGETS_CPP/qwhatsthis.cpp \ + $$WIDGETS_CPP/qwidgetstack.cpp \ + $$WIDGETS_CPP/qaction.cpp \ + $$WIDGETS_CPP/qdatetimeedit.cpp \ + $$WIDGETS_CPP/qeffects.cpp \ + $$WIDGETS_CPP/qtextview.cpp \ + $$WIDGETS_CPP/qtextbrowser.cpp \ + $$WIDGETS_CPP/qtextedit.cpp \ + $$WIDGETS_CPP/qwidgetplugin.cpp + !embedded:mac:SOURCES += $$WIDGETS_CPP/qmenubar_mac.cpp +} + +wince-* { + SOURCES += $$WIDGETS_CPP/ce/qcemainwindow.cpp + HEADERS += $$WIDGETS_H/ce/qcemainwindow.h + + SOURCES -= $$WIDGETS_CPP/qsyntaxhighlighter.cpp \ + $$WIDGETS_CPP/qsplashscreen.cpp + + HEADERS -= $$WIDGETS_H/qsyntaxhighlighter.h \ + $$WIDGETS_H/qsplashscreen.h +} diff --git a/src/widgets/qtabbar.cpp b/src/widgets/qtabbar.cpp new file mode 100644 index 0000000..fca4957 --- /dev/null +++ b/src/widgets/qtabbar.cpp @@ -0,0 +1,1368 @@ +/**************************************************************************** +** +** Implementation of QTab and QTabBar classes +** +** 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 "qtabbar.h" +#ifndef QT_NO_TABBAR +#include "qaccel.h" +#include "qbitmap.h" +#include "qtoolbutton.h" +#include "qtooltip.h" +#include "qapplication.h" +#include "qstyle.h" +#include "qpainter.h" +#include "qiconset.h" +#include "qcursor.h" +#include "../kernel/qinternal_p.h" +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif + +#ifdef Q_WS_MACX +#include <qmacstyle_mac.h> +#endif + + +/*! + \class QTab qtabbar.h + \brief The QTab class provides the structures in a QTabBar. + + \ingroup advanced + + This class is used for custom QTabBar tab headings. + + \sa QTabBar +*/ + + +/*! + Constructs an empty tab. All fields are set to empty. +*/ + +QTab::QTab() + : enabled( TRUE ), + id ( 0 ), + iconset( 0 ), + tb( 0 ) +{ +} + +/*! + Constructs a tab with the text \a text. +*/ + +QTab::QTab( const QString &text ) + : label( text ), + enabled( TRUE ), + id( 0 ), + iconset( 0 ), + tb( 0 ) +{ +} + +/*! + Constructs a tab with an \a icon and the text, \a text. +*/ + +QTab::QTab( const QIconSet& icon, const QString& text ) + : label( text ), + enabled( TRUE ), + id( 0 ), + iconset( new QIconSet(icon) ), + tb( 0 ) +{ +} + +/*! + \fn QString QTab::text() const + + Returns the text of the QTab label. +*/ + +/*! + \fn QIconSet QTab::iconSet() const + + Return the QIconSet of the QTab. +*/ + +/*! + \fn void QTab::setRect( const QRect &rect ) + + Set the QTab QRect to \a rect. +*/ + +/*! + \fn QRect QTab::rect() const + + Return the QRect for the QTab. +*/ + +/*! + \fn void QTab::setEnabled( bool enable ) + + If \a enable is TRUE enable the QTab, otherwise disable it. +*/ + +/*! + \fn bool QTab::isEnabled() const + + Returns TRUE if the QTab is enabled; otherwise returns FALSE. +*/ + +/*! + \fn void QTab::setIdentifier( int i ) + + Set the identifier for the QTab to \a i. Each QTab's identifier + within a QTabBar must be unique. +*/ + +/*! + \fn int QTab::identifier() const + + Return the QTab's identifier. +*/ + + + +/*! + Destroys the tab and frees up all allocated resources. +*/ + +QTab::~QTab() +{ + delete iconset; + tb = 0; +} + +/*! + \class QTabBar qtabbar.h + \brief The QTabBar class provides a tab bar, e.g. for use in tabbed dialogs. + + \ingroup advanced + + QTabBar is straightforward to use; it draws the tabs using one of + the predefined \link QTabBar::Shape shapes\endlink, and emits a + signal when a tab is selected. It can be subclassed to tailor the + look and feel. Qt also provides a ready-made \l{QTabWidget} and a + \l{QTabDialog}. + + The choice of tab shape is a matter of taste, although tab dialogs + (for preferences and similar) invariably use \c RoundedAbove; + nobody uses \c TriangularAbove. Tab controls in windows other than + dialogs almost always use either \c RoundedBelow or \c + TriangularBelow. Many spreadsheets and other tab controls in which + all the pages are essentially similar use \c TriangularBelow, + whereas \c RoundedBelow is used mostly when the pages are + different (e.g. a multi-page tool palette). + + The most important part of QTabBar's API is the selected() signal. + This is emitted whenever the selected page changes (even at + startup, when the selected page changes from 'none'). There is + also a slot, setCurrentTab(), which can be used to select a page + programmatically. + + QTabBar creates automatic accelerator keys in the manner of + QButton; e.g. if a tab's label is "\&Graphics", Alt+G becomes an + accelerator key for switching to that tab. + + The following virtual functions may need to be reimplemented: + \list + \i paint() paints a single tab. paintEvent() calls paint() for + each tab so that any overlap will look right. + \i addTab() creates a new tab and adds it to the bar. + \i selectTab() decides which tab, if any, the user selects with the mouse. + \endlist + + The index of the current tab is returned by currentTab(). The tab + with a particular index is returned by tabAt(), the tab with a + particular id is returned by tab(). The index of a tab is returned + by indexOf(). The current tab can be set by index or tab pointer + using one of the setCurrentTab() functions. + + <img src=qtabbar-m.png> <img src=qtabbar-w.png> +*/ + +/*! + \enum QTabBar::Shape + + This enum type lists the built-in shapes supported by QTabBar: + + \value RoundedAbove the normal rounded look above the pages + + \value RoundedBelow the normal rounded look below the pages + + \value TriangularAbove triangular tabs above the pages (very + unusual; included for completeness) + + \value TriangularBelow triangular tabs similar to those used in + the Excel spreadsheet, for example +*/ + +class QTabBarToolTip; + +struct QTabPrivate { + int id; + int focus; +#ifndef QT_NO_ACCEL + QAccel * a; +#endif + QTab *pressed; + QTabBar::Shape s; + QToolButton* rightB; + QToolButton* leftB; + int btnWidth; + bool scrolls; + QTabBarToolTip * toolTips; +}; + +#ifndef QT_NO_TOOLTIP +/* \internal +*/ +class QTabBarToolTip : public QToolTip +{ +public: + QTabBarToolTip( QWidget * parent ) + : QToolTip( parent ) {} + virtual ~QTabBarToolTip() {} + + void add( QTab * tab, const QString & tip ) + { + tabTips.replace( tab, tip ); + } + + void remove( QTab * tab ) + { + tabTips.erase( tab ); + } + + QString tipForTab( QTab * tab ) const + { + QMapConstIterator<QTab *, QString> it; + it = tabTips.find( tab ); + if ( it != tabTips.end() ) + return it.data(); + else + return QString(); + } + +protected: + void maybeTip( const QPoint & p ) + { + QTabBar * tb = (QTabBar *) parentWidget(); + if ( !tb ) + return; + + // check if the scroll buttons in the tab bar are visible - + // don't display any tips if the pointer is over one of them + QRect rectL, rectR; + rectL.setRect( tb->d->leftB->x(), tb->d->leftB->y(), + tb->d->leftB->width(), tb->d->leftB->height() ); + rectR.setRect( tb->d->rightB->x(), tb->d->rightB->y(), + tb->d->rightB->width(), tb->d->rightB->height() ); + if ( tb->d->scrolls && (rectL.contains( p ) || rectR.contains( p )) ) + return; + +#ifndef QT_NO_TOOLTIP + // find and show the tool tip for the tab under the point p + QMapIterator<QTab *, QString> it; + for ( it = tabTips.begin(); it != tabTips.end(); ++it ) { + if ( it.key()->rect().contains( p ) ) + tip( it.key()->rect(), it.data() ); + } +#endif + } + +private: + QMap<QTab *, QString> tabTips; +}; +#endif + +/*! + \fn void QTabBar::selected( int id ) + + QTabBar emits this signal whenever any tab is selected, whether by + the program or by the user. The argument \a id is the id of the + tab as returned by addTab(). + + show() is guaranteed to emit this signal; you can display your + page in a slot connected to this signal. +*/ + +/*! + \fn void QTabBar::layoutChanged() + + QTabBar emits the signal whenever the layout of the tab bar has + been recalculated, for example when the contents of a tab change. +*/ + +/*! + Constructs a new, empty tab bar; the \a parent and \a name + arguments are passed on to the QWidget constructor. +*/ + +QTabBar::QTabBar( QWidget * parent, const char *name ) + : QWidget( parent, name, WNoAutoErase | WNoMousePropagation ) +{ + d = new QTabPrivate; + d->pressed = 0; + d->id = 0; + d->focus = 0; + d->toolTips = 0; +#ifndef QT_NO_ACCEL + d->a = new QAccel( this, "tab accelerators" ); + connect( d->a, SIGNAL(activated(int)), this, SLOT(setCurrentTab(int)) ); + connect( d->a, SIGNAL(activatedAmbiguously(int)), this, SLOT(setCurrentTab(int)) ); +#endif + d->s = RoundedAbove; + d->scrolls = FALSE; + d->leftB = new QToolButton( LeftArrow, this, "qt_left_btn" ); + connect( d->leftB, SIGNAL( clicked() ), this, SLOT( scrollTabs() ) ); + d->leftB->hide(); + d->rightB = new QToolButton( RightArrow, this, "qt_right_btn" ); + connect( d->rightB, SIGNAL( clicked() ), this, SLOT( scrollTabs() ) ); + d->rightB->hide(); + d->btnWidth = style().pixelMetric(QStyle::PM_TabBarScrollButtonWidth, this); + l = new QPtrList<QTab>; + lstatic = new QPtrList<QTab>; + lstatic->setAutoDelete( TRUE ); + setFocusPolicy( TabFocus ); + setSizePolicy( QSizePolicy( QSizePolicy::Preferred, QSizePolicy::Fixed ) ); +} + + +/*! + Destroys the tab control, freeing memory used. +*/ + +QTabBar::~QTabBar() +{ +#ifndef QT_NO_TOOLTIP + if ( d->toolTips ) + delete d->toolTips; +#endif + delete d; + d = 0; + delete l; + l = 0; + delete lstatic; + lstatic = 0; +} + + +/*! + Adds the tab, \a newTab, to the tab control. + + Sets \a newTab's id to a new id and places the tab just to the + right of the existing tabs. If the tab's label contains an + ampersand, the letter following the ampersand is used as an + accelerator for the tab, e.g. if the label is "Bro\&wse" then + Alt+W becomes an accelerator which will move the focus to this + tab. Returns the id. + + \sa insertTab() +*/ + +int QTabBar::addTab( QTab * newTab ) +{ + return insertTab( newTab ); +} + + +/*! + Inserts the tab, \a newTab, into the tab control. + + If \a index is not specified, the tab is simply appended. + Otherwise it's inserted at the specified position. + + Sets \a newTab's id to a new id. If the tab's label contains an + ampersand, the letter following the ampersand is used as an + accelerator for the tab, e.g. if the label is "Bro\&wse" then + Alt+W becomes an accelerator which will move the focus to this + tab. Returns the id. + + \sa addTab() +*/ + +int QTabBar::insertTab( QTab * newTab, int index ) +{ + newTab->id = d->id++; + if ( !tab( d->focus ) ) + d->focus = newTab->id; + + newTab->setTabBar( this ); + l->insert( 0, newTab ); + if ( index < 0 || index > int(lstatic->count()) ) + lstatic->append( newTab ); + else + lstatic->insert( index, newTab ); + + layoutTabs(); + updateArrowButtons(); + makeVisible( tab( currentTab() ) ); + +#ifndef QT_NO_ACCEL + int p = QAccel::shortcutKey( newTab->label ); + if ( p ) + d->a->insertItem( p, newTab->id ); +#endif + + return newTab->id; +} + + +/*! + Removes tab \a t from the tab control, and deletes the tab. +*/ +void QTabBar::removeTab( QTab * t ) +{ + //#### accelerator labels?? +#ifndef QT_NO_TOOLTIP + if ( d->toolTips ) + d->toolTips->remove( t ); +#endif +#ifndef QT_NO_ACCEL + if ( d->a ) + d->a->removeItem( t->id ); +#endif + bool updateFocus = t->id == d->focus; + // remove the TabBar Reference + if(d->pressed == t) + d->pressed = 0; + t->setTabBar( 0 ); + l->remove( t ); + lstatic->remove( t ); + layoutTabs(); + updateArrowButtons(); + makeVisible( tab( currentTab() ) ); + if ( updateFocus ) + d->focus = currentTab(); + update(); +} + + +/*! + Enables tab \a id if \a enabled is TRUE or disables it if \a + enabled is FALSE. If \a id is currently selected, + setTabEnabled(FALSE) makes another tab selected. + + setTabEnabled() updates the display if this causes a change in \a + id's status. + + \sa update(), isTabEnabled() +*/ + +void QTabBar::setTabEnabled( int id, bool enabled ) +{ + QTab * t; + for( t = l->first(); t; t = l->next() ) { + if ( t && t->id == id ) { + if ( t->enabled != enabled ) { + t->enabled = enabled; +#ifndef QT_NO_ACCEL + d->a->setItemEnabled( t->id, enabled ); +#endif + QRect r( t->r ); + if ( !enabled && id == currentTab() ) { + QPoint p1( t->r.center() ), p2; + int m = 2147483647; + int distance; + // look for the closest enabled tab - measure the + // distance between the centers of the two tabs + for( QTab * n = l->first(); n; n = l->next() ) { + if ( n->enabled ) { + p2 = n->r.center(); + distance = (p2.x() - p1.x())*(p2.x() - p1.x()) + + (p2.y() - p1.y())*(p2.y() - p1.y()); + if ( distance < m ) { + t = n; + m = distance; + } + } + } + if ( t->enabled ) { + r = r.unite( t->r ); + l->append( l->take( l->findRef( t ) ) ); + emit selected( t->id ); + } + } + repaint( r, FALSE ); + } + return; + } + } +} + + +/*! + Returns TRUE if the tab with id \a id exists and is enabled; + otherwise returns FALSE. + + \sa setTabEnabled() +*/ + +bool QTabBar::isTabEnabled( int id ) const +{ + QTab * t = tab( id ); + if ( t ) + return t->enabled; + return FALSE; +} + + + +/*! + \reimp +*/ +QSize QTabBar::sizeHint() const +{ + QSize sz(0, 0); + if ( QTab * t = l->first() ) { + QRect r( t->r ); + while ( (t = l->next()) != 0 ) + r = r.unite( t->r ); + sz = r.size(); + } + return sz.expandedTo(QApplication::globalStrut()); +} + +/*! + \reimp +*/ + +QSize QTabBar::minimumSizeHint() const +{ + if(style().styleHint( QStyle::SH_TabBar_PreferNoArrows, this )) + return sizeHint(); + return QSize( d->rightB->sizeHint().width() * 2 + 75, sizeHint().height() ); +} + +/*! + Paints the tab \a t using painter \a p. If and only if \a selected + is TRUE, \a t is drawn currently selected. + + This virtual function may be reimplemented to change the look of + QTabBar. If you decide to reimplement it, you may also need to + reimplement sizeHint(). +*/ + +void QTabBar::paint( QPainter * p, QTab * t, bool selected ) const +{ + QStyle::SFlags flags = QStyle::Style_Default; + + if (isEnabled() && t->isEnabled()) + flags |= QStyle::Style_Enabled; + if (topLevelWidget() == qApp->activeWindow()) + flags |= QStyle::Style_Active; + if ( selected ) + flags |= QStyle::Style_Selected; + else if(t == d->pressed) + flags |= QStyle::Style_Sunken; + //selection flags + if(t->rect().contains(mapFromGlobal(QCursor::pos()))) + flags |= QStyle::Style_MouseOver; + style().drawControl( QStyle::CE_TabBarTab, p, this, t->rect(), + colorGroup(), flags, QStyleOption(t) ); + + QRect r( t->r ); + p->setFont( font() ); + + int iw = 0; + int ih = 0; + if ( t->iconset != 0 ) { + iw = t->iconset->pixmap( QIconSet::Small, QIconSet::Normal ).width() + 4; + ih = t->iconset->pixmap( QIconSet::Small, QIconSet::Normal ).height(); + } + QFontMetrics fm = p->fontMetrics(); + int fw = fm.width( t->label ); + fw -= t->label.contains('&') * fm.width('&'); + fw += t->label.contains("&&") * fm.width('&'); + int w = iw + fw + 4; + int h = QMAX(fm.height() + 4, ih ); + int offset = 3; +#ifdef Q_WS_MAC + if (::qt_cast<QMacStyle *>(&style())) + offset = 0; +#endif + paintLabel( p, QRect( r.left() + (r.width()-w)/2 - offset, + r.top() + (r.height()-h)/2, + w, h ), t, t->id == keyboardFocusTab() ); +} + +/*! + Paints the label of tab \a t centered in rectangle \a br using + painter \a p. A focus indication is drawn if \a has_focus is TRUE. +*/ + +void QTabBar::paintLabel( QPainter* p, const QRect& br, + QTab* t, bool has_focus ) const +{ + QRect r = br; + bool selected = currentTab() == t->id; + if ( t->iconset) { + // the tab has an iconset, draw it in the right mode + QIconSet::Mode mode = (t->enabled && isEnabled()) + ? QIconSet::Normal : QIconSet::Disabled; + if ( mode == QIconSet::Normal && has_focus ) + mode = QIconSet::Active; + QPixmap pixmap = t->iconset->pixmap( QIconSet::Small, mode ); + int pixw = pixmap.width(); + int pixh = pixmap.height(); + r.setLeft( r.left() + pixw + 4 ); + r.setRight( r.right() + 2 ); + + int xoff = 0, yoff = 0; + if(!selected) { + xoff = style().pixelMetric(QStyle::PM_TabBarTabShiftHorizontal, this); + yoff = style().pixelMetric(QStyle::PM_TabBarTabShiftVertical, this); + } + p->drawPixmap( br.left() + 2 + xoff, br.center().y()-pixh/2 + yoff, pixmap ); + } + + QStyle::SFlags flags = QStyle::Style_Default; + + if (isEnabled() && t->isEnabled()) + flags |= QStyle::Style_Enabled; + if (has_focus) + flags |= QStyle::Style_HasFocus; + if ( selected ) + flags |= QStyle::Style_Selected; + else if(t == d->pressed) + flags |= QStyle::Style_Sunken; + if(t->rect().contains(mapFromGlobal(QCursor::pos()))) + flags |= QStyle::Style_MouseOver; + style().drawControl( QStyle::CE_TabBarLabel, p, this, r, + t->isEnabled() ? colorGroup(): palette().disabled(), + flags, QStyleOption(t) ); +} + + +/*! + Repaints the tab row. All the painting is done by paint(); + paintEvent() only decides which tabs need painting and in what + order. The event is passed in \a e. + + \sa paint() +*/ + +void QTabBar::paintEvent( QPaintEvent * e ) +{ + if ( e->rect().isNull() ) + return; + + QSharedDoubleBuffer buffer( this, e->rect() ); + + QTab * t; + t = l->first(); + do { + QTab * n = l->next(); + if ( t && t->r.intersects( e->rect() ) ) + paint( buffer.painter(), t, n == 0 ); + t = n; + } while ( t != 0 ); + + if ( d->scrolls && lstatic->first()->r.left() < 0 ) { + QPointArray a; + int h = height(); + if ( d->s == RoundedAbove ) { + buffer.painter()->fillRect( 0, 3, 4, h-5, + colorGroup().brush( QColorGroup::Background ) ); + a.setPoints( 5, 0,2, 3,h/4, 0,h/2, 3,3*h/4, 0,h ); + } else if ( d->s == RoundedBelow ) { + buffer.painter()->fillRect( 0, 2, 4, h-5, + colorGroup().brush( QColorGroup::Background ) ); + a.setPoints( 5, 0,0, 3,h/4, 0,h/2, 3,3*h/4, 0,h-3 ); + } + + if ( !a.isEmpty() ) { + buffer.painter()->setPen( colorGroup().light() ); + buffer.painter()->drawPolyline( a ); + a.translate( 1, 0 ); + buffer.painter()->setPen( colorGroup().midlight() ); + buffer.painter()->drawPolyline( a ); + } + } +} + + +/*! + This virtual function is called by the mouse event handlers to + determine which tab is pressed. The default implementation returns + a pointer to the tab whose bounding rectangle contains \a p, if + exactly one tab's bounding rectangle contains \a p. Otherwise it + returns 0. + + \sa mousePressEvent() mouseReleaseEvent() +*/ + +QTab * QTabBar::selectTab( const QPoint & p ) const +{ + QTab * selected = 0; + bool moreThanOne = FALSE; + + QPtrListIterator<QTab> i( *l ); + while( i.current() ) { + QTab * t = i.current(); + ++i; + + if ( t && t->r.contains( p ) ) { + if ( selected ) + moreThanOne = TRUE; + else + selected = t; + } + } + + return moreThanOne ? 0 : selected; +} + + +/*! + \reimp +*/ +void QTabBar::mousePressEvent( QMouseEvent * e ) +{ + if ( e->button() != LeftButton ) { + e->ignore(); + return; + } + QTab *t = selectTab( e->pos() ); + if ( t && t->enabled ) { + d->pressed = t; + if(e->type() == style().styleHint( QStyle::SH_TabBar_SelectMouseType, this )) + setCurrentTab( t ); + else + repaint(t->rect(), FALSE); + } +} + + +/*! + \reimp +*/ + +void QTabBar::mouseMoveEvent ( QMouseEvent *e ) +{ + if ( e->state() != LeftButton ) { + e->ignore(); + return; + } + if(style().styleHint( QStyle::SH_TabBar_SelectMouseType, this ) == QEvent::MouseButtonRelease) { + QTab *t = selectTab( e->pos() ); + if(t != d->pressed) { + if(d->pressed) + repaint(d->pressed->rect(), FALSE); + if((d->pressed = t)) + repaint(t->rect(), FALSE); + } + } +} + +/*! + \reimp +*/ + +void QTabBar::mouseReleaseEvent( QMouseEvent *e ) +{ + if ( e->button() != LeftButton ) + e->ignore(); + if(d->pressed) { + QTab *t = selectTab( e->pos() ) == d->pressed ? d->pressed : 0; + d->pressed = 0; + if(t && t->enabled && e->type() == style().styleHint( QStyle::SH_TabBar_SelectMouseType, this )) + setCurrentTab( t ); + } +} + + +/*! + \reimp +*/ +void QTabBar::show() +{ + // ensures that one tab is selected. + QTab * t = l->last(); + QWidget::show(); + + if ( t ) + emit selected( t->id ); +} + +/*! + \property QTabBar::currentTab + \brief the id of the tab bar's visible tab + + If no tab page is currently visible, the property's value is -1. + Even if the property's value is not -1, you cannot assume that the + user can see the relevant page, or that the tab is enabled. When + you need to display something the value of this property + represents the best page to display. + + When this property is set to \e id, it will raise the tab with the + id \e id and emit the selected() signal. + + \sa selected() isTabEnabled() +*/ + +int QTabBar::currentTab() const +{ + const QTab * t = l->getLast(); + + return t ? t->id : -1; +} + +void QTabBar::setCurrentTab( int id ) +{ + setCurrentTab( tab( id ) ); +} + + +/*! + \overload + + Raises \a tab and emits the selected() signal unless the tab was + already current. + + \sa currentTab() selected() +*/ + +void QTabBar::setCurrentTab( QTab * tab ) +{ + if ( tab && l ) { + if ( l->last() == tab ) + return; + + QRect r = l->last()->r; + if ( l->findRef( tab ) >= 0 ) + l->append( l->take() ); + + d->focus = tab->id; + + setMicroFocusHint( tab->rect().x(), tab->rect().y(), tab->rect().width(), tab->rect().height(), FALSE ); + + if ( tab->r.intersects( r ) ) { + repaint( r.unite( tab->r ), FALSE ); + } else { +#ifdef Q_WS_MACX + update(); +#else + repaint( r, FALSE ); + repaint( tab->r, FALSE ); +#endif + } + makeVisible( tab ); + emit selected( tab->id ); + +#ifdef QT_ACCESSIBILITY_SUPPORT + QAccessible::updateAccessibility( this, indexOf(tab->id)+1, QAccessible::Focus ); +#endif + } +} + +/*! + \property QTabBar::keyboardFocusTab + \brief the id of the tab that has the keyboard focus + + This property contains the id of the tab that has the keyboard + focus or -1 if the tab bar does not have the keyboard focus. +*/ + +int QTabBar::keyboardFocusTab() const +{ + return hasFocus() ? d->focus : -1; +} + + +/*! + \reimp +*/ +void QTabBar::keyPressEvent( QKeyEvent * e ) +{ + // The right and left arrow keys move a selector, the spacebar + // makes the tab with the selector active. All other keys are + // ignored. + + int old = d->focus; + + bool reverse = QApplication::reverseLayout(); + if ( ( !reverse && e->key() == Key_Left ) || ( reverse && e->key() == Key_Right ) ) { + // left - skip past any disabled ones + if ( d->focus >= 0 ) { + QTab * t = lstatic->last(); + while ( t && t->id != d->focus ) + t = lstatic->prev(); + do { + t = lstatic->prev(); + } while ( t && !t->enabled); + if (t) + d->focus = t->id; + } + if ( d->focus < 0 ) + d->focus = old; + } else if ( ( !reverse && e->key() == Key_Right ) || ( reverse && e->key() == Key_Left ) ) { + QTab * t = lstatic->first(); + while ( t && t->id != d->focus ) + t = lstatic->next(); + do { + t = lstatic->next(); + } while ( t && !t->enabled); + + if (t) + d->focus = t->id; + if ( d->focus >= d->id ) + d->focus = old; + } else { + // other keys - ignore + e->ignore(); + return; + } + + // if the focus moved, repaint and signal + if ( old != d->focus ) { + setCurrentTab( d->focus ); + } +} + + +/*! + Returns the tab with id \a id or 0 if there is no such tab. + + \sa count() +*/ + +QTab * QTabBar::tab( int id ) const +{ + QTab * t; + for( t = l->first(); t; t = l->next() ) + if ( t && t->id == id ) + return t; + return 0; +} + + +/*! + Returns the tab at position \a index. + + \sa indexOf() +*/ + +QTab * QTabBar::tabAt( int index ) const +{ + QTab * t; + t = lstatic->at( index ); + return t; +} + + +/*! + Returns the position index of the tab with id \a id or -1 if no + tab has this \a id. + + \sa tabAt() +*/ +int QTabBar::indexOf( int id ) const +{ + QTab * t; + int idx = 0; + for( t = lstatic->first(); t; t = lstatic->next() ) { + if ( t && t->id == id ) + return idx; + idx++; + } + return -1; +} + + +/*! + \property QTabBar::count + \brief the number of tabs in the tab bar + + \sa tab() +*/ +int QTabBar::count() const +{ + return l->count(); +} + + +/*! + The list of QTab objects in the tab bar. + + This list is unlikely to be in the order that the QTab elements + appear visually. One way of iterating over the tabs is like this: + \code + for ( uint i = 0; i < myTabBar->count(); ++i ) { + nextTab = myTabBar->tabAt( i ); + // do something with nextTab + } + \endcode +*/ +QPtrList<QTab> * QTabBar::tabList() +{ + return l; +} + + +/*! + \property QTabBar::shape + \brief the shape of the tabs in the tab bar + + The value of this property is one of the following: \c + RoundedAbove (default), \c RoundedBelow, \c TriangularAbove or \c + TriangularBelow. + + \sa Shape +*/ +QTabBar::Shape QTabBar::shape() const +{ + return d ? d->s : RoundedAbove; +} + +void QTabBar::setShape( Shape s ) +{ + if ( !d || d->s == s ) + return; + //######### must recalculate heights + d->s = s; + update(); +} + +/*! + Lays out all existing tabs according to their label and their + iconset. + */ +void QTabBar::layoutTabs() +{ + if ( lstatic->isEmpty() ) + return; + + QSize oldSh(0, 0); + if ( QTab * t = l->first() ) { + QRect r( t->r ); + while ( (t = l->next()) != 0 ) + r = r.unite( t->r ); + oldSh = r.size(); + } + + d->btnWidth = style().pixelMetric(QStyle::PM_TabBarScrollButtonWidth, this); + int hframe, vframe, overlap; + hframe = style().pixelMetric( QStyle::PM_TabBarTabHSpace, this ); + vframe = style().pixelMetric( QStyle::PM_TabBarTabVSpace, this ); + overlap = style().pixelMetric( QStyle::PM_TabBarTabOverlap, this ); + + QFontMetrics fm = fontMetrics(); + QRect r; + QTab *t; + bool reverse = QApplication::reverseLayout(); + if ( reverse ) + t = lstatic->last(); + else + t = lstatic->first(); + int x = 0; + int offset = (t && d->scrolls) ? t->r.x() : 0; + while ( t ) { + int lw = fm.width( t->label ); + lw -= t->label.contains('&') * fm.width('&'); + lw += t->label.contains("&&") * fm.width('&'); + int iw = 0; + int ih = 0; + if ( t->iconset != 0 ) { + iw = t->iconset->pixmap( QIconSet::Small, QIconSet::Normal ).width() + 4; + ih = t->iconset->pixmap( QIconSet::Small, QIconSet::Normal ).height(); + } + int h = QMAX( fm.height(), ih ); + h = QMAX( h, QApplication::globalStrut().height() ); + + h += vframe; + t->r = QRect(QPoint(x, 0), style().sizeFromContents(QStyle::CT_TabBarTab, this, + QSize( QMAX( lw + hframe + iw, QApplication::globalStrut().width() ), h ), + QStyleOption(t) )); + x += t->r.width() - overlap; + r = r.unite( t->r ); + if ( reverse ) + t = lstatic->prev(); + else + t = lstatic->next(); + } + x += overlap; + int w = (d->scrolls) ? d->leftB->x() : width(); + if (x + offset < w) + offset = w - x; + if (offset > 0) + offset = 0; + + for ( t = lstatic->first(); t; t = lstatic->next() ) { + t->r.moveBy( offset, 0 ); + t->r.setHeight( r.height() ); + } + + if ( sizeHint() != oldSh ) + updateGeometry(); + + emit layoutChanged(); +} + +/*! + \reimp +*/ + +bool QTabBar::event( QEvent *e ) +{ + if ( e->type() == QEvent::LanguageChange ) { + layoutTabs(); + updateArrowButtons(); + makeVisible( tab( currentTab() )); + } + + return QWidget::event( e ); +} + +/*! + \reimp +*/ + +void QTabBar::styleChange( QStyle& old ) +{ + layoutTabs(); + updateArrowButtons(); + QWidget::styleChange( old ); +} + +/*! + \reimp +*/ +void QTabBar::focusInEvent( QFocusEvent * ) +{ + QTab *t = tab( d->focus ); + if ( t ) + repaint( t->r, FALSE ); +} + +/*! + \reimp +*/ +void QTabBar::focusOutEvent( QFocusEvent * ) +{ + QTab *t = tab( d->focus ); + if ( t ) + repaint( t->r, FALSE ); +} + +/*! + \reimp +*/ +void QTabBar::resizeEvent( QResizeEvent * ) +{ + const int arrowWidth = QMAX( d->btnWidth, QApplication::globalStrut().width() );; + d->rightB->setGeometry( width() - arrowWidth, 0, arrowWidth, height() ); + d->leftB->setGeometry( width() - 2*arrowWidth, 0, arrowWidth, height() ); + layoutTabs(); + updateArrowButtons(); + makeVisible( tab( currentTab() )); +} + +void QTabBar::scrollTabs() +{ + QTab* left = 0; + QTab* right = 0; + for ( QTab* t = lstatic->first(); t; t = lstatic->next() ) { + if ( t->r.left() < 0 && t->r.right() > 0 ) + left = t; + if ( t->r.left() < d->leftB->x()+2 ) + right = t; + } + + if ( sender() == d->leftB ) + makeVisible( left ); + else if ( sender() == d->rightB ) + makeVisible( right ); +} + +void QTabBar::makeVisible( QTab* tab ) +{ + bool tooFarLeft = ( tab && tab->r.left() < 0 ); + bool tooFarRight = ( tab && tab->r.right() >= d->leftB->x() ); + + if ( !d->scrolls || ( !tooFarLeft && ! tooFarRight ) ) + return; + + bool bs = signalsBlocked(); + blockSignals(TRUE); + layoutTabs(); + blockSignals(bs); + + int offset = 0; + + if ( tooFarLeft ) { + offset = tab->r.left(); + if (tab != lstatic->first()) + offset -= 8; + } else if ( tooFarRight ) { + offset = tab->r.right() - d->leftB->x() + 1; + } + + for ( QTab* t = lstatic->first(); t; t = lstatic->next() ) + t->r.moveBy( -offset, 0 ); + + d->leftB->setEnabled( lstatic->first()->r.left() < 0); + d->rightB->setEnabled( lstatic->last()->r.right() >= d->leftB->x() ); + + // Make sure disabled buttons pop up again + if ( !d->leftB->isEnabled() && d->leftB->isDown() ) + d->leftB->setDown( FALSE ); + if ( !d->rightB->isEnabled() && d->rightB->isDown() ) + d->rightB->setDown( FALSE ); + + update(); + emit layoutChanged(); +} + +void QTabBar::updateArrowButtons() +{ + if (lstatic->isEmpty()) { + d->scrolls = FALSE; + } else { + d->scrolls = (lstatic->last()->r.right() - lstatic->first()->r.left() > width()); + } + if ( d->scrolls ) { + const int arrowWidth = QMAX( d->btnWidth, QApplication::globalStrut().width() ); + if ( QApplication::reverseLayout() ) { + d->rightB->setGeometry( arrowWidth, 0, arrowWidth, height() ); + d->leftB->setGeometry( 0, 0, arrowWidth, height() ); + } else { + d->rightB->setGeometry( width() - arrowWidth, 0, arrowWidth, height() ); + d->leftB->setGeometry( width() - 2*arrowWidth, 0, arrowWidth, height() ); + } + + d->leftB->setEnabled( lstatic->first()->r.left() < 0); + d->rightB->setEnabled( lstatic->last()->r.right() >= d->leftB->x() ); + d->leftB->show(); + d->rightB->show(); + } else { + d->leftB->hide(); + d->rightB->hide(); + layoutTabs(); + } +} + +/*! + Removes the tool tip for the tab at index position \a index. +*/ +void QTabBar::removeToolTip( int index ) +{ +#ifndef QT_NO_TOOLTIP + QTab * tab = tabAt( index ); + if ( !tab || !d->toolTips ) + return; + d->toolTips->remove( tab ); +#endif +} + +/*! + Sets the tool tip for the tab at index position \a index to \a + tip. +*/ +void QTabBar::setToolTip( int index, const QString & tip ) +{ +#ifndef QT_NO_TOOLTIP + QTab * tab = tabAt( index ); + if ( !tab ) + return; + if ( d->toolTips == 0 ) + d->toolTips = new QTabBarToolTip( this ); + d->toolTips->add( tab, tip ); +#endif +} + +/*! + Returns the tool tip for the tab at index position \a index. +*/ +QString QTabBar::toolTip( int index ) const +{ +#ifndef QT_NO_TOOLTIP + if ( d->toolTips ) + return d->toolTips->tipForTab( tabAt( index ) ); + else +#endif + return QString(); +} + +/*! + Sets the text of the tab to \a text. +*/ +void QTab::setText( const QString& text ) +{ + label = text; + if ( tb ) { +#ifndef QT_NO_ACCEL + tb->d->a->removeItem( id ); + int p = QAccel::shortcutKey( text ); + if ( p ) + tb->d->a->insertItem( p, id ); +#endif + tb->layoutTabs(); + tb->repaint(FALSE); + +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( tb, tb->indexOf(id)+1, QAccessible::NameChanged ); +#endif + } +} + +/*! + Sets the tab's iconset to \a icon +*/ +void QTab::setIconSet( const QIconSet &icon ) +{ + iconset = new QIconSet( icon ); +} + +// this allows us to handle accelerators that are in a QTabBar. +void QTab::setTabBar( QTabBar *newTb ) +{ + tb = newTb; +} + +/*! + \internal +*/ +void QTabBar::fontChange( const QFont & oldFont ) +{ + layoutTabs(); + QWidget::fontChange( oldFont ); +} + +#endif diff --git a/src/widgets/qtabbar.h b/src/widgets/qtabbar.h new file mode 100644 index 0000000..063f34a --- /dev/null +++ b/src/widgets/qtabbar.h @@ -0,0 +1,186 @@ +/**************************************************************************** +** +** Definition of QTab and QTabBar classes +** +** 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. +** +**********************************************************************/ + +#ifndef QTABBAR_H +#define QTABBAR_H + +#ifndef QT_H +#include "qwidget.h" +#include "qptrlist.h" +#endif // QT_H + +#ifndef QT_NO_TABBAR + +class QTabBar; +class QIconSet; + +class Q_EXPORT QTab : public Qt +{ + friend class QTabBar; + friend class QTabWidget; + +public: + QTab(); + virtual ~QTab(); + QTab( const QString& text ); + QTab( const QIconSet& icon, const QString& text = QString::null ); + + void setText( const QString& text); + QString text() const { return label; } + void setIconSet( const QIconSet& icon ); + QIconSet* iconSet() const { return iconset; } + void setRect( const QRect& rect ) { r = rect; } + QRect rect() const { return r; } + void setEnabled( bool enable ) { enabled = enable; } + bool isEnabled() const { return enabled; } + void setIdentifier( int i ) { id = i; } + int identifier() const { return id; } + +private: + void setTabBar( QTabBar *tb ); + QString label; + QRect r; // the bounding rectangle of this (may overlap with others) + bool enabled; + int id; + QIconSet* iconset; // optional iconset + QTabBar *tb; +}; + + +struct QTabPrivate; +//class *QAccel; + +class Q_EXPORT QTabBar: public QWidget +{ + Q_OBJECT + Q_ENUMS( Shape ) + Q_PROPERTY( Shape shape READ shape WRITE setShape ) + Q_PROPERTY( int currentTab READ currentTab WRITE setCurrentTab ) + Q_PROPERTY( int count READ count ) + Q_PROPERTY( int keyboardFocusTab READ keyboardFocusTab ) + +public: + QTabBar( QWidget* parent=0, const char* name=0 ); + ~QTabBar(); + + enum Shape { RoundedAbove, RoundedBelow, + TriangularAbove, TriangularBelow }; + + Shape shape() const; + virtual void setShape( Shape ); + + void show(); + + virtual int addTab( QTab * ); + virtual int insertTab( QTab *, int index = -1 ); + virtual void removeTab( QTab * ); + + virtual void setTabEnabled( int, bool ); + bool isTabEnabled( int ) const; + + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + int currentTab() const; + int keyboardFocusTab() const; + + QTab * tab( int ) const; + QTab * tabAt( int ) const; + int indexOf( int ) const; + int count() const; + + virtual void layoutTabs(); + virtual QTab * selectTab( const QPoint & p ) const; + + void removeToolTip( int index ); + void setToolTip( int index, const QString & tip ); + QString toolTip( int index ) const; + +public slots: + virtual void setCurrentTab( int ); + virtual void setCurrentTab( QTab * ); + +signals: + void selected( int ); + void layoutChanged(); + +protected: + virtual void paint( QPainter *, QTab *, bool ) const; // ### not const + virtual void paintLabel( QPainter*, const QRect&, QTab*, bool ) const; + + void focusInEvent( QFocusEvent *e ); + void focusOutEvent( QFocusEvent *e ); + + void resizeEvent( QResizeEvent * ); + void paintEvent( QPaintEvent * ); + void mousePressEvent ( QMouseEvent * ); + void mouseMoveEvent ( QMouseEvent * ); + void mouseReleaseEvent ( QMouseEvent * ); + void keyPressEvent( QKeyEvent * ); + void styleChange( QStyle& ); + void fontChange ( const QFont & ); + + bool event( QEvent *e ); + + QPtrList<QTab> * tabList(); + +private slots: + void scrollTabs(); + +private: + QPtrList<QTab> * l; + QPtrList<QTab> * lstatic; + void makeVisible( QTab* t = 0 ); + void updateArrowButtons(); + QTabPrivate * d; + + friend class QTabBarToolTip; + friend class QTab; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QTabBar( const QTabBar & ); + QTabBar& operator=( const QTabBar & ); +#endif +}; + + +#endif // QT_NO_TABBAR + +#endif // QTABBAR_H diff --git a/src/widgets/qtabwidget.cpp b/src/widgets/qtabwidget.cpp new file mode 100644 index 0000000..f4e3d87 --- /dev/null +++ b/src/widgets/qtabwidget.cpp @@ -0,0 +1,1097 @@ +/**************************************************************************** +** +** Implementation of QTabWidget class +** +** Created : 990318 +** +** 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 "qtabwidget.h" +#ifndef QT_NO_TABWIDGET +#include "qobjectlist.h" +#include "qtabbar.h" +#include "qapplication.h" +#include "qwidgetstack.h" +#include "qbitmap.h" +#include "qaccel.h" +#include "qstyle.h" +#include "qpainter.h" +#include "qtoolbutton.h" + +#ifdef Q_OS_MACX +#include <qmacstyle_mac.h> +#endif + +/*! + \class QTabWidget qtabwidget.h + \brief The QTabWidget class provides a stack of tabbed widgets. + + \ingroup organizers + \ingroup advanced + \mainclass + + A tab widget provides a tab bar of tabs and a `page area' below + (or above, see \l{TabPosition}) the tabs. Each tab is associated + with a different widget (called a `page'). Only the current tab's + page is shown in the page area; all the other tabs' pages are + hidden. The user can show a different page by clicking on its tab + or by pressing its Alt+\e{letter} accelerator if it has one. + + The normal way to use QTabWidget is to do the following in the + constructor: + \list 1 + \i Create a QTabWidget. + \i Create a QWidget for each of the pages in the tab dialog, + insert children into it, set up geometry management for it and use + addTab() (or insertTab()) to set up a tab and keyboard accelerator + for it. + \i Connect to the signals and slots. + \endlist + + The position of the tabs is set with setTabPosition(), their shape + with setTabShape(), and their margin with setMargin(). + + If you don't call addTab() and the QTabWidget is already visible, + then the page you have created will not be visible. Don't + confuse the object name you supply to the QWidget constructor and + the tab label you supply to addTab(). addTab() takes a name which + indicates an accelerator and is meaningful and descriptive to the + user, whereas the widget name is used primarily for debugging. + + The signal currentChanged() is emitted when the user selects a + page. + + The current page is available as an index position with + currentPageIndex() or as a wiget pointer with currentPage(). You + can retrieve a pointer to a page with a given index using page(), + and can find the index position of a page with indexOf(). Use + setCurrentPage() to show a particular page by index, or showPage() + to show a page by widget pointer. + + You can change a tab's label and iconset using changeTab() or + setTabLabel() and setTabIconSet(). A tab page can be removed with + removePage(). + + Each tab is either enabled or disabled at any given time (see + setTabEnabled()). If a tab is enabled, the tab text is drawn + normally and the user can select that tab. If it is disabled, the + tab is drawn in a different way and the user cannot select that + tab. Note that even if a tab is disabled, the page can still be + visible, for example if all of the tabs happen to be disabled. + + Although tab widgets can be a very good way to split up a complex + dialog, it's also very easy to get into a mess. See QTabDialog for + some design hints. An alternative is to use a QWidgetStack for + which you provide some means of navigating between pages, for + example, a QToolBar or a QListBox. + + Most of the functionality in QTabWidget is provided by a QTabBar + (at the top, providing the tabs) and a QWidgetStack (most of the + area, organizing the individual pages). + + <img src=qtabwidget-m.png> <img src=qtabwidget-w.png> + + \sa QTabDialog, QToolBox +*/ + +/*! + \enum Qt::Corner + This enum type specifies a corner in a rectangle: + \value TopLeft top left corner + \value TopRight top right corner + \value BottomLeft bottom left corner + \value BottomRight bottom right corner +*/ + +/*! + \enum QTabWidget::TabPosition + + This enum type defines where QTabWidget draws the tab row: + \value Top above the pages + \value Bottom below the pages +*/ + +/*! + \enum QTabWidget::TabShape + + This enum type defines the shape of the tabs: + \value Rounded rounded look (normal) + \value Triangular triangular look (very unusual, included for completeness) +*/ + +/* undocumented now + \obsolete + + \fn void QTabWidget::selected( const QString &tabLabel ); + + This signal is emitted whenever a tab is selected (raised), + including during the first show(). + + \sa raise() +*/ + + +/*! + \fn void QTabWidget::currentChanged( QWidget* ); + + This signal is emitted whenever the current page changes. The + parameter is the new current page. + + \sa currentPage(), showPage(), tabLabel() +*/ + +class QTabBarBase : public QWidget +{ +public: + QTabBarBase( QTabWidget* parent=0, const char* name=0 ) + : QWidget( parent, name ) {}; +protected: + void paintEvent( QPaintEvent * ) + { + QObject * obj = parent(); + if( obj ){ + QTabWidget * t = (QTabWidget *) obj; + QPainter p( this ); + QStyle::SFlags flags = QStyle::Style_Default; + + if ( t->tabPosition() == QTabWidget::Top ) + flags |= QStyle::Style_Top; + if ( t->tabPosition() == QTabWidget::Bottom ) + flags |= QStyle::Style_Bottom; + if(parentWidget()->isEnabled()) + flags |= QStyle::Style_Enabled; + + style().drawPrimitive( QStyle::PE_TabBarBase, &p, rect(), + colorGroup(), flags ); + } + } +}; + +class QTabWidgetData +{ +public: + QTabWidgetData() + : tabs(0), tabBase(0), stack(0), dirty( TRUE ), + pos( QTabWidget::Top ), shape( QTabWidget::Rounded ), + leftCornerWidget(0), rightCornerWidget(0) {}; + ~QTabWidgetData(){}; + QTabBar* tabs; + QTabBarBase* tabBase; + QWidgetStack* stack; + bool dirty; + QTabWidget::TabPosition pos; + QTabWidget::TabShape shape; + int alignment; + QWidget* leftCornerWidget; + QWidget* rightCornerWidget; +}; + +/*! + Constructs a tabbed widget called \a name with parent \a parent, + and widget flags \a f. +*/ +QTabWidget::QTabWidget( QWidget *parent, const char *name, WFlags f ) + : QWidget( parent, name, f ) +{ + d = new QTabWidgetData; + d->stack = new QWidgetStack( this, "tab pages" ); + d->stack->installEventFilter( this ); + d->tabBase = new QTabBarBase( this, "tab base" ); + d->tabBase->resize( 1, 1 ); + setTabBar( new QTabBar( this, "tab control" ) ); + + d->stack->setFrameStyle( QFrame::TabWidgetPanel | QFrame::Raised ); +#ifdef Q_OS_TEMP + d->pos = Bottom; +#endif + + setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) ); + setFocusPolicy( TabFocus ); + setFocusProxy( d->tabs ); + + installEventFilter( this ); +#ifdef Q_OS_MACX + if (::qt_cast<QMacStyle*>(&style())) + setMargin(10); // According to HIGuidelines at least. +#endif +} + +/*! + \reimp +*/ +QTabWidget::~QTabWidget() +{ + delete d; +} + +/*! + Adds another tab and page to the tab view. + + The new page is \a child; the tab's label is \a label. Note the + difference between the widget name (which you supply to widget + constructors and to setTabEnabled(), for example) and the tab + label. The name is internal to the program and invariant, whereas + the label is shown on-screen and may vary according to language + and other factors. + + If the tab's \a label contains an ampersand, the letter following + the ampersand is used as an accelerator for the tab, e.g. if the + label is "Bro\&wse" then Alt+W becomes an accelerator which will + move the focus to this tab. + + If you call addTab() after show() the screen will flicker and the + user may be confused. + + Adding the same child twice will have undefined behavior. + + \sa insertTab() +*/ +void QTabWidget::addTab( QWidget *child, const QString &label) +{ + insertTab( child, label ); +} + + +/*! + \overload + + Adds another tab and page to the tab view. + + This function is the same as addTab(), but with an additional \a + iconset. +*/ +void QTabWidget::addTab( QWidget *child, const QIconSet& iconset, const QString &label ) +{ + insertTab( child, iconset, label ); +} + +/*! + \overload + + This is a low-level function for adding tabs. It is useful if you + are using setTabBar() to set a QTabBar subclass with an overridden + QTabBar::paint() function for a subclass of QTab. The \a child is + the new page and \a tab is the tab to put the \a child on. +*/ +void QTabWidget::addTab( QWidget *child, QTab* tab ) +{ + insertTab( child, tab ); +} + + + +/*! + Inserts another tab and page to the tab view. + + The new page is \a child; the tab's label is \a label. Note the + difference between the widget name (which you supply to widget + constructors and to setTabEnabled(), for example) and the tab + label. The name is internal to the program and invariant, whereas + the label is shown on-screen and may vary according to language + and other factors. + + If the tab's \a label contains an ampersand, the letter following + the ampersand is used as an accelerator for the tab, e.g. if the + label is "Bro\&wse" then Alt+W becomes an accelerator which will + move the focus to this tab. + + If \a index is not specified, the tab is simply appended. + Otherwise it is inserted at the specified position. + + If you call insertTab() after show(), the screen will flicker and + the user may be confused. + + \sa addTab() +*/ +void QTabWidget::insertTab( QWidget *child, const QString &label, int index) +{ + QTab * t = new QTab(); + Q_CHECK_PTR( t ); + t->label = label; + insertTab( child, t, index ); +} + + +/*! + \overload + + Inserts another tab and page to the tab view. + + This function is the same as insertTab(), but with an additional + \a iconset. +*/ +void QTabWidget::insertTab( QWidget *child, const QIconSet& iconset, const QString &label, int index ) +{ + QTab * t = new QTab(); + Q_CHECK_PTR( t ); + t->label = label; + t->iconset = new QIconSet( iconset ); + insertTab( child, t, index ); +} + +/*! + \overload + + This is a lower-level method for inserting tabs, similar to the + other insertTab() method. It is useful if you are using + setTabBar() to set a QTabBar subclass with an overridden + QTabBar::paint() function for a subclass of QTab. The \a child is + the new page, \a tab is the tab to put the \a child on and \a + index is the position in the tab bar that this page should occupy. +*/ +void QTabWidget::insertTab( QWidget *child, QTab* tab, int index) +{ + tab->enabled = TRUE; + int id = d->tabs->insertTab( tab, index ); + d->stack->addWidget( child, id ); + if ( d->stack->frameStyle() != ( QFrame::TabWidgetPanel | QFrame::Raised ) ) + d->stack->setFrameStyle( QFrame::TabWidgetPanel | QFrame::Raised ); + setUpLayout(); +} + + +/*! + Defines a new \a label for page \a{w}'s tab. +*/ +void QTabWidget::changeTab( QWidget *w, const QString &label) +{ + int id = d->stack->id( w ); + if ( id < 0 ) + return; + QTab* t = d->tabs->tab( id ); + if ( !t ) + return; + // this will update the accelerators + t->setText( label ); + + d->tabs->layoutTabs(); + d->tabs->update(); + setUpLayout(); +} + +/*! + \overload + + Defines a new \a iconset and a new \a label for page \a{w}'s tab. +*/ +void QTabWidget::changeTab( QWidget *w, const QIconSet& iconset, const QString &label) +{ + int id = d->stack->id( w ); + if ( id < 0 ) + return; + QTab* t = d->tabs->tab( id ); + if ( !t ) + return; + if ( t->iconset ) { + delete t->iconset; + t->iconset = 0; + } + // this will update the accelerators + t->iconset = new QIconSet( iconset ); + t->setText( label ); + + d->tabs->layoutTabs(); + d->tabs->update(); + setUpLayout(); +} + +/*! + Returns TRUE if the page \a w is enabled; otherwise returns FALSE. + + \sa setTabEnabled(), QWidget::isEnabled() +*/ + +bool QTabWidget::isTabEnabled( QWidget* w ) const +{ + int id = d->stack->id( w ); + if ( id >= 0 ) + return w->isEnabled(); + else + return FALSE; +} + +/*! + If \a enable is TRUE, page \a w is enabled; otherwise page \a w is + disabled. The page's tab is redrawn appropriately. + + QTabWidget uses QWidget::setEnabled() internally, rather than + keeping a separate flag. + + Note that even a disabled tab/page may be visible. If the page is + visible already, QTabWidget will not hide it; if all the pages are + disabled, QTabWidget will show one of them. + + \sa isTabEnabled(), QWidget::setEnabled() +*/ + +void QTabWidget::setTabEnabled( QWidget* w, bool enable) +{ + int id = d->stack->id( w ); + if ( id >= 0 ) { + w->setEnabled( enable ); + d->tabs->setTabEnabled( id, enable ); + } +} + +/*! + Sets widget \a w to be the shown in the specified \a corner of the + tab widget. + + Only the horizontal element of the \a corner will be used. + + \sa cornerWidget(), setTabPosition() +*/ +void QTabWidget::setCornerWidget( QWidget * w, Qt::Corner corner ) +{ + if ( !w ) + return; + if ( (uint)corner & 1 ) + d->rightCornerWidget = w; + else + d->leftCornerWidget = w; +} + +/*! + Returns the widget shown in the \a corner of the tab widget or 0. +*/ +QWidget * QTabWidget::cornerWidget( Qt::Corner corner ) const +{ + if ( (uint)corner & 1 ) + return d->rightCornerWidget; + return d->leftCornerWidget; +} + +/*! + Ensures that page \a w is shown. This is useful mainly for + accelerators. + + \warning Used carelessly, this function can easily surprise or + confuse the user. + + \sa QTabBar::setCurrentTab() +*/ +void QTabWidget::showPage( QWidget * w) +{ + int id = d->stack->id( w ); + if ( id >= 0 ) { + d->stack->raiseWidget( w ); + d->tabs->setCurrentTab( id ); + // ### why overwrite the frame style? + if ( d->stack->frameStyle() != ( QFrame::TabWidgetPanel |QFrame::Raised ) ) + d->stack->setFrameStyle( QFrame::TabWidgetPanel | QFrame::Raised ); + } +} + +/*! + Removes page \a w from this stack of widgets. Does not delete \a + w. + + \sa addTab(), showPage(), QWidgetStack::removeWidget() +*/ +void QTabWidget::removePage( QWidget * w ) +{ + int id = d->stack->id( w ); + if ( id >= 0 ) { + int oldId = d->stack->id(currentPage()); + bool fixCurrentTab = oldId == id; + //switches to the next enabled tab + d->tabs->setTabEnabled( id, FALSE ); + //if no next enabled page we fix the current page + fixCurrentTab = fixCurrentTab && oldId == d->stack->id(currentPage()); + + d->stack->removeWidget( w ); + d->tabs->removeTab( d->tabs->tab(id) ); + if ( fixCurrentTab ) + showTab( d->tabs->currentTab() ); + setUpLayout(); + + if ( d->tabs->count() == 0 ) + d->stack->setFrameStyle( QFrame::NoFrame ); + } +} + +/*! + Returns the label text for the tab on page \a w. +*/ + +QString QTabWidget::tabLabel( QWidget * w ) const +{ + QTab * t = d->tabs->tab( d->stack->id( w ) ); + return t ? t->label : QString::null; +} + +/*! + Sets the tab label for page \a w to \a l +*/ + +void QTabWidget::setTabLabel( QWidget * w, const QString &l ) +{ + QTab * t = d->tabs->tab( d->stack->id( w ) ); + if ( t ) + t->label = l; + d->tabs->layoutTabs(); + d->tabs->update(); + setUpLayout(); +} + +/*! + Returns a pointer to the page currently being displayed by the tab + dialog. The tab dialog does its best to make sure that this value + is never 0 (but if you try hard enough, it can be). +*/ + +QWidget * QTabWidget::currentPage() const +{ + return page( currentPageIndex() ); +} + +/*! + \property QTabWidget::autoMask + \brief whether the tab widget is automatically masked + + \sa QWidget::setAutoMask() +*/ + +/*! + \property QTabWidget::currentPage + \brief the index position of the current tab page + + \sa QTabBar::currentTab() +*/ + +int QTabWidget::currentPageIndex() const +{ + return d->tabs->indexOf( d->tabs->currentTab() ); +} + +void QTabWidget::setCurrentPage( int index ) +{ + d->tabs->setCurrentTab( d->tabs->tabAt( index ) ); + showTab( d->tabs->currentTab() ); +} + + +/*! + Returns the index position of page \a w, or -1 if the widget + cannot be found. +*/ +int QTabWidget::indexOf( QWidget* w ) const +{ + return d->tabs->indexOf( d->stack->id( w ) ); +} + + +/*! + \reimp +*/ +void QTabWidget::resizeEvent( QResizeEvent *e ) +{ + QWidget::resizeEvent( e ); + setUpLayout(); +} + +/*! + Replaces the dialog's QTabBar heading with the tab bar \a tb. Note + that this must be called \e before any tabs have been added, or + the behavior is undefined. + + \sa tabBar() +*/ +void QTabWidget::setTabBar( QTabBar* tb) +{ + if ( tb->parentWidget() != this ) + tb->reparent( this, QPoint(0,0), TRUE ); + delete d->tabs; + d->tabs = tb; + setFocusProxy( d->tabs ); + connect( d->tabs, SIGNAL(selected(int)), + this, SLOT(showTab(int)) ); + setUpLayout(); +} + + +/*! + Returns the current QTabBar. + + \sa setTabBar() +*/ +QTabBar* QTabWidget::tabBar() const +{ + return d->tabs; +} + +/*! + Ensures that the selected tab's page is visible and appropriately + sized. +*/ + +void QTabWidget::showTab( int i ) +{ + if ( d->stack->widget( i ) ) { + d->stack->raiseWidget( i ); + emit selected( d->tabs->tab( i )->label ); + emit currentChanged( d->stack->widget( i ) ); + } +} + +/* + Set up the layout. +*/ +void QTabWidget::setUpLayout( bool onlyCheck ) +{ + if ( onlyCheck && !d->dirty ) + return; // nothing to do + + if ( !isVisible() ) { + d->dirty = TRUE; + return; // we'll do it later + } + + QSize t( 0, d->stack->frameWidth() ); + if ( d->tabs->isVisibleTo(this) ) + t = d->tabs->sizeHint(); + int lcw = 0; + if ( d->leftCornerWidget && d->leftCornerWidget->isVisible() ) { + QSize sz = d->leftCornerWidget->sizeHint(); + d->leftCornerWidget->resize(sz); + lcw = sz.width(); + if ( t.height() > lcw ) + lcw = t.height(); + } + int rcw = 0; + if ( d->rightCornerWidget && d->rightCornerWidget->isVisible() ) { + QSize sz = d->rightCornerWidget->sizeHint(); + d->rightCornerWidget->resize(sz); + rcw = sz.width(); + if ( t.height() > rcw ) + rcw = t.height(); + } + int tw = width() - lcw - rcw; + if ( t.width() > tw ) + t.setWidth( tw ); + int lw = d->stack->lineWidth(); + bool reverse = QApplication::reverseLayout(); + int tabx, taby, stacky, exty, exth, overlap; + + exth = style().pixelMetric( QStyle::PM_TabBarBaseHeight, this ); + overlap = style().pixelMetric( QStyle::PM_TabBarBaseOverlap, this ); + + if ( d->pos == Bottom ) { + taby = height() - t.height() - lw; + stacky = 0; + exty = taby - (exth - overlap); + } else { // Top + taby = 0; + stacky = t.height()-lw + (exth - overlap); + exty = taby + t.height() - overlap; + } + + int lhs = (QMAX( 0, lw - 2 ) + lcw); + int alignment = style().styleHint( QStyle::SH_TabBar_Alignment, this ); + if ( alignment == AlignHCenter && t.width() < width() ) + tabx = lhs + ((width()-(lcw+rcw))/2 - t.width()/2); + else if(reverse || alignment == AlignRight) + tabx = QMIN( width() - t.width(), width() - t.width() - lw + 2 ) - lcw; + else + tabx = lhs; + + d->tabs->setGeometry( tabx, taby, t.width(), t.height() ); + d->tabBase->setGeometry( 0, exty, width(), exth ); + if ( exth == 0 ) + d->tabBase->hide(); + else + d->tabBase->show(); + + d->stack->setGeometry( 0, stacky, width(), height() - (exth-overlap) - + t.height()+QMAX(0, lw-2)); + + d->dirty = FALSE; + + // move cornerwidgets + if ( d->leftCornerWidget ) { + int y = ( t.height() / 2 ) - ( d->leftCornerWidget->height() / 2 ); + int x = ( reverse ? width() - lcw + y : y ); + d->leftCornerWidget->move( x, y + taby ); + } + if ( d->rightCornerWidget ) { + int y = ( t.height() / 2 ) - ( d->rightCornerWidget->height() / 2 ); + int x = ( reverse ? y : width() - rcw + y ); + d->rightCornerWidget->move( x, y + taby ); + } + if ( !onlyCheck ) + update(); + updateGeometry(); + if ( autoMask() ) + updateMask(); +} + +/*! + \reimp +*/ +QSize QTabWidget::sizeHint() const +{ + QSize lc(0, 0), rc(0, 0); + + if (d->leftCornerWidget) + lc = d->leftCornerWidget->sizeHint(); + if(d->rightCornerWidget) + rc = d->rightCornerWidget->sizeHint(); + if ( !d->dirty ) { + QTabWidget *that = (QTabWidget*)this; + that->setUpLayout( TRUE ); + } + QSize s( d->stack->sizeHint() ); + QSize t( d->tabs->sizeHint() ); + if(!style().styleHint(QStyle::SH_TabBar_PreferNoArrows, d->tabs)) + t = t.boundedTo( QSize(200,200) ); + else + t = t.boundedTo( QApplication::desktop()->size() ); + + QSize sz( QMAX( s.width(), t.width() + rc.width() + lc.width() ), + s.height() + (QMAX( rc.height(), QMAX(lc.height(), t.height()))) + ( d->tabBase->isVisible() ? d->tabBase->height() : 0 ) ); + return style().sizeFromContents(QStyle::CT_TabWidget, this, sz).expandedTo(QApplication::globalStrut()); +} + + +/*! + \reimp + + Returns a suitable minimum size for the tab widget. +*/ +QSize QTabWidget::minimumSizeHint() const +{ + QSize lc(0, 0), rc(0, 0); + + if(d->leftCornerWidget) + lc = d->leftCornerWidget->minimumSizeHint(); + if(d->rightCornerWidget) + rc = d->rightCornerWidget->minimumSizeHint(); + if ( !d->dirty ) { + QTabWidget *that = (QTabWidget*)this; + that->setUpLayout( TRUE ); + } + QSize s( d->stack->minimumSizeHint() ); + QSize t( d->tabs->minimumSizeHint() ); + + QSize sz( QMAX( s.width(), t.width() + rc.width() + lc.width() ), + s.height() + (QMAX( rc.height(), QMAX(lc.height(), t.height()))) + ( d->tabBase->isVisible() ? d->tabBase->height() : 0 ) ); + return style().sizeFromContents(QStyle::CT_TabWidget, this, sz).expandedTo(QApplication::globalStrut()); +} + +/*! + \reimp + */ +void QTabWidget::showEvent( QShowEvent * ) +{ + setUpLayout(); +} + + +/*! + \property QTabWidget::tabPosition + \brief the position of the tabs in this tab widget + + Possible values for this property are \c QTabWidget::Top and \c + QTabWidget::Bottom. + + \sa TabPosition +*/ +QTabWidget::TabPosition QTabWidget::tabPosition() const +{ + return d->pos; +} + +void QTabWidget::setTabPosition( TabPosition pos) +{ + if (d->pos == pos) + return; + d->pos = pos; + if (d->tabs->shape() == QTabBar::TriangularAbove || d->tabs->shape() == QTabBar::TriangularBelow ) { + if ( pos == Bottom ) + d->tabs->setShape( QTabBar::TriangularBelow ); + else + d->tabs->setShape( QTabBar::TriangularAbove ); + } + else { + if ( pos == Bottom ) + d->tabs->setShape( QTabBar::RoundedBelow ); + else + d->tabs->setShape( QTabBar::RoundedAbove ); + } + d->tabs->layoutTabs(); + setUpLayout(); +} + +/*! + \property QTabWidget::tabShape + \brief the shape of the tabs in this tab widget + + Possible values for this property are \c QTabWidget::Rounded + (default) or \c QTabWidget::Triangular. + + \sa TabShape +*/ + +QTabWidget::TabShape QTabWidget::tabShape() const +{ + return d->shape; +} + +void QTabWidget::setTabShape( TabShape s ) +{ + if ( d->shape == s ) + return; + d->shape = s; + if ( d->pos == Top ) { + if ( s == Rounded ) + d->tabs->setShape( QTabBar::RoundedAbove ); + else + d->tabs->setShape( QTabBar::TriangularAbove ); + } else { + if ( s == Rounded ) + d->tabs->setShape( QTabBar::RoundedBelow ); + else + d->tabs->setShape( QTabBar::TriangularBelow ); + } + d->tabs->layoutTabs(); + setUpLayout(); +} + + +/*! + \property QTabWidget::margin + \brief the margin in this tab widget + + The margin is the distance between the innermost pixel of the + frame and the outermost pixel of the pages. +*/ +int QTabWidget::margin() const +{ + return d->stack->margin(); +} + +void QTabWidget::setMargin( int w ) +{ + d->stack->setMargin( w ); + setUpLayout(); +} + + +/*! + \reimp + */ +void QTabWidget::styleChange( QStyle& old ) +{ + QWidget::styleChange( old ); + setUpLayout(); +} + + +/*! + \reimp + */ +void QTabWidget::updateMask() +{ + if ( !autoMask() ) + return; + + QRect r; + QRegion reg( r ); + reg += QRegion( d->tabs->geometry() ); + reg += QRegion( d->stack->geometry() ); + setMask( reg ); +} + + +/*! + \reimp + */ +bool QTabWidget::eventFilter( QObject *o, QEvent * e) +{ + if ( o == this ) { + if ( e->type() == QEvent::LanguageChange || e->type() == QEvent::LayoutHint ) { + d->dirty = TRUE; + setUpLayout(); + updateGeometry(); + } else if ( e->type() == QEvent::KeyPress ) { + QKeyEvent *ke = (QKeyEvent*) e; + if ( ( ke->key() == Qt::Key_Tab || ke->key() == Qt::Key_Backtab ) && + count() > 1 && + ke->state() & Qt::ControlButton ) { + int page = currentPageIndex(); + if ( ke->key() == Qt::Key_Backtab || ke->state() & Qt::ShiftButton ) { + page--; + if ( page < 0 ) + page = count() - 1; + } else { + page++; + if ( page >= count() ) + page = 0; + } + setCurrentPage( page ); + if ( !qApp->focusWidget() ) + d->tabs->setFocus(); + return TRUE; + } + } + + } else if ( o == d->stack ) { + if ( e->type() == QEvent::ChildRemoved + && ( (QChildEvent*)e )->child()->isWidgetType() ) { + removePage( (QWidget*) ( (QChildEvent*)e )->child() ); + return TRUE; + } else if ( e->type() == QEvent::LayoutHint ) { + updateGeometry(); + } + } + return FALSE; +} + +/*! + Returns the tab page at index position \a index or 0 if the \a + index is out of range. +*/ +QWidget *QTabWidget::page( int index ) const +{ + QTab *t = d->tabs->tabAt(index); + if ( t ) + return d->stack->widget( t->id ); + // else + return 0; +} + +/*! + Returns the label of the tab at index position \a index or + QString::null if the \a index is out of range. +*/ +QString QTabWidget::label( int index ) const +{ + QTab *t = d->tabs->tabAt( index ); + if ( t ) + return t->label; + // else + return QString::null; +} + +/*! + \property QTabWidget::count + \brief the number of tabs in the tab bar +*/ +int QTabWidget::count() const +{ + return d->tabs->count(); +} + +/*! + Returns the iconset of page \a w or a \link QIconSet::QIconSet() + null iconset\endlink if \a w is not a tab page or does not have an + iconset. +*/ +QIconSet QTabWidget::tabIconSet( QWidget * w ) const +{ + int id = d->stack->id( w ); + if ( id < 0 ) + return QIconSet(); + QTab* t = d->tabs->tab( id ); + if ( !t ) + return QIconSet(); + if ( t->iconset ) + return QIconSet( *t->iconset ); + else + return QIconSet(); +} + +/*! + Sets the iconset for page \a w to \a iconset. +*/ +void QTabWidget::setTabIconSet( QWidget * w, const QIconSet & iconset ) +{ + int id = d->stack->id( w ); + if ( id < 0 ) + return; + QTab* t = d->tabs->tab( id ); + if ( !t ) + return; + if ( t->iconset ) + delete t->iconset; + t->iconset = new QIconSet( iconset ); + + d->tabs->layoutTabs(); + d->tabs->update(); + setUpLayout(); +} + +/*! + Sets the tab tool tip for page \a w to \a tip. + + \sa removeTabToolTip(), tabToolTip() +*/ +void QTabWidget::setTabToolTip( QWidget * w, const QString & tip ) +{ + int index = d->tabs->indexOf( d->stack->id( w ) ); + if ( index < 0 ) + return; + d->tabs->setToolTip( index, tip ); +} + +/*! + Returns the tab tool tip for page \a w or QString::null if no tool + tip has been set. + + \sa setTabToolTip(), removeTabToolTip() +*/ +QString QTabWidget::tabToolTip( QWidget * w ) const +{ + int index = d->tabs->indexOf( d->stack->id( w ) ); + if ( index < 0 ) + return QString(); + return d->tabs->toolTip( index ); +} + +/*! + Removes the tab tool tip for page \a w. If the page does not have + a tip, nothing happens. + + \sa setTabToolTip(), tabToolTip() +*/ +void QTabWidget::removeTabToolTip( QWidget * w ) +{ + int index = d->tabs->indexOf( d->stack->id( w ) ); + if ( index < 0 ) + return; + d->tabs->removeToolTip( index ); +} + +#endif diff --git a/src/widgets/qtabwidget.h b/src/widgets/qtabwidget.h new file mode 100644 index 0000000..9c479db --- /dev/null +++ b/src/widgets/qtabwidget.h @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Definition of QTabWidget class +** +** Created : 990318 +** +** 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. +** +**********************************************************************/ + +#ifndef QTABWIDGET_H +#define QTABWIDGET_H + +#ifndef QT_H +#include "qwidget.h" +#include "qiconset.h" +#endif // QT_H + +#ifndef QT_NO_TABWIDGET + +class QTabBar; +class QTabWidgetData; +class QTab; +class QWidgetStack; + + +class Q_EXPORT QTabWidget : public QWidget +{ + Q_OBJECT + Q_ENUMS( TabPosition ) + Q_ENUMS( TabShape ) + Q_PROPERTY( TabPosition tabPosition READ tabPosition WRITE setTabPosition ) + Q_PROPERTY( TabShape tabShape READ tabShape WRITE setTabShape ) + Q_PROPERTY( int margin READ margin WRITE setMargin ) + Q_PROPERTY( int currentPage READ currentPageIndex WRITE setCurrentPage ) + Q_PROPERTY( int count READ count ) + Q_OVERRIDE( bool autoMask DESIGNABLE true SCRIPTABLE true ) + +public: + QTabWidget( QWidget *parent = 0, const char *name = 0, WFlags f = 0 ); + ~QTabWidget(); + + virtual void addTab( QWidget *, const QString & ); // ### make these inline in 4.0 + virtual void addTab( QWidget *child, const QIconSet& iconset, + const QString &label ); + virtual void addTab( QWidget *, QTab* ); + + virtual void insertTab( QWidget *, const QString &, int index = -1 ); + virtual void insertTab( QWidget *child, const QIconSet& iconset, + const QString &label, int index = -1 ); + virtual void insertTab( QWidget *, QTab*, int index = -1 ); + + void changeTab( QWidget *, const QString &); + void changeTab( QWidget *child, const QIconSet& iconset, + const QString &label ); + + bool isTabEnabled( QWidget * ) const; + void setTabEnabled( QWidget *, bool ); + + void setCornerWidget( QWidget * w, Qt::Corner corner = Qt::TopRight ); + QWidget * cornerWidget( Qt::Corner corner = Qt::TopRight ) const; + + QString tabLabel( QWidget * ) const; + void setTabLabel( QWidget *p, const QString &l ); + + QIconSet tabIconSet( QWidget * w ) const; + void setTabIconSet( QWidget * w, const QIconSet & iconset ); + + void removeTabToolTip( QWidget * w ); + void setTabToolTip( QWidget * w, const QString & tip ); + QString tabToolTip( QWidget * w ) const; + + QWidget * currentPage() const; + QWidget *page( int ) const; + QString label( int ) const; + int currentPageIndex() const; + int indexOf( QWidget * ) const; + + QSize sizeHint() const; + QSize minimumSizeHint() const; + + enum TabPosition { Top, Bottom }; + TabPosition tabPosition() const; + void setTabPosition( TabPosition ); + + enum TabShape { Rounded, Triangular }; + TabShape tabShape() const; + void setTabShape( TabShape s ); + + int margin() const; + void setMargin( int ); + + int count() const; + +public slots: + void setCurrentPage( int ); + virtual void showPage( QWidget * ); + virtual void removePage( QWidget * ); + +protected: + void showEvent( QShowEvent * ); + void resizeEvent( QResizeEvent * ); + void setTabBar( QTabBar * ); + QTabBar* tabBar() const; + void styleChange( QStyle& ); + void updateMask(); + bool eventFilter( QObject *, QEvent * ); + +signals: + void currentChanged( QWidget * ); +#ifndef Q_QDOC + void selected( const QString& ); +#endif + +private slots: + void showTab( int ); + +private: + QTabWidgetData *d; + void setUpLayout( bool = FALSE ); + friend class QTabDialog; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QTabWidget( const QTabWidget & ); + QTabWidget& operator=( const QTabWidget & ); +#endif +}; + +#endif // QT_NO_TABWIDGET + +#endif // QTABWIDGET_H diff --git a/src/widgets/qtextbrowser.cpp b/src/widgets/qtextbrowser.cpp new file mode 100644 index 0000000..38e280b --- /dev/null +++ b/src/widgets/qtextbrowser.cpp @@ -0,0 +1,555 @@ +/**************************************************************************** +** +** Implementation of the QTextBrowser class +** +** Created : 990101 +** +** 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 "qtextbrowser.h" +#ifndef QT_NO_TEXTBROWSER +#include "../kernel/qrichtext_p.h" + +#include "qapplication.h" +#include "qlayout.h" +#include "qpainter.h" + +#include "qvaluestack.h" +#include "stdio.h" +#include "qfile.h" +#include "qtextstream.h" +#include "qlayout.h" +#include "qbitmap.h" +#include "qtimer.h" +#include "qimage.h" +#include "qsimplerichtext.h" +#include "qdragobject.h" +#include "qurl.h" +#include "qcursor.h" + +/*! + \class QTextBrowser qtextbrowser.h + \brief The QTextBrowser class provides a rich text browser with hypertext navigation. + + \ingroup advanced + \ingroup helpsystem + \ingroup text + \mainclass + + This class extends QTextEdit (in read-only mode), adding some + navigation functionality so that users can follow links in + hypertext documents. The contents of QTextEdit is set with + setText(), but QTextBrowser has an additional function, + setSource(), which makes it possible to set the text to a named + document. The name is looked up in the text view's mime source + factory. If a document name ends with an anchor (for example, "\c + #anchor"), the text browser automatically scrolls to that position + (using scrollToAnchor()). When the user clicks on a hyperlink, the + browser will call setSource() itself, with the link's \c href + value as argument. You can track the current source by connetion + to the sourceChanged() signal. + + QTextBrowser provides backward() and forward() slots which you can + use to implement Back and Forward buttons. The home() slot sets + the text to the very first document displayed. The linkClicked() + signal is emitted when the user clicks a link. + + By using QTextEdit::setMimeSourceFactory() you can provide your + own subclass of QMimeSourceFactory. This makes it possible to + access data from anywhere, for example from a network or from a + database. See QMimeSourceFactory::data() for details. + + If you intend using the mime factory to read the data directly + from the file system, you may have to specify the encoding for the + file extension you are using. For example: + \code + mimeSourceFactory()->setExtensionType("qml", "text/utf8"); + \endcode + This is to ensure that the factory is able to resolve the document + names. + + QTextBrowser interprets the tags it processes in accordance with + the default style sheet. Change the style sheet with + \l{setStyleSheet()}; see QStyleSheet for details. + + If you want to provide your users with editable rich text use + QTextEdit. If you want a text browser without hypertext navigation + use QTextEdit, and use QTextEdit::setReadOnly() to disable + editing. If you just need to display a small piece of rich text + use QSimpleRichText or QLabel. + + <img src=qtextbrowser-m.png> <img src=qtextbrowser-w.png> +*/ + +class QTextBrowserData +{ +public: + QTextBrowserData():textOrSourceChanged(FALSE) {} + + QValueStack<QString> stack; + QValueStack<QString> forwardStack; + QString home; + QString curmain; + QString curmark; + + /*flag necessary to give the linkClicked() signal some meaningful + semantics when somebody connected to it calls setText() or + setSource() */ + bool textOrSourceChanged; +}; + + +/*! + Constructs an empty QTextBrowser called \a name, with parent \a + parent. +*/ +QTextBrowser::QTextBrowser(QWidget *parent, const char *name) + : QTextEdit( parent, name ) +{ + setReadOnly( TRUE ); + d = new QTextBrowserData; + + viewport()->setMouseTracking( TRUE ); +} + +/*! + \reimp +*/ +QTextBrowser::~QTextBrowser() +{ + delete d; +} + + +/*! + \property QTextBrowser::source + \brief the name of the displayed document. + + This is a QString::null if no document is displayed or if the + source is unknown. + + Setting this property uses the mimeSourceFactory() to lookup the + named document. It also checks for optional anchors and scrolls + the document accordingly. + + If the first tag in the document is \c{<qt type=detail>}, the + document is displayed as a popup rather than as new document in + the browser window itself. Otherwise, the document is displayed + normally in the text browser with the text set to the contents of + the named document with setText(). + + If you are using the filesystem access capabilities of the mime + source factory, you must ensure that the factory knows about the + encoding of specified files; otherwise no data will be available. + The default factory handles a couple of common file extensions + such as \c *.html and \c *.txt with reasonable defaults. See + QMimeSourceFactory::data() for details. +*/ + +QString QTextBrowser::source() const +{ + if ( d->stack.isEmpty() ) + return QString::null; + else + return d->stack.top(); +} + +/*! + \property QTextBrowser::undoDepth + \brief This text browser's undo depth. +*/ + +/*! + \property QTextBrowser::overwriteMode + \brief This text browser's overwrite mode. +*/ + +/*! + \property QTextBrowser::modified + \brief Whether the contents have been modified. +*/ + +/*! + \property QTextBrowser::readOnly + \brief Whether the contents are read only. +*/ + +/*! + \property QTextBrowser::undoRedoEnabled + \brief Whether undo and redo are enabled. +*/ + + + +/*! + Reloads the current set source. +*/ + +void QTextBrowser::reload() +{ + QString s = d->curmain; + d->curmain = ""; + setSource( s ); +} + + +void QTextBrowser::setSource(const QString& name) +{ +#ifndef QT_NO_CURSOR + if ( isVisible() ) + qApp->setOverrideCursor( waitCursor ); +#endif + d->textOrSourceChanged = TRUE; + QString source = name; + QString mark; + int hash = name.find('#'); + if ( hash != -1) { + source = name.left( hash ); + mark = name.mid( hash+1 ); + } + + if ( source.left(5) == "file:" ) + source = source.mid(6); + + QString url = mimeSourceFactory()->makeAbsolute( source, context() ); + QString txt; + bool dosettext = FALSE; + + if ( !source.isEmpty() && url != d->curmain ) { + const QMimeSource* m = + mimeSourceFactory()->data( source, context() ); + if ( !m ){ + qWarning("QTextBrowser: no mimesource for %s", source.latin1() ); + } + else { + if ( !QTextDrag::decode( m, txt ) ) { + qWarning("QTextBrowser: cannot decode %s", source.latin1() ); + } + } + if ( isVisible() ) { + QString firstTag = txt.left( txt.find( '>' ) + 1 ); + if ( firstTag.left( 3 ) == "<qt" && firstTag.contains( "type" ) && firstTag.contains( "detail" ) ) { + popupDetail( txt, QCursor::pos() ); +#ifndef QT_NO_CURSOR + qApp->restoreOverrideCursor(); +#endif + return; + } + } + + d->curmain = url; + dosettext = TRUE; + } + + d->curmark = mark; + + if ( !mark.isEmpty() ) { + url += "#"; + url += mark; + } + if ( !d->home ) + d->home = url; + + if ( d->stack.isEmpty() || d->stack.top() != url) + d->stack.push( url ); + + int stackCount = (int)d->stack.count(); + if ( d->stack.top() == url ) + stackCount--; + emit backwardAvailable( stackCount > 0 ); + stackCount = (int)d->forwardStack.count(); + if ( d->forwardStack.isEmpty() || d->forwardStack.top() == url ) + stackCount--; + emit forwardAvailable( stackCount > 0 ); + + if ( dosettext ) + QTextEdit::setText( txt, url ); + + if ( !mark.isEmpty() ) + scrollToAnchor( mark ); + else + setContentsPos( 0, 0 ); + +#ifndef QT_NO_CURSOR + if ( isVisible() ) + qApp->restoreOverrideCursor(); +#endif + + emit sourceChanged( url ); +} + +/*! + \fn void QTextBrowser::backwardAvailable(bool available) + + This signal is emitted when the availability of backward() + changes. \a available is FALSE when the user is at home(); + otherwise it is TRUE. +*/ + +/*! + \fn void QTextBrowser::forwardAvailable(bool available) + + This signal is emitted when the availability of forward() changes. + \a available is TRUE after the user navigates backward() and FALSE + when the user navigates or goes forward(). +*/ + +/*! + \fn void QTextBrowser::sourceChanged( const QString& src) + + This signal is emitted when the mime source has changed, \a src + being the new source. + + Source changes happen both programmatically when calling + setSource(), forward(), backword() or home() or when the user + clicks on links or presses the equivalent key sequences. +*/ + +/*! \fn void QTextBrowser::highlighted (const QString &link) + + This signal is emitted when the user has selected but not + activated a link in the document. \a link is the value of the \c + href i.e. the name of the target document. +*/ + +/*! + \fn void QTextBrowser::linkClicked( const QString& link) + + This signal is emitted when the user clicks a link. The \a link is + the value of the \c href i.e. the name of the target document. + + The \a link will be the absolute location of the document, based + on the value of the anchor's href tag and the current context of + the document. + + \sa anchorClicked(), context() +*/ + +/*! + \fn void QTextBrowser::anchorClicked( const QString& name, const QString &link) + + This signal is emitted when the user clicks an anchor. The \a link is + the value of the \c href i.e. the name of the target document. The \a name + is the name of the anchor. + + \sa linkClicked() +*/ + +/*! + Changes the document displayed to the previous document in the + list of documents built by navigating links. Does nothing if there + is no previous document. + + \sa forward(), backwardAvailable() +*/ +void QTextBrowser::backward() +{ + if ( d->stack.count() <= 1) + return; + d->forwardStack.push( d->stack.pop() ); + setSource( d->stack.pop() ); + emit forwardAvailable( TRUE ); +} + +/*! + Changes the document displayed to the next document in the list of + documents built by navigating links. Does nothing if there is no + next document. + + \sa backward(), forwardAvailable() +*/ +void QTextBrowser::forward() +{ + if ( d->forwardStack.isEmpty() ) + return; + setSource( d->forwardStack.pop() ); + emit forwardAvailable( !d->forwardStack.isEmpty() ); +} + +/*! + Changes the document displayed to be the first document the + browser displayed. +*/ +void QTextBrowser::home() +{ + if (!d->home.isNull() ) + setSource( d->home ); +} + +/*! + The event \a e is used to provide the following keyboard shortcuts: + \table + \header \i Keypress \i Action + \row \i Alt+Left Arrow \i \l backward() + \row \i Alt+Right Arrow \i \l forward() + \row \i Alt+Up Arrow \i \l home() + \endtable +*/ +void QTextBrowser::keyPressEvent( QKeyEvent * e ) +{ + if ( e->state() & AltButton ) { + switch (e->key()) { + case Key_Right: + forward(); + return; + case Key_Left: + backward(); + return; + case Key_Up: + home(); + return; + } + } + QTextEdit::keyPressEvent(e); +} + +class QTextDetailPopup : public QWidget +{ +public: + QTextDetailPopup() + : QWidget ( 0, "automatic QText detail widget", WType_Popup | WDestructiveClose ) + { + } + +protected: + + void mousePressEvent( QMouseEvent*) + { + close(); + } +}; + + +void QTextBrowser::popupDetail( const QString& contents, const QPoint& pos ) +{ + + const int shadowWidth = 6; // also used as '5' and '6' and even '8' below + const int vMargin = 8; + const int hMargin = 12; + + QWidget* popup = new QTextDetailPopup; + popup->setBackgroundMode( QWidget::NoBackground ); + + QSimpleRichText* doc = new QSimpleRichText( contents, popup->font() ); + doc->adjustSize(); + QRect r( 0, 0, doc->width(), doc->height() ); + + int w = r.width() + 2*hMargin; + int h = r.height() + 2*vMargin; + + popup->resize( w + shadowWidth, h + shadowWidth ); + + // okay, now to find a suitable location + //###### we need a global fancy popup positioning somewhere + popup->move(pos - popup->rect().center()); + if (popup->geometry().right() > QApplication::desktop()->width()) + popup->move( QApplication::desktop()->width() - popup->width(), + popup->y() ); + if (popup->geometry().bottom() > QApplication::desktop()->height()) + popup->move( popup->x(), + QApplication::desktop()->height() - popup->height() ); + if ( popup->x() < 0 ) + popup->move( 0, popup->y() ); + if ( popup->y() < 0 ) + popup->move( popup->x(), 0 ); + + + popup->show(); + + // now for super-clever shadow stuff. super-clever mostly in + // how many window system problems it skirts around. + + QPainter p( popup ); + p.setPen( QApplication::palette().active().foreground() ); + p.drawRect( 0, 0, w, h ); + p.setPen( QApplication::palette().active().mid() ); + p.setBrush( QColor( 255, 255, 240 ) ); + p.drawRect( 1, 1, w-2, h-2 ); + p.setPen( black ); + + doc->draw( &p, hMargin, vMargin, r, popup->colorGroup(), 0 ); + delete doc; + + p.drawPoint( w + 5, 6 ); + p.drawLine( w + 3, 6, + w + 5, 8 ); + p.drawLine( w + 1, 6, + w + 5, 10 ); + int i; + for( i=7; i < h; i += 2 ) + p.drawLine( w, i, + w + 5, i + 5 ); + for( i = w - i + h; i > 6; i -= 2 ) + p.drawLine( i, h, + i + 5, h + 5 ); + for( ; i > 0 ; i -= 2 ) + p.drawLine( 6, h + 6 - i, + i + 5, h + 5 ); +} + +/*! + \fn void QTextBrowser::setText( const QString &txt ) + + \overload + + Sets the text to \a txt. +*/ + +/*! + \reimp +*/ + +void QTextBrowser::setText( const QString &txt, const QString &context ) +{ + d->textOrSourceChanged = TRUE; + d->curmark = ""; + d->curmain = ""; + QTextEdit::setText( txt, context ); +} + +void QTextBrowser::emitHighlighted( const QString &s ) +{ + emit highlighted( s ); +} + +void QTextBrowser::emitLinkClicked( const QString &s ) +{ + d->textOrSourceChanged = FALSE; + emit linkClicked( s ); + if ( !d->textOrSourceChanged ) + setSource( s ); +} + +#endif // QT_NO_TEXTBROWSER diff --git a/src/widgets/qtextbrowser.h b/src/widgets/qtextbrowser.h new file mode 100644 index 0000000..50b02eb --- /dev/null +++ b/src/widgets/qtextbrowser.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Definition of the QTextBrowser class +** +** Created : 990101 +** +** 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. +** +**********************************************************************/ + +#ifndef QTEXTBROWSER_H +#define QTEXTBROWSER_H + +#ifndef QT_H +#include "qptrlist.h" +#include "qpixmap.h" +#include "qcolor.h" +#include "qtextedit.h" +#endif // QT_H + +#ifndef QT_NO_TEXTBROWSER + +class QTextBrowserData; + +class Q_EXPORT QTextBrowser : public QTextEdit +{ + Q_OBJECT + Q_PROPERTY( QString source READ source WRITE setSource ) + Q_OVERRIDE( int undoDepth DESIGNABLE false SCRIPTABLE false ) + Q_OVERRIDE( bool overwriteMode DESIGNABLE false SCRIPTABLE false ) + Q_OVERRIDE( bool modified SCRIPTABLE false) + Q_OVERRIDE( bool readOnly DESIGNABLE false SCRIPTABLE false ) + Q_OVERRIDE( bool undoRedoEnabled DESIGNABLE false SCRIPTABLE false ) + +public: + QTextBrowser( QWidget* parent=0, const char* name=0 ); + ~QTextBrowser(); + + QString source() const; + +public slots: + virtual void setSource(const QString& name); + virtual void backward(); + virtual void forward(); + virtual void home(); + virtual void reload(); + void setText( const QString &txt ) { setText( txt, QString::null ); } + virtual void setText( const QString &txt, const QString &context ); + +signals: + void backwardAvailable( bool ); + void forwardAvailable( bool ); + void sourceChanged( const QString& ); + void highlighted( const QString& ); + void linkClicked( const QString& ); + void anchorClicked( const QString&, const QString& ); + +protected: + void keyPressEvent( QKeyEvent * e); + +private: + void popupDetail( const QString& contents, const QPoint& pos ); + bool linksEnabled() const { return TRUE; } + void emitHighlighted( const QString &s ); + void emitLinkClicked( const QString &s ); + QTextBrowserData *d; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QTextBrowser( const QTextBrowser & ); + QTextBrowser& operator=( const QTextBrowser & ); +#endif +}; + +#endif // QT_NO_TEXTBROWSER + +#endif // QTEXTBROWSER_H diff --git a/src/widgets/qtextedit.cpp b/src/widgets/qtextedit.cpp new file mode 100644 index 0000000..0908e84 --- /dev/null +++ b/src/widgets/qtextedit.cpp @@ -0,0 +1,7467 @@ +/**************************************************************************** +** +** Implementation of the QTextEdit class +** +** Created : 990101 +** +** 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 "qtextedit.h" + +#ifndef QT_NO_TEXTEDIT + +// Keep this position to avoid patch rejection +#ifndef QT_NO_IM +#include "qinputcontext.h" +#endif + +#include "../kernel/qrichtext_p.h" +#include "qpainter.h" +#include "qpen.h" +#include "qbrush.h" +#include "qpixmap.h" +#include "qfont.h" +#include "qcolor.h" +#include "qstyle.h" +#include "qsize.h" +#include "qevent.h" +#include "qtimer.h" +#include "qapplication.h" +#include "qlistbox.h" +#include "qvbox.h" +#include "qapplication.h" +#include "qclipboard.h" +#include "qcolordialog.h" +#include "qfontdialog.h" +#include "qstylesheet.h" +#include "qdragobject.h" +#include "qurl.h" +#include "qcursor.h" +#include "qregexp.h" +#include "qpopupmenu.h" +#include "qptrstack.h" +#include "qmetaobject.h" +#include "qtextbrowser.h" +#include <private/qucom_p.h> +#include "private/qsyntaxhighlighter_p.h" +#include <qguardedptr.h> + +#ifndef QT_NO_ACCEL +#include <qkeysequence.h> +#define ACCEL_KEY(k) "\t" + QString(QKeySequence( Qt::CTRL | Qt::Key_ ## k )) +#else +#define ACCEL_KEY(k) "\t" + QString("Ctrl+" #k) +#endif + +#ifdef QT_TEXTEDIT_OPTIMIZATION +#define LOGOFFSET(i) d->logOffset + i +#endif + +struct QUndoRedoInfoPrivate +{ + QTextString text; +}; + +class QTextEditPrivate +{ +public: + QTextEditPrivate() + :preeditStart(-1),preeditLength(-1),ensureCursorVisibleInShowEvent(FALSE), + tabChangesFocus(FALSE), +#ifndef QT_NO_CLIPBOARD + clipboard_mode( QClipboard::Clipboard ), +#endif +#ifdef QT_TEXTEDIT_OPTIMIZATION + od(0), optimMode(FALSE), + maxLogLines(-1), + logOffset(0), +#endif + autoFormatting( (uint)QTextEdit::AutoAll ) + { + for ( int i=0; i<7; i++ ) + id[i] = 0; + } + int id[ 7 ]; + int preeditStart; + int preeditLength; + bool composeMode() const { return ( preeditLength > 0 ); } + + uint ensureCursorVisibleInShowEvent : 1; + uint tabChangesFocus : 1; + QString scrollToAnchor; // used to deferr scrollToAnchor() until the show event when we are resized + QString pressedName; + QString onName; +#ifndef QT_NO_CLIPBOARD + QClipboard::Mode clipboard_mode; +#endif + QTimer *trippleClickTimer; + QPoint trippleClickPoint; +#ifdef QT_TEXTEDIT_OPTIMIZATION + QTextEditOptimPrivate * od; + bool optimMode : 1; + int maxLogLines; + int logOffset; +#endif + uint autoFormatting; +}; + +#ifndef QT_NO_MIME +class QRichTextDrag : public QTextDrag +{ +public: + QRichTextDrag( QWidget *dragSource = 0, const char *name = 0 ); + + void setPlainText( const QString &txt ) { setText( txt ); } + void setRichText( const QString &txt ) { richTxt = txt; } + + virtual QByteArray encodedData( const char *mime ) const; + virtual const char* format( int i ) const; + + static bool decode( QMimeSource *e, QString &str, const QCString &mimetype, + const QCString &subtype ); + static bool canDecode( QMimeSource* e ); + +private: + QString richTxt; + +}; + +QRichTextDrag::QRichTextDrag( QWidget *dragSource, const char *name ) + : QTextDrag( dragSource, name ) +{ +} + +QByteArray QRichTextDrag::encodedData( const char *mime ) const +{ + if ( qstrcmp( "application/x-qrichtext", mime ) == 0 ) { + return richTxt.utf8(); // #### perhaps we should use USC2 instead? + } else + return QTextDrag::encodedData( mime ); +} + +bool QRichTextDrag::decode( QMimeSource *e, QString &str, const QCString &mimetype, + const QCString &subtype ) +{ + if ( mimetype == "application/x-qrichtext" ) { + // do richtext decode + const char *mime; + int i; + for ( i = 0; ( mime = e->format( i ) ); ++i ) { + if ( qstrcmp( "application/x-qrichtext", mime ) != 0 ) + continue; + str = QString::fromUtf8( e->encodedData( mime ) ); + return TRUE; + } + return FALSE; + } + + // do a regular text decode + QCString subt = subtype; + return QTextDrag::decode( e, str, subt ); +} + +bool QRichTextDrag::canDecode( QMimeSource* e ) +{ + if ( e->provides( "application/x-qrichtext" ) ) + return TRUE; + return QTextDrag::canDecode( e ); +} + +const char* QRichTextDrag::format( int i ) const +{ + if ( QTextDrag::format( i ) ) + return QTextDrag::format( i ); + if ( QTextDrag::format( i-1 ) ) + return "application/x-qrichtext"; + return 0; +} + +#endif + +static bool block_set_alignment = FALSE; + +/*! + \class QTextEdit qtextedit.h + \brief The QTextEdit widget provides a powerful single-page rich text editor. + + \ingroup basic + \ingroup text + \mainclass + + \tableofcontents + + \section1 Introduction and Concepts + + QTextEdit is an advanced WYSIWYG viewer/editor supporting rich + text formatting using HTML-style tags. It is optimized to handle + large documents and to respond quickly to user input. + + QTextEdit has four modes of operation: + \table + \header \i Mode \i Command \i Notes + \row \i Plain Text Editor \i setTextFormat(PlainText) + \i Set text with setText(); text() returns plain text. Text + attributes (e.g. colors) can be set, but plain text is always + returned. + \row \i Rich Text Editor \i setTextFormat(RichText) + \i Set text with setText(); text() returns rich text. Rich + text editing is fairly limited. You can't set margins or + insert images for example (although you can read and + correctly display files that have margins set and that + include images). This mode is mostly useful for editing small + amounts of rich text. <sup>1.</sup> + \row \i Text Viewer \i setReadOnly(TRUE) + \i Set text with setText() or append() (which has no undo + history so is faster and uses less memory); text() returns + plain or rich text depending on the textFormat(). This mode + can correctly display a large subset of HTML tags. + \row \i Log Viewer \i setTextFormat(LogText) + \i Append text using append(). The widget is set to be read + only and rich text support is disabled although a few HTML + tags (for color, bold, italic and underline) may be used. + (See \link #logtextmode LogText mode\endlink for details.) + \endtable + + <sup>1.</sup><small>A more complete API that supports setting + margins, images, etc., is planned for a later Qt release.</small> + + QTextEdit can be used as a syntax highlighting editor when used in + conjunction with QSyntaxHighlighter. + + We recommend that you always call setTextFormat() to set the mode + you want to use. If you use \c AutoText then setText() and + append() will try to determine whether the text they are given is + plain text or rich text. If you use \c RichText then setText() and + append() will assume that the text they are given is rich text. + insert() simply inserts the text it is given. + + QTextEdit works on paragraphs and characters. A paragraph is a + formatted string which is word-wrapped to fit into the width of + the widget. By default when reading plain text, one newline + signify a paragraph. A document consists of zero or more + paragraphs, indexed from 0. Characters are indexed on a + per-paragraph basis, also indexed from 0. The words in the + paragraph are aligned in accordance with the paragraph's + alignment(). Paragraphs are separated by hard line breaks. Each + character within a paragraph has its own attributes, for example, + font and color. + + The text edit documentation uses the following concepts: + \list + \i \e{current format} -- + this is the format at the current cursor position, \e and it + is the format of the selected text if any. + \i \e{current paragraph} -- the paragraph which contains the + cursor. + \endlist + + QTextEdit can display images (using QMimeSourceFactory), lists and + tables. If the text is too large to view within the text edit's + viewport, scrollbars will appear. The text edit can load both + plain text and HTML files (a subset of HTML 3.2 and 4). The + rendering style and the set of valid tags are defined by a + styleSheet(). Custom tags can be created and placed in a custom + style sheet. Change the style sheet with \l{setStyleSheet()}; see + QStyleSheet for details. The images identified by image tags are + displayed if they can be interpreted using the text edit's + \l{QMimeSourceFactory}; see setMimeSourceFactory(). + + If you want a text browser with more navigation use QTextBrowser. + If you just need to display a small piece of rich text use QLabel + or QSimpleRichText. + + If you create a new QTextEdit, and want to allow the user to edit + rich text, call setTextFormat(Qt::RichText) to ensure that the + text is treated as rich text. (Rich text uses HTML tags to set + text formatting attributes. See QStyleSheet for information on the + HTML tags that are supported.). If you don't call setTextFormat() + explicitly the text edit will guess from the text itself whether + it is rich text or plain text. This means that if the text looks + like HTML or XML it will probably be interpreted as rich text, so + you should call setTextFormat(Qt::PlainText) to preserve such + text. + + Note that we do not intend to add a full-featured web browser + widget to Qt (because that would easily double Qt's size and only + a few applications would benefit from it). The rich + text support in Qt is designed to provide a fast, portable and + efficient way to add reasonable online help facilities to + applications, and to provide a basis for rich text editors. + + \section1 Using QTextEdit as a Display Widget + + QTextEdit can display a large HTML subset, including tables and + images. + + The text is set or replaced using setText() which deletes any + existing text and replaces it with the text passed in the + setText() call. If you call setText() with legacy HTML (with + setTextFormat(RichText) in force), and then call text(), the text + that is returned may have different markup, but will render the + same. Text can be inserted with insert(), paste(), pasteSubType() + and append(). Text that is appended does not go into the undo + history; this makes append() faster and consumes less memory. Text + can also be cut(). The entire text is deleted with clear() and the + selected text is deleted with removeSelectedText(). Selected + (marked) text can also be deleted with del() (which will delete + the character to the right of the cursor if no text is selected). + + Loading and saving text is achieved using setText() and text(), + for example: + \code + QFile file( fileName ); // Read the text from a file + if ( file.open( IO_ReadOnly ) ) { + QTextStream stream( &file ); + textEdit->setText( stream.read() ); + } + + QFile file( fileName ); // Write the text to a file + if ( file.open( IO_WriteOnly ) ) { + QTextStream stream( &file ); + stream << textEdit->text(); + textEdit->setModified( FALSE ); + } + \endcode + + By default the text edit wraps words at whitespace to fit within + the text edit widget. The setWordWrap() function is used to + specify the kind of word wrap you want, or \c NoWrap if you don't + want any wrapping. Call setWordWrap() to set a fixed pixel width + \c FixedPixelWidth, or character column (e.g. 80 column) \c + FixedColumnWidth with the pixels or columns specified with + setWrapColumnOrWidth(). If you use word wrap to the widget's width + \c WidgetWidth, you can specify whether to break on whitespace or + anywhere with setWrapPolicy(). + + The background color is set differently than other widgets, using + setPaper(). You specify a brush style which could be a plain color + or a complex pixmap. + + Hypertext links are automatically underlined; this can be changed + with setLinkUnderline(). The tab stop width is set with + setTabStopWidth(). + + The zoomIn() and zoomOut() functions can be used to resize the + text by increasing (decreasing for zoomOut()) the point size used. + Images are not affected by the zoom functions. + + The lines() function returns the number of lines in the text and + paragraphs() returns the number of paragraphs. The number of lines + within a particular paragraph is returned by linesOfParagraph(). + The length of the entire text in characters is returned by + length(). + + You can scroll to an anchor in the text, e.g. + \c{<a name="anchor">} with scrollToAnchor(). The find() function + can be used to find and select a given string within the text. + + A read-only QTextEdit provides the same functionality as the + (obsolete) QTextView. (QTextView is still supplied for + compatibility with old code.) + + \section2 Read-only key bindings + + When QTextEdit is used read-only the key-bindings are limited to + navigation, and text may only be selected with the mouse: + \table + \header \i Keypresses \i Action + \row \i UpArrow \i Move one line up + \row \i DownArrow \i Move one line down + \row \i LeftArrow \i Move one character left + \row \i RightArrow \i Move one character right + \row \i PageUp \i Move one (viewport) page up + \row \i PageDown \i Move one (viewport) page down + \row \i Home \i Move to the beginning of the text + \row \i End \i Move to the end of the text + \row \i Shift+Wheel + \i Scroll the page horizontally (the Wheel is the mouse wheel) + \row \i Ctrl+Wheel \i Zoom the text + \endtable + + The text edit may be able to provide some meta-information. For + example, the documentTitle() function will return the text from + within HTML \c{<title>} tags. + + The text displayed in a text edit has a \e context. The context is + a path which the text edit's QMimeSourceFactory uses to resolve + the locations of files and images. It is passed to the + mimeSourceFactory() when quering data. (See QTextEdit() and + \l{context()}.) + + \target logtextmode + \section2 Using QTextEdit in LogText Mode + + Setting the text format to \c LogText puts the widget in a special + mode which is optimized for very large texts. Editing, word wrap, + and rich text support are disabled in this mode (the widget is + explicitly made read-only). This allows the text to be stored in a + different, more memory efficient manner. However, a certain degree + of text formatting is supported through the use of formatting tags. + A tag is delimited by \c < and \c {>}. The characters \c {<}, \c > + and \c & are escaped by using \c {<}, \c {>} and \c {&}. + A tag pair consists of a left and a right tag (or open/close tags). + Left-tags mark the starting point for formatting, while right-tags + mark the ending point. A right-tag always start with a \c / before + the tag keyword. For example \c <b> and \c </b> are a tag pair. + Tags can be nested, but they have to be closed in the same order as + they are opened. For example, \c <b><u></u></b> is valid, while \c + <b><u></b></u> will output an error message. + + By using tags it is possible to change the color, bold, italic and + underline settings for a piece of text. A color can be specified + by using the HTML font tag \c {<font color=colorname>}. The color + name can be one of the color names from the X11 color database, or + a RGB hex value (e.g \c {#00ff00}). Example of valid color tags: + \c {<font color=red>}, \c {<font color="light blue">}, \c {<font + color="#223344">}. Bold, italic and underline settings can be + specified by the tags \c {<b>}, \c <i> and \c {<u>}. Note that a + tag does not necessarily have to be closed. A valid example: + \code + This is <font color=red>red</font> while <b>this</b> is <font color=blue>blue</font>. + <font color=green><font color=yellow>Yellow,</font> and <u>green</u>. + \endcode + + Stylesheets can also be used in LogText mode. To create and use a + custom tag, you could do the following: + \code + QTextEdit * log = new QTextEdit( this ); + log->setTextFormat( Qt::LogText ); + QStyleSheetItem * item = new QStyleSheetItem( log->styleSheet(), "mytag" ); + item->setColor( "red" ); + item->setFontWeight( QFont::Bold ); + item->setFontUnderline( TRUE ); + log->append( "This is a <mytag>custom tag</mytag>!" ); + \endcode + Note that only the color, bold, underline and italic attributes of + a QStyleSheetItem is used in LogText mode. + + Note that you can use setMaxLogLines() to limit the number of + lines the widget can hold in LogText mode. + + There are a few things that you need to be aware of when the + widget is in this mode: + \list + \i Functions that deal with rich text formatting and cursor + movement will not work or return anything valid. + \i Lines are equivalent to paragraphs. + \endlist + + \section1 Using QTextEdit as an Editor + + All the information about using QTextEdit as a display widget also + applies here. + + The current format's attributes are set with setItalic(), + setBold(), setUnderline(), setFamily() (font family), + setPointSize(), setColor() and setCurrentFont(). The current + paragraph's alignment is set with setAlignment(). + + Use setSelection() to select text. The setSelectionAttributes() + function is used to indicate how selected text should be + displayed. Use hasSelectedText() to find out if any text is + selected. The currently selected text's position is available + using getSelection() and the selected text itself is returned by + selectedText(). The selection can be copied to the clipboard with + copy(), or cut to the clipboard with cut(). It can be deleted with + removeSelectedText(). The entire text can be selected (or + unselected) using selectAll(). QTextEdit supports multiple + selections. Most of the selection functions operate on the default + selection, selection 0. If the user presses a non-selecting key, + e.g. a cursor key without also holding down Shift, all selections + are cleared. + + Set and get the position of the cursor with setCursorPosition() + and getCursorPosition() respectively. When the cursor is moved, + the signals currentFontChanged(), currentColorChanged() and + currentAlignmentChanged() are emitted to reflect the font, color + and alignment at the new cursor position. + + If the text changes, the textChanged() signal is emitted, and if + the user inserts a new line by pressing Return or Enter, + returnPressed() is emitted. The isModified() function will return + TRUE if the text has been modified. + + QTextEdit provides command-based undo and redo. To set the depth + of the command history use setUndoDepth() which defaults to 100 + steps. To undo or redo the last operation call undo() or redo(). + The signals undoAvailable() and redoAvailable() indicate whether + the undo and redo operations can be executed. + + The indent() function is used to reindent a paragraph. It is + useful for code editors, for example in \link designer-manual.book + Qt Designer\endlink's code editor \e{Ctrl+I} invokes the indent() + function. + + \section2 Editing key bindings + + The list of key-bindings which are implemented for editing: + \table + \header \i Keypresses \i Action + \row \i Backspace \i Delete the character to the left of the cursor + \row \i Delete \i Delete the character to the right of the cursor + \row \i Ctrl+A \i Move the cursor to the beginning of the line + \row \i Ctrl+B \i Move the cursor one character left + \row \i Ctrl+C \i Copy the marked text to the clipboard (also + Ctrl+Insert under Windows) + \row \i Ctrl+D \i Delete the character to the right of the cursor + \row \i Ctrl+E \i Move the cursor to the end of the line + \row \i Ctrl+F \i Move the cursor one character right + \row \i Ctrl+H \i Delete the character to the left of the cursor + \row \i Ctrl+K \i Delete to end of line + \row \i Ctrl+N \i Move the cursor one line down + \row \i Ctrl+P \i Move the cursor one line up + \row \i Ctrl+V \i Paste the clipboard text into line edit + (also Shift+Insert under Windows) + \row \i Ctrl+X \i Cut the marked text, copy to clipboard + (also Shift+Delete under Windows) + \row \i Ctrl+Z \i Undo the last operation + \row \i Ctrl+Y \i Redo the last operation + \row \i LeftArrow \i Move the cursor one character left + \row \i Ctrl+LeftArrow \i Move the cursor one word left + \row \i RightArrow \i Move the cursor one character right + \row \i Ctrl+RightArrow \i Move the cursor one word right + \row \i UpArrow \i Move the cursor one line up + \row \i Ctrl+UpArrow \i Move the cursor one word up + \row \i DownArrow \i Move the cursor one line down + \row \i Ctrl+Down Arrow \i Move the cursor one word down + \row \i PageUp \i Move the cursor one page up + \row \i PageDown \i Move the cursor one page down + \row \i Home \i Move the cursor to the beginning of the line + \row \i Ctrl+Home \i Move the cursor to the beginning of the text + \row \i End \i Move the cursor to the end of the line + \row \i Ctrl+End \i Move the cursor to the end of the text + \row \i Shift+Wheel \i Scroll the page horizontally + (the Wheel is the mouse wheel) + \row \i Ctrl+Wheel \i Zoom the text + \endtable + + To select (mark) text hold down the Shift key whilst pressing one + of the movement keystrokes, for example, \e{Shift+Right Arrow} + will select the character to the right, and \e{Shift+Ctrl+Right + Arrow} will select the word to the right, etc. + + By default the text edit widget operates in insert mode so all + text that the user enters is inserted into the text edit and any + text to the right of the cursor is moved out of the way. The mode + can be changed to overwrite, where new text overwrites any text to + the right of the cursor, using setOverwriteMode(). +*/ + +/*! + \enum QTextEdit::AutoFormatting + + \value AutoNone Do not perform any automatic formatting + \value AutoBulletList Only automatically format bulletted lists + \value AutoAll Apply all available autoformatting +*/ + + +/*! + \enum QTextEdit::KeyboardAction + + This enum is used by doKeyboardAction() to specify which action + should be executed: + + \value ActionBackspace Delete the character to the left of the + cursor. + + \value ActionDelete Delete the character to the right of the + cursor. + + \value ActionReturn Split the paragraph at the cursor position. + + \value ActionKill If the cursor is not at the end of the + paragraph, delete the text from the cursor position until the end + of the paragraph. If the cursor is at the end of the paragraph, + delete the hard line break at the end of the paragraph: this will + cause this paragraph to be joined with the following paragraph. + + \value ActionWordBackspace Delete the word to the left of the + cursor position. + + \value ActionWordDelete Delete the word to the right of the + cursor position + +*/ + +/*! + \enum QTextEdit::VerticalAlignment + + This enum is used to set the vertical alignment of the text. + + \value AlignNormal Normal alignment + \value AlignSuperScript Superscript + \value AlignSubScript Subscript +*/ + +/*! + \enum QTextEdit::TextInsertionFlags + + \internal + + \value RedoIndentation + \value CheckNewLines + \value RemoveSelected +*/ + + +/*! + \fn void QTextEdit::copyAvailable(bool yes) + + This signal is emitted when text is selected or de-selected in the + text edit. + + When text is selected this signal will be emitted with \a yes set + to TRUE. If no text has been selected or if the selected text is + de-selected this signal is emitted with \a yes set to FALSE. + + If \a yes is TRUE then copy() can be used to copy the selection to + the clipboard. If \a yes is FALSE then copy() does nothing. + + \sa selectionChanged() +*/ + + +/*! + \fn void QTextEdit::textChanged() + + This signal is emitted whenever the text in the text edit changes. + + \sa setText() append() +*/ + +/*! + \fn void QTextEdit::selectionChanged() + + This signal is emitted whenever the selection changes. + + \sa setSelection() copyAvailable() +*/ + +/*! \fn QTextDocument *QTextEdit::document() const + + \internal + + This function returns the QTextDocument which is used by the text + edit. +*/ + +/*! \fn void QTextEdit::setDocument( QTextDocument *doc ) + + \internal + + This function sets the QTextDocument which should be used by the text + edit to \a doc. This can be used, for example, if you want to + display a document using multiple views. You would create a + QTextDocument and set it to the text edits which should display it. + You would need to connect to the textChanged() and + selectionChanged() signals of all the text edits and update them all + accordingly (preferably with a slight delay for efficiency reasons). +*/ + +/*! + \enum QTextEdit::CursorAction + + This enum is used by moveCursor() to specify in which direction + the cursor should be moved: + + \value MoveBackward Moves the cursor one character backward + + \value MoveWordBackward Moves the cursor one word backward + + \value MoveForward Moves the cursor one character forward + + \value MoveWordForward Moves the cursor one word forward + + \value MoveUp Moves the cursor up one line + + \value MoveDown Moves the cursor down one line + + \value MoveLineStart Moves the cursor to the beginning of the line + + \value MoveLineEnd Moves the cursor to the end of the line + + \value MoveHome Moves the cursor to the beginning of the document + + \value MoveEnd Moves the cursor to the end of the document + + \value MovePgUp Moves the cursor one viewport page up + + \value MovePgDown Moves the cursor one viewport page down +*/ + +/*! + \enum Qt::AnchorAttribute + + An anchor has one or more of the following attributes: + + \value AnchorName the name attribute of the anchor. This attribute is + used when scrolling to an anchor in the document. + + \value AnchorHref the href attribute of the anchor. This attribute is + used when a link is clicked to determine what content to load. +*/ + +/*! + \property QTextEdit::overwriteMode + \brief the text edit's overwrite mode + + If FALSE (the default) characters entered by the user are inserted + with any characters to the right being moved out of the way. If + TRUE, the editor is in overwrite mode, i.e. characters entered by + the user overwrite any characters to the right of the cursor + position. +*/ + +/*! + \fn void QTextEdit::setCurrentFont( const QFont &f ) + + Sets the font of the current format to \a f. + + If the widget is in \c LogText mode this function will do + nothing. Use setFont() instead. + + \sa currentFont() setPointSize() setFamily() +*/ + +/*! + \property QTextEdit::undoDepth + \brief the depth of the undo history + + The maximum number of steps in the undo/redo history. The default + is 100. + + \sa undo() redo() +*/ + +/*! + \fn void QTextEdit::undoAvailable( bool yes ) + + This signal is emitted when the availability of undo changes. If + \a yes is TRUE, then undo() will work until undoAvailable( FALSE ) + is next emitted. + + \sa undo() undoDepth() +*/ + +/*! + \fn void QTextEdit::modificationChanged( bool m ) + + This signal is emitted when the modification status of the + document has changed. If \a m is TRUE, the document was modified, + otherwise the modification state has been reset to unmodified. + + \sa modified +*/ + +/*! + \fn void QTextEdit::redoAvailable( bool yes ) + + This signal is emitted when the availability of redo changes. If + \a yes is TRUE, then redo() will work until redoAvailable( FALSE ) + is next emitted. + + \sa redo() undoDepth() +*/ + +/*! + \fn void QTextEdit::currentFontChanged( const QFont &f ) + + This signal is emitted if the font of the current format has + changed. + + The new font is \a f. + + \sa setCurrentFont() +*/ + +/*! + \fn void QTextEdit::currentColorChanged( const QColor &c ) + + This signal is emitted if the color of the current format has + changed. + + The new color is \a c. + + \sa setColor() +*/ + +/*! + \fn void QTextEdit::currentVerticalAlignmentChanged( VerticalAlignment a ) + + This signal is emitted if the vertical alignment of the current + format has changed. + + The new vertical alignment is \a a. + + \sa setVerticalAlignment() +*/ + +/*! + \fn void QTextEdit::currentAlignmentChanged( int a ) + + This signal is emitted if the alignment of the current paragraph + has changed. + + The new alignment is \a a. + + \sa setAlignment() +*/ + +/*! + \fn void QTextEdit::cursorPositionChanged( QTextCursor *c ) + + \internal +*/ + +/*! + \fn void QTextEdit::cursorPositionChanged( int para, int pos ) + + This signal is emitted if the position of the cursor has changed. + \a para contains the paragraph index and \a pos contains the + character position within the paragraph. + + \sa setCursorPosition() +*/ + +/*! + \fn void QTextEdit::clicked( int para, int pos ) + + This signal is emitted when the mouse is clicked on the paragraph + \a para at character position \a pos. + + \sa doubleClicked() +*/ + +/*! \fn void QTextEdit::doubleClicked( int para, int pos ) + + This signal is emitted when the mouse is double-clicked on the + paragraph \a para at character position \a pos. + + \sa clicked() +*/ + + +/*! + \fn void QTextEdit::returnPressed() + + This signal is emitted if the user pressed the Return or the Enter + key. +*/ + +/*! + \fn QTextCursor *QTextEdit::textCursor() const + + Returns the text edit's text cursor. + + \warning QTextCursor is not in the public API, but in special + circumstances you might wish to use it. +*/ + +/*! + Constructs an empty QTextEdit called \a name, with parent \a + parent. +*/ + +QTextEdit::QTextEdit( QWidget *parent, const char *name ) + : QScrollView( parent, name, WStaticContents | WNoAutoErase ), + doc( new QTextDocument( 0 ) ), undoRedoInfo( doc ) +{ + init(); +} + +/*! + Constructs a QTextEdit called \a name, with parent \a parent. The + text edit will display the text \a text using context \a context. + + The \a context is a path which the text edit's QMimeSourceFactory + uses to resolve the locations of files and images. It is passed to + the mimeSourceFactory() when quering data. + + For example if the text contains an image tag, + \c{<img src="image.png">}, and the context is "path/to/look/in", the + QMimeSourceFactory will try to load the image from + "path/to/look/in/image.png". If the tag was + \c{<img src="/image.png">}, the context will not be used (because + QMimeSourceFactory recognizes that we have used an absolute path) + and will try to load "/image.png". The context is applied in exactly + the same way to \e hrefs, for example, + \c{<a href="target.html">Target</a>}, would resolve to + "path/to/look/in/target.html". +*/ + +QTextEdit::QTextEdit( const QString& text, const QString& context, + QWidget *parent, const char *name) + : QScrollView( parent, name, WStaticContents | WNoAutoErase ), + doc( new QTextDocument( 0 ) ), undoRedoInfo( doc ) +{ + init(); + setText( text, context ); +} + +/*! + \reimp +*/ + +QTextEdit::~QTextEdit() +{ + delete undoRedoInfo.d; + undoRedoInfo.d = 0; + delete cursor; + delete doc; +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + delete d->od; +#endif + delete d; +} + +void QTextEdit::init() +{ + d = new QTextEditPrivate; + doc->formatCollection()->setPaintDevice( this ); + undoEnabled = TRUE; + readonly = TRUE; + setReadOnly( FALSE ); + setFrameStyle( LineEditPanel | Sunken ); + connect( doc, SIGNAL( minimumWidthChanged(int) ), + this, SLOT( documentWidthChanged(int) ) ); + + mousePressed = FALSE; + inDoubleClick = FALSE; + modified = FALSE; + onLink = QString::null; + d->onName = QString::null; + overWrite = FALSE; + wrapMode = WidgetWidth; + wrapWidth = -1; + wPolicy = AtWhiteSpace; + inDnD = FALSE; + doc->setFormatter( new QTextFormatterBreakWords ); + doc->formatCollection()->defaultFormat()->setFont( QScrollView::font() ); + doc->formatCollection()->defaultFormat()->setColor( colorGroup().color( QColorGroup::Text ) ); + currentFormat = doc->formatCollection()->defaultFormat(); + currentAlignment = Qt::AlignAuto; + + setBackgroundMode( PaletteBase ); + viewport()->setBackgroundMode( PaletteBase ); + viewport()->setAcceptDrops( TRUE ); + resizeContents( 0, doc->lastParagraph() ? + ( doc->lastParagraph()->paragId() + 1 ) * doc->formatCollection()->defaultFormat()->height() : 0 ); + + setKeyCompression( TRUE ); + viewport()->setMouseTracking( TRUE ); +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif + cursor = new QTextCursor( doc ); + + formatTimer = new QTimer( this ); + connect( formatTimer, SIGNAL( timeout() ), + this, SLOT( formatMore() ) ); + lastFormatted = doc->firstParagraph(); + + scrollTimer = new QTimer( this ); + connect( scrollTimer, SIGNAL( timeout() ), + this, SLOT( autoScrollTimerDone() ) ); + + interval = 0; + changeIntervalTimer = new QTimer( this ); + connect( changeIntervalTimer, SIGNAL( timeout() ), + this, SLOT( doChangeInterval() ) ); + + cursorVisible = TRUE; + blinkTimer = new QTimer( this ); + connect( blinkTimer, SIGNAL( timeout() ), + this, SLOT( blinkCursor() ) ); + +#ifndef QT_NO_DRAGANDDROP + dragStartTimer = new QTimer( this ); + connect( dragStartTimer, SIGNAL( timeout() ), + this, SLOT( startDrag() ) ); +#endif + + d->trippleClickTimer = new QTimer( this ); + + formatMore(); + + blinkCursorVisible = FALSE; + + viewport()->setFocusProxy( this ); + viewport()->setFocusPolicy( WheelFocus ); + setInputMethodEnabled( TRUE ); + viewport()->installEventFilter( this ); + connect( this, SIGNAL(horizontalSliderReleased()), this, SLOT(sliderReleased()) ); + connect( this, SIGNAL(verticalSliderReleased()), this, SLOT(sliderReleased()) ); + installEventFilter( this ); +} + +void QTextEdit::paintDocument( bool drawAll, QPainter *p, int cx, int cy, int cw, int ch ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + Q_ASSERT( !d->optimMode ); + if ( d->optimMode ) + return; +#endif + + bool drawCur = hasFocus() || viewport()->hasFocus(); + if (( hasSelectedText() && !style().styleHint( QStyle::SH_BlinkCursorWhenTextSelected ) ) || + isReadOnly() || !cursorVisible || doc->hasSelection( QTextDocument::IMSelectionText )) + drawCur = FALSE; + QColorGroup g = colorGroup(); + const QColorGroup::ColorRole backRole = QPalette::backgroundRoleFromMode(backgroundMode()); + if ( doc->paper() ) + g.setBrush( backRole, *doc->paper() ); + + if ( contentsY() < doc->y() ) { + p->fillRect( contentsX(), contentsY(), visibleWidth(), doc->y(), + g.brush( backRole ) ); + } + if ( drawAll && doc->width() - contentsX() < cx + cw ) { + p->fillRect( doc->width() - contentsX(), cy, cx + cw - doc->width() + contentsX(), ch, + g.brush( backRole ) ); + } + + p->setBrushOrigin( -contentsX(), -contentsY() ); + + lastFormatted = doc->draw( p, cx, cy, cw, ch, g, !drawAll, drawCur, cursor ); + + if ( lastFormatted == doc->lastParagraph() ) + resizeContents( contentsWidth(), doc->height() ); + + if ( contentsHeight() < visibleHeight() && ( !doc->lastParagraph() || doc->lastParagraph()->isValid() ) && drawAll ) + p->fillRect( 0, contentsHeight(), visibleWidth(), + visibleHeight() - contentsHeight(), g.brush( backRole ) ); +} + +/*! + \reimp +*/ + +void QTextEdit::drawContents( QPainter *p, int cx, int cy, int cw, int ch ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + optimDrawContents( p, cx, cy, cw, ch ); + return; + } +#endif + paintDocument( TRUE, p, cx, cy, cw, ch ); + int v; + p->setPen( foregroundColor() ); + if ( document()->isPageBreakEnabled() && ( v = document()->flow()->pageSize() ) > 0 ) { + int l = int(cy / v) * v; + while ( l < cy + ch ) { + p->drawLine( cx, l, cx + cw - 1, l ); + l += v; + } + } + + // This invocation is required to follow dragging of active window + // by the showed candidate window. + updateMicroFocusHint(); +} + +/*! + \reimp +*/ + +void QTextEdit::drawContents( QPainter *p ) +{ + if ( horizontalScrollBar()->isVisible() && + verticalScrollBar()->isVisible() ) { + const QRect verticalRect = verticalScrollBar()->geometry(); + const QRect horizontalRect = horizontalScrollBar()->geometry(); + + QRect cornerRect; + cornerRect.setTop( verticalRect.bottom() ); + cornerRect.setBottom( horizontalRect.bottom() ); + cornerRect.setLeft( verticalRect.left() ); + cornerRect.setRight( verticalRect.right() ); + + p->fillRect( cornerRect, colorGroup().background() ); + } +} + +/*! + \reimp +*/ + +bool QTextEdit::event( QEvent *e ) +{ + if ( e->type() == QEvent::AccelOverride && !isReadOnly() ) { + QKeyEvent* ke = (QKeyEvent*) e; + switch(ke->state()) { + case NoButton: + case Keypad: + case ShiftButton: + if ( ke->key() < Key_Escape ) { + ke->accept(); + } else { + switch ( ke->key() ) { + case Key_Return: + case Key_Enter: + case Key_Delete: + case Key_Home: + case Key_End: + case Key_Backspace: + case Key_Left: + case Key_Right: + ke->accept(); + default: + break; + } + } + break; + + case ControlButton: + case ControlButton|ShiftButton: + case ControlButton|Keypad: + case ControlButton|ShiftButton|Keypad: + switch ( ke->key() ) { + case Key_Tab: + case Key_Backtab: + ke->ignore(); + break; +// Those are too frequently used for application functionality +/* case Key_A: + case Key_B: + case Key_D: + case Key_E: + case Key_F: + case Key_H: + case Key_I: + case Key_K: + case Key_N: + case Key_P: + case Key_T: +*/ + case Key_C: + case Key_V: + case Key_X: + case Key_Y: + case Key_Z: + case Key_Left: + case Key_Right: + case Key_Up: + case Key_Down: + case Key_Home: + case Key_End: +#if defined (Q_WS_WIN) + case Key_Insert: + case Key_Delete: +#endif + ke->accept(); + default: + break; + } + break; + + default: + switch ( ke->key() ) { +#if defined (Q_WS_WIN) + case Key_Insert: + ke->accept(); +#endif + default: + break; + } + break; + } + } + + if ( e->type() == QEvent::Show ) { + if ( +#ifdef QT_TEXTEDIT_OPTIMIZATION + !d->optimMode && +#endif + d->ensureCursorVisibleInShowEvent ) { + ensureCursorVisible(); + d->ensureCursorVisibleInShowEvent = FALSE; + } + if ( !d->scrollToAnchor.isEmpty() ) { + scrollToAnchor( d->scrollToAnchor ); + d->scrollToAnchor = QString::null; + } + } + return QWidget::event( e ); +} + +/*! + Processes the key event, \a e. By default key events are used to + provide keyboard navigation and text editing. +*/ + +void QTextEdit::keyPressEvent( QKeyEvent *e ) +{ + changeIntervalTimer->stop(); + interval = 10; + bool unknownKey = FALSE; + if ( isReadOnly() ) { + if ( !handleReadOnlyKeyEvent( e ) ) + QScrollView::keyPressEvent( e ); + changeIntervalTimer->start( 100, TRUE ); + return; + } + + + bool selChanged = FALSE; + for ( int i = 1; i < doc->numSelections(); ++i ) // start with 1 as we don't want to remove the Standard-Selection + selChanged = doc->removeSelection( i ) || selChanged; + + if ( selChanged ) { + cursor->paragraph()->document()->nextDoubleBuffered = TRUE; + repaintChanged(); + } + + bool clearUndoRedoInfo = TRUE; + + + switch ( e->key() ) { + case Key_Left: + case Key_Right: { + // a bit hacky, but can't change this without introducing new enum values for move and keeping the + // correct semantics and movement for BiDi and non BiDi text. + CursorAction a; + if ( cursor->paragraph()->string()->isRightToLeft() == (e->key() == Key_Right) ) + a = e->state() & ControlButton ? MoveWordBackward : MoveBackward; + else + a = e->state() & ControlButton ? MoveWordForward : MoveForward; + moveCursor( a, e->state() & ShiftButton ); + break; + } + case Key_Up: + moveCursor( e->state() & ControlButton ? MovePgUp : MoveUp, e->state() & ShiftButton ); + break; + case Key_Down: + moveCursor( e->state() & ControlButton ? MovePgDown : MoveDown, e->state() & ShiftButton ); + break; + case Key_Home: + moveCursor( e->state() & ControlButton ? MoveHome : MoveLineStart, e->state() & ShiftButton ); + break; + case Key_End: + moveCursor( e->state() & ControlButton ? MoveEnd : MoveLineEnd, e->state() & ShiftButton ); + break; + case Key_Prior: + moveCursor( MovePgUp, e->state() & ShiftButton ); + break; + case Key_Next: + moveCursor( MovePgDown, e->state() & ShiftButton ); + break; + case Key_Return: case Key_Enter: + if ( doc->hasSelection( QTextDocument::Standard, FALSE ) ) + removeSelectedText(); + if ( textFormat() == Qt::RichText && ( e->state() & ControlButton ) ) { + // Ctrl-Enter inserts a line break in rich text mode + insert( QString( QChar( 0x2028) ), TRUE, FALSE ); + } else { +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif + clearUndoRedoInfo = FALSE; + doKeyboardAction( ActionReturn ); + emit returnPressed(); + } + break; + case Key_Delete: +#if defined (Q_WS_WIN) + if ( e->state() & ShiftButton ) { + cut(); + break; + } else +#endif + if ( doc->hasSelection( QTextDocument::Standard, TRUE ) ) { + removeSelectedText(); + break; + } + doKeyboardAction( e->state() & ControlButton ? ActionWordDelete + : ActionDelete ); + clearUndoRedoInfo = FALSE; + + break; + case Key_Insert: + if ( e->state() & ShiftButton ) + paste(); +#if defined (Q_WS_WIN) + else if ( e->state() & ControlButton ) + copy(); +#endif + else + setOverwriteMode( !isOverwriteMode() ); + break; + case Key_Backspace: +#if defined (Q_WS_WIN) + if ( e->state() & AltButton ) { + if (e->state() & ControlButton ) { + break; + } else if ( e->state() & ShiftButton ) { + redo(); + break; + } else { + undo(); + break; + } + } else +#endif + if ( doc->hasSelection( QTextDocument::Standard, TRUE ) ) { + removeSelectedText(); + break; + } + + doKeyboardAction( e->state() & ControlButton ? ActionWordBackspace + : ActionBackspace ); + clearUndoRedoInfo = FALSE; + break; + case Key_F16: // Copy key on Sun keyboards + copy(); + break; + case Key_F18: // Paste key on Sun keyboards + paste(); + break; + case Key_F20: // Cut key on Sun keyboards + cut(); + break; + case Key_Direction_L: + if ( doc->textFormat() == Qt::PlainText ) { + // change the whole doc + QTextParagraph *p = doc->firstParagraph(); + while ( p ) { + p->setDirection( QChar::DirL ); + p->setAlignment( Qt::AlignLeft ); + p->invalidate( 0 ); + p = p->next(); + } + } else { + if ( !cursor->paragraph() || cursor->paragraph()->direction() == QChar::DirL ) + return; + cursor->paragraph()->setDirection( QChar::DirL ); + if ( cursor->paragraph()->length() <= 1&& + ( (cursor->paragraph()->alignment() & (Qt::AlignLeft | Qt::AlignRight) ) != 0 ) ) + setAlignment( Qt::AlignLeft ); + } + repaintChanged(); + break; + case Key_Direction_R: + if ( doc->textFormat() == Qt::PlainText ) { + // change the whole doc + QTextParagraph *p = doc->firstParagraph(); + while ( p ) { + p->setDirection( QChar::DirR ); + p->setAlignment( Qt::AlignRight ); + p->invalidate( 0 ); + p = p->next(); + } + } else { + if ( !cursor->paragraph() || cursor->paragraph()->direction() == QChar::DirR ) + return; + cursor->paragraph()->setDirection( QChar::DirR ); + if ( cursor->paragraph()->length() <= 1&& + ( (cursor->paragraph()->alignment() & (Qt::AlignLeft | Qt::AlignRight) ) != 0 ) ) + setAlignment( Qt::AlignRight ); + } + repaintChanged(); + break; + default: { + if ( e->text().length() && + ( !( e->state() & ControlButton ) && +#ifndef Q_OS_MACX + !( e->state() & AltButton ) && +#endif + !( e->state() & MetaButton ) || + ( ( (e->state()&ControlButton) | AltButton ) == (ControlButton|AltButton) ) ) && + ( !e->ascii() || e->ascii() >= 32 || e->text() == "\t" ) ) { + clearUndoRedoInfo = FALSE; + if ( e->key() == Key_Tab ) { + if ( d->tabChangesFocus ) { + e->ignore(); + break; + } + if ( textFormat() == Qt::RichText && cursor->index() == 0 + && ( cursor->paragraph()->isListItem() || cursor->paragraph()->listDepth() ) ) { + clearUndoRedo(); + undoRedoInfo.type = UndoRedoInfo::Style; + undoRedoInfo.id = cursor->paragraph()->paragId(); + undoRedoInfo.eid = undoRedoInfo.id; + undoRedoInfo.styleInformation = QTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid ); + cursor->paragraph()->setListDepth( cursor->paragraph()->listDepth() +1 ); + clearUndoRedo(); + drawCursor( FALSE ); + repaintChanged(); + drawCursor( TRUE ); + break; + } + } else if ( e->key() == Key_BackTab ) { + if ( d->tabChangesFocus ) { + e->ignore(); + break; + } + } + + if ( ( autoFormatting() & AutoBulletList ) && + textFormat() == Qt::RichText && cursor->index() == 0 + && !cursor->paragraph()->isListItem() + && ( e->text()[0] == '-' || e->text()[0] == '*' ) ) { + clearUndoRedo(); + undoRedoInfo.type = UndoRedoInfo::Style; + undoRedoInfo.id = cursor->paragraph()->paragId(); + undoRedoInfo.eid = undoRedoInfo.id; + undoRedoInfo.styleInformation = QTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid ); + setParagType( QStyleSheetItem::DisplayListItem, QStyleSheetItem::ListDisc ); + clearUndoRedo(); + drawCursor( FALSE ); + repaintChanged(); + drawCursor( TRUE ); + break; + } + if (overWrite && !cursor->atParagEnd() && !doc->hasSelection(QTextDocument::Standard)) { + doKeyboardAction(ActionDelete); + clearUndoRedoInfo = FALSE; + } + QString t = e->text(); +#ifdef Q_WS_X11 + extern bool qt_hebrew_keyboard_hack; + if ( qt_hebrew_keyboard_hack ) { + // the X11 keyboard layout is broken and does not reverse + // braces correctly. This is a hack to get halfway correct + // behaviour + QTextParagraph *p = cursor->paragraph(); + if ( p && p->string() && p->string()->isRightToLeft() ) { + QChar *c = (QChar *)t.unicode(); + int l = t.length(); + while( l-- ) { + if ( c->mirrored() ) + *c = c->mirroredChar(); + c++; + } + } + } +#endif + insert( t, TRUE, FALSE ); + break; + } else if ( e->state() & ControlButton ) { + switch ( e->key() ) { + case Key_C: case Key_F16: // Copy key on Sun keyboards + copy(); + break; + case Key_V: + paste(); + break; + case Key_X: + cut(); + break; + case Key_I: case Key_T: case Key_Tab: + if ( !d->tabChangesFocus ) + indent(); + break; + case Key_A: +#if defined(Q_WS_X11) + moveCursor( MoveLineStart, e->state() & ShiftButton ); +#else + selectAll( TRUE ); +#endif + break; + case Key_B: + moveCursor( MoveBackward, e->state() & ShiftButton ); + break; + case Key_F: + moveCursor( MoveForward, e->state() & ShiftButton ); + break; + case Key_D: + if ( doc->hasSelection( QTextDocument::Standard ) ) { + removeSelectedText(); + break; + } + doKeyboardAction( ActionDelete ); + clearUndoRedoInfo = FALSE; + break; + case Key_H: + if ( doc->hasSelection( QTextDocument::Standard ) ) { + removeSelectedText(); + break; + } + if ( !cursor->paragraph()->prev() && + cursor->atParagStart() ) + break; + + doKeyboardAction( ActionBackspace ); + clearUndoRedoInfo = FALSE; + break; + case Key_E: + moveCursor( MoveLineEnd, e->state() & ShiftButton ); + break; + case Key_N: + moveCursor( MoveDown, e->state() & ShiftButton ); + break; + case Key_P: + moveCursor( MoveUp, e->state() & ShiftButton ); + break; + case Key_Z: + if(e->state() & ShiftButton) + redo(); + else + undo(); + break; + case Key_Y: + redo(); + break; + case Key_K: + doKeyboardAction( ActionKill ); + break; +#if defined(Q_WS_WIN) + case Key_Insert: + copy(); + break; + case Key_Delete: + del(); + break; +#endif + default: + unknownKey = FALSE; + break; + } + } else { + unknownKey = TRUE; + } + } + } + + emit cursorPositionChanged( cursor ); + emit cursorPositionChanged( cursor->paragraph()->paragId(), cursor->index() ); + if ( clearUndoRedoInfo ) + clearUndoRedo(); + changeIntervalTimer->start( 100, TRUE ); + if ( unknownKey ) + e->ignore(); +} + +/*! + This function is not intended as polymorphic usage. Just a shared code + fragment that calls QWidget::sendMouseEventToInputContext() easily for this + class. + */ +bool QTextEdit::sendMouseEventToInputContext( QMouseEvent *e ) +{ +#ifndef QT_NO_IM + if ( d->composeMode() ) { + QTextCursor c( doc ); + if ( c.place( e->pos(), doc->firstParagraph(), FALSE, FALSE, FALSE ) ) { + int mousePos = c.index() - d->preeditStart; + if ( cursor->globalY() == c.globalY() && + mousePos >= 0 && mousePos < d->preeditLength ) { + QWidget::sendMouseEventToInputContext( mousePos, e->type(), + e->button(), e->state() ); + } + } else if ( e->type() != QEvent::MouseMove ) { + // send button events on out of preedit + QWidget::sendMouseEventToInputContext( -1, e->type(), + e->button(), e->state() ); + } + return TRUE; + } +#endif + return FALSE; +} + + +/*! + \reimp +*/ +void QTextEdit::imStartEvent( QIMEvent *e ) +{ + if ( isReadOnly() ) { + e->ignore(); + return; + } + + if ( hasSelectedText() ) + removeSelectedText(); + d->preeditStart = cursor->index(); + clearUndoRedo(); + undoRedoInfo.type = UndoRedoInfo::IME; +} + +/*! + \reimp +*/ +void QTextEdit::imComposeEvent( QIMEvent *e ) +{ + if ( isReadOnly() ) { + e->ignore(); + return; + } + + doc->removeSelection( QTextDocument::IMCompositionText ); + doc->removeSelection( QTextDocument::IMSelectionText ); + + if ( d->composeMode() && cursor->paragraph() ) + cursor->paragraph()->remove( d->preeditStart, d->preeditLength ); + cursor->setIndex( d->preeditStart ); + d->preeditLength = e->text().length(); + + int sellen = e->selectionLength(); + uint insertionFlags = CheckNewLines | RemoveSelected | AsIMCompositionText; + if ( sellen > 0 ) { + insertionFlags |= WithIMSelection; + } + insert( e->text(), insertionFlags ); + // insert can trigger an imEnd event as it emits a textChanged signal, so better + // be careful + if(d->preeditStart != -1) { + cursor->setIndex( d->preeditStart + d->preeditLength ); + QTextCursor c = *cursor; + cursor->setIndex( d->preeditStart ); + doc->setSelectionStart( QTextDocument::IMCompositionText, *cursor ); + doc->setSelectionEnd( QTextDocument::IMCompositionText, c ); + + cursor->setIndex( d->preeditStart + e->cursorPos() ); + + if ( sellen > 0 ) { + cursor->setIndex( d->preeditStart + e->cursorPos() + sellen ); + c = *cursor; + cursor->setIndex( d->preeditStart + e->cursorPos() ); + doc->setSelectionStart( QTextDocument::IMSelectionText, *cursor ); + doc->setSelectionEnd( QTextDocument::IMSelectionText, c ); +#if 0 + // Disabled for Asian input method that shows candidate + // window. This behavior is same as Qt/E 2.3.7 which supports + // Asian input methods. Asian input methods need start point + // of IM selection text to place candidate window as adjacent + // to the selection text. + cursor->setIndex( d->preeditStart + d->preeditLength ); +#endif + } + } + + updateMicroFocusHint(); + repaintChanged(); +} + +/*! + \reimp +*/ +void QTextEdit::imEndEvent( QIMEvent *e ) +{ + if ( isReadOnly() ) { + e->ignore(); + return; + } + + doc->removeSelection( QTextDocument::IMCompositionText ); + doc->removeSelection( QTextDocument::IMSelectionText ); + + if (undoRedoInfo.type == UndoRedoInfo::IME) + undoRedoInfo.type = UndoRedoInfo::Invalid; + + if ( d->composeMode() && cursor->paragraph() ) + cursor->paragraph()->remove( d->preeditStart, d->preeditLength ); + if ( d->preeditStart >= 0 ) { + cursor->setIndex( d->preeditStart ); + //TODO: Qt 4 we should use the new virtual insert function + insert( e->text(), FALSE ); + } + d->preeditStart = d->preeditLength = -1; + + repaintChanged(); +} + + +static bool qtextedit_ignore_readonly = FALSE; + +/*! + Executes keyboard action \a action. This is normally called by a + key event handler. +*/ + +void QTextEdit::doKeyboardAction( KeyboardAction action ) +{ + if ( isReadOnly() && !qtextedit_ignore_readonly ) + return; + + if ( cursor->nestedDepth() != 0 ) // #### for 3.0, disable editing of tables as this is not advanced enough + return; + + lastFormatted = cursor->paragraph(); + drawCursor( FALSE ); + bool doUpdateCurrentFormat = TRUE; + + switch ( action ) { + case ActionWordDelete: + case ActionDelete: + if ( action == ActionDelete && !cursor->atParagEnd() ) { + if ( undoEnabled ) { + checkUndoRedoInfo( UndoRedoInfo::Delete ); + if ( !undoRedoInfo.valid() ) { + undoRedoInfo.id = cursor->paragraph()->paragId(); + undoRedoInfo.index = cursor->index(); + undoRedoInfo.d->text = QString::null; + } + int idx = cursor->index(); + do { + undoRedoInfo.d->text.insert( undoRedoInfo.d->text.length(), cursor->paragraph()->at( idx++ ), TRUE ); + } while ( !cursor->paragraph()->string()->validCursorPosition( idx ) ); + } + cursor->remove(); + } else { + clearUndoRedo(); + doc->setSelectionStart( QTextDocument::Temp, *cursor ); + if ( action == ActionWordDelete && !cursor->atParagEnd() ) { + cursor->gotoNextWord(); + } else { + cursor->gotoNextLetter(); + } + doc->setSelectionEnd( QTextDocument::Temp, *cursor ); + removeSelectedText( QTextDocument::Temp ); + } + break; + case ActionWordBackspace: + case ActionBackspace: + if ( textFormat() == Qt::RichText + && (cursor->paragraph()->isListItem() + || cursor->paragraph()->listDepth() ) + && cursor->index() == 0 ) { + if ( undoEnabled ) { + clearUndoRedo(); + undoRedoInfo.type = UndoRedoInfo::Style; + undoRedoInfo.id = cursor->paragraph()->paragId(); + undoRedoInfo.eid = undoRedoInfo.id; + undoRedoInfo.styleInformation = QTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid ); + } + int ldepth = cursor->paragraph()->listDepth(); + if ( cursor->paragraph()->isListItem() && ldepth == 1 ) { + cursor->paragraph()->setListItem( FALSE ); + } else if ( QMAX( ldepth, 1 ) == 1 ) { + cursor->paragraph()->setListItem( FALSE ); + cursor->paragraph()->setListDepth( 0 ); + } else { + cursor->paragraph()->setListDepth( ldepth - 1 ); + } + clearUndoRedo(); + lastFormatted = cursor->paragraph(); + repaintChanged(); + drawCursor( TRUE ); + return; + } + + if ( action == ActionBackspace && !cursor->atParagStart() ) { + if ( undoEnabled ) { + checkUndoRedoInfo( UndoRedoInfo::Delete ); + if ( !undoRedoInfo.valid() ) { + undoRedoInfo.id = cursor->paragraph()->paragId(); + undoRedoInfo.index = cursor->index(); + undoRedoInfo.d->text = QString::null; + } + undoRedoInfo.d->text.insert( 0, cursor->paragraph()->at( cursor->index()-1 ), TRUE ); + undoRedoInfo.index = cursor->index()-1; + } + cursor->removePreviousChar(); + lastFormatted = cursor->paragraph(); + } else if ( cursor->paragraph()->prev() + || (action == ActionWordBackspace + && !cursor->atParagStart()) ) { + clearUndoRedo(); + doc->setSelectionStart( QTextDocument::Temp, *cursor ); + if ( action == ActionWordBackspace && !cursor->atParagStart() ) { + cursor->gotoPreviousWord(); + } else { + cursor->gotoPreviousLetter(); + } + doc->setSelectionEnd( QTextDocument::Temp, *cursor ); + removeSelectedText( QTextDocument::Temp ); + } + break; + case ActionReturn: + if ( undoEnabled ) { + checkUndoRedoInfo( UndoRedoInfo::Return ); + if ( !undoRedoInfo.valid() ) { + undoRedoInfo.id = cursor->paragraph()->paragId(); + undoRedoInfo.index = cursor->index(); + undoRedoInfo.d->text = QString::null; + } + undoRedoInfo.d->text += "\n"; + } + cursor->splitAndInsertEmptyParagraph(); + if ( cursor->paragraph()->prev() ) { + lastFormatted = cursor->paragraph()->prev(); + lastFormatted->invalidate( 0 ); + } + doUpdateCurrentFormat = FALSE; + break; + case ActionKill: + clearUndoRedo(); + doc->setSelectionStart( QTextDocument::Temp, *cursor ); + if ( cursor->atParagEnd() ) + cursor->gotoNextLetter(); + else + cursor->setIndex( cursor->paragraph()->length() - 1 ); + doc->setSelectionEnd( QTextDocument::Temp, *cursor ); + removeSelectedText( QTextDocument::Temp ); + break; + } + + formatMore(); + repaintChanged(); + ensureCursorVisible(); + drawCursor( TRUE ); + updateMicroFocusHint(); + if ( doUpdateCurrentFormat ) + updateCurrentFormat(); + setModified(); + emit textChanged(); +} + +void QTextEdit::readFormats( QTextCursor &c1, QTextCursor &c2, QTextString &text, bool fillStyles ) +{ +#ifndef QT_NO_DATASTREAM + QDataStream styleStream( undoRedoInfo.styleInformation, IO_WriteOnly ); +#endif + c2.restoreState(); + c1.restoreState(); + int lastIndex = text.length(); + if ( c1.paragraph() == c2.paragraph() ) { + for ( int i = c1.index(); i < c2.index(); ++i ) + text.insert( lastIndex + i - c1.index(), c1.paragraph()->at( i ), TRUE ); +#ifndef QT_NO_DATASTREAM + if ( fillStyles ) { + styleStream << (int) 1; + c1.paragraph()->writeStyleInformation( styleStream ); + } +#endif + } else { + int i; + for ( i = c1.index(); i < c1.paragraph()->length()-1; ++i ) + text.insert( lastIndex++, c1.paragraph()->at( i ), TRUE ); + int num = 2; // start and end, being different + text += "\n"; lastIndex++; + + if (c1.paragraph()->next() != c2.paragraph()) { + num += text.appendParagraphs(c1.paragraph()->next(), c2.paragraph()); + lastIndex = text.length(); + } + + for ( i = 0; i < c2.index(); ++i ) + text.insert( i + lastIndex, c2.paragraph()->at( i ), TRUE ); +#ifndef QT_NO_DATASTREAM + if ( fillStyles ) { + styleStream << num; + for ( QTextParagraph *p = c1.paragraph(); --num >= 0; p = p->next() ) + p->writeStyleInformation( styleStream ); + } +#endif + } +} + +/*! + Removes the selection \a selNum (by default 0). This does not + remove the selected text. + + \sa removeSelectedText() +*/ + +void QTextEdit::removeSelection( int selNum ) +{ + doc->removeSelection( selNum ); + repaintChanged(); +} + +/*! + Deletes the text of selection \a selNum (by default, the default + selection, 0). If there is no selected text nothing happens. + + \sa selectedText removeSelection() +*/ + +void QTextEdit::removeSelectedText( int selNum ) +{ + if(selNum != 0) + resetInputContext(); + + QTextCursor c1 = doc->selectionStartCursor( selNum ); + c1.restoreState(); + QTextCursor c2 = doc->selectionEndCursor( selNum ); + c2.restoreState(); + + // ### no support for editing tables yet, plus security for broken selections + if ( c1.nestedDepth() || c2.nestedDepth() ) + return; + + for ( int i = 0; i < (int)doc->numSelections(); ++i ) { + if ( i == selNum ) + continue; + doc->removeSelection( i ); + } + + drawCursor( FALSE ); + if ( undoEnabled ) { + checkUndoRedoInfo( UndoRedoInfo::RemoveSelected ); + if ( !undoRedoInfo.valid() ) { + doc->selectionStart( selNum, undoRedoInfo.id, undoRedoInfo.index ); + undoRedoInfo.d->text = QString::null; + } + readFormats( c1, c2, undoRedoInfo.d->text, TRUE ); + } + + doc->removeSelectedText( selNum, cursor ); + if ( cursor->isValid() ) { + lastFormatted = 0; // make sync a noop + ensureCursorVisible(); + lastFormatted = cursor->paragraph(); + formatMore(); + repaintContents( FALSE ); + ensureCursorVisible(); + drawCursor( TRUE ); + clearUndoRedo(); +#if defined(Q_WS_WIN) + // there seems to be a problem with repainting or erasing the area + // of the scrollview which is not the contents on windows + if ( contentsHeight() < visibleHeight() ) + viewport()->repaint( 0, contentsHeight(), visibleWidth(), visibleHeight() - contentsHeight(), TRUE ); +#endif +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif + updateMicroFocusHint(); + } else { + delete cursor; + cursor = new QTextCursor( doc ); + drawCursor( TRUE ); + repaintContents( TRUE ); + } + setModified(); + emit textChanged(); + emit selectionChanged(); + emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) ); +} + +/*! + Moves the text cursor according to \a action. This is normally + used by some key event handler. \a select specifies whether the + text between the current cursor position and the new position + should be selected. +*/ + +void QTextEdit::moveCursor( CursorAction action, bool select ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return; +#endif +#ifdef Q_WS_MACX + QTextCursor c1 = *cursor; + QTextCursor c2; +#endif + drawCursor( FALSE ); + if ( select ) { + if ( !doc->hasSelection( QTextDocument::Standard ) ) + doc->setSelectionStart( QTextDocument::Standard, *cursor ); + moveCursor( action ); +#ifdef Q_WS_MACX + c2 = *cursor; + if (c1 == c2) + if (action == MoveDown || action == MovePgDown) + moveCursor( MoveEnd ); + else if (action == MoveUp || action == MovePgUp) + moveCursor( MoveHome ); +#endif + if ( doc->setSelectionEnd( QTextDocument::Standard, *cursor ) ) { + cursor->paragraph()->document()->nextDoubleBuffered = TRUE; + repaintChanged(); + } else { + drawCursor( TRUE ); + } + ensureCursorVisible(); + emit selectionChanged(); + emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) ); + } else { +#ifdef Q_WS_MACX + QTextCursor cStart = doc->selectionStartCursor( QTextDocument::Standard ); + QTextCursor cEnd = doc->selectionEndCursor( QTextDocument::Standard ); + bool redraw = doc->removeSelection( QTextDocument::Standard ); + if (redraw && action == MoveDown) + *cursor = cEnd; + else if (redraw && action == MoveUp) + *cursor = cStart; + if (redraw && action == MoveForward) + *cursor = cEnd; + else if (redraw && action == MoveBackward) + *cursor = cStart; + else + moveCursor( action ); + c2 = *cursor; + if (c1 == c2) + if (action == MoveDown) + moveCursor( MoveEnd ); + else if (action == MoveUp) + moveCursor( MoveHome ); +#else + bool redraw = doc->removeSelection( QTextDocument::Standard ); + moveCursor( action ); +#endif + if ( !redraw ) { + ensureCursorVisible(); + drawCursor( TRUE ); + } else { + cursor->paragraph()->document()->nextDoubleBuffered = TRUE; + repaintChanged(); + ensureCursorVisible(); + drawCursor( TRUE ); +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif + } + if ( redraw ) { + emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) ); + emit selectionChanged(); + } + } + + drawCursor( TRUE ); + updateCurrentFormat(); + updateMicroFocusHint(); +} + +/*! + \overload +*/ + +void QTextEdit::moveCursor( CursorAction action ) +{ + resetInputContext(); + switch ( action ) { + case MoveBackward: + cursor->gotoPreviousLetter(); + break; + case MoveWordBackward: + cursor->gotoPreviousWord(); + break; + case MoveForward: + cursor->gotoNextLetter(); + break; + case MoveWordForward: + cursor->gotoNextWord(); + break; + case MoveUp: + cursor->gotoUp(); + break; + case MovePgUp: + cursor->gotoPageUp( visibleHeight() ); + break; + case MoveDown: + cursor->gotoDown(); + break; + case MovePgDown: + cursor->gotoPageDown( visibleHeight() ); + break; + case MoveLineStart: + cursor->gotoLineStart(); + break; + case MoveHome: + cursor->gotoHome(); + break; + case MoveLineEnd: + cursor->gotoLineEnd(); + break; + case MoveEnd: + ensureFormatted( doc->lastParagraph() ); + cursor->gotoEnd(); + break; + } + updateMicroFocusHint(); + updateCurrentFormat(); +} + +/*! + \reimp +*/ + +void QTextEdit::resizeEvent( QResizeEvent *e ) +{ + QScrollView::resizeEvent( e ); + if ( doc->visibleWidth() == 0 ) + doResize(); +} + +/*! + \reimp +*/ + +void QTextEdit::viewportResizeEvent( QResizeEvent *e ) +{ + QScrollView::viewportResizeEvent( e ); + if ( e->oldSize().width() != e->size().width() ) { + bool stayAtBottom = e->oldSize().height() != e->size().height() && + contentsY() > 0 && contentsY() >= doc->height() - e->oldSize().height(); + doResize(); + if ( stayAtBottom ) + scrollToBottom(); + } +} + +/*! + Ensures that the cursor is visible by scrolling the text edit if + necessary. + + \sa setCursorPosition() +*/ + +void QTextEdit::ensureCursorVisible() +{ + // Not visible or the user is draging the window, so don't position to caret yet + if ( !isUpdatesEnabled() || !isVisible() || isHorizontalSliderPressed() || isVerticalSliderPressed() ) { + d->ensureCursorVisibleInShowEvent = TRUE; + return; + } + sync(); + QTextStringChar *chr = cursor->paragraph()->at( cursor->index() ); + int h = cursor->paragraph()->lineHeightOfChar( cursor->index() ); + int x = cursor->paragraph()->rect().x() + chr->x + cursor->offsetX(); + int y = 0; int dummy; + cursor->paragraph()->lineHeightOfChar( cursor->index(), &dummy, &y ); + y += cursor->paragraph()->rect().y() + cursor->offsetY(); + int w = 1; + ensureVisible( x, y + h / 2, w, h / 2 + 2 ); +} + +/*! + \internal +*/ +void QTextEdit::sliderReleased() +{ + if ( d->ensureCursorVisibleInShowEvent && isVisible() ) { + d->ensureCursorVisibleInShowEvent = FALSE; + ensureCursorVisible(); + } +} + +/*! + \internal +*/ +void QTextEdit::drawCursor( bool visible ) +{ + if ( !isUpdatesEnabled() || + !viewport()->isUpdatesEnabled() || + !cursor->paragraph() || + !cursor->paragraph()->isValid() || + ( !style().styleHint( QStyle::SH_BlinkCursorWhenTextSelected ) && + ( d->optimMode ? optimHasSelection() : doc->hasSelection( QTextDocument::Standard, TRUE ))) || + ( visible && !hasFocus() && !viewport()->hasFocus() && !inDnD ) || + doc->hasSelection( QTextDocument::IMSelectionText ) || + isReadOnly() ) + return; + + // Asian users regard selection text as cursor on candidate + // selection phase of input method, so ordinary cursor should be + // invisible if IM selection text exists. + if ( doc->hasSelection( QTextDocument::IMSelectionText ) ) { + visible = FALSE; + } + + QPainter p( viewport() ); + QRect r( cursor->topParagraph()->rect() ); + cursor->paragraph()->setChanged( TRUE ); + p.translate( -contentsX() + cursor->totalOffsetX(), -contentsY() + cursor->totalOffsetY() ); + QPixmap *pix = 0; + QColorGroup cg( colorGroup() ); + const QColorGroup::ColorRole backRole = QPalette::backgroundRoleFromMode(backgroundMode()); + if ( cursor->paragraph()->background() ) + cg.setBrush( backRole, *cursor->paragraph()->background() ); + else if ( doc->paper() ) + cg.setBrush( backRole, *doc->paper() ); + p.setBrushOrigin( -contentsX(), -contentsY() ); + cursor->paragraph()->document()->nextDoubleBuffered = TRUE; + if ( !cursor->nestedDepth() ) { + int h = cursor->paragraph()->lineHeightOfChar( cursor->index() ); + int dist = 5; + if ( ( cursor->paragraph()->alignment() & Qt::AlignJustify ) == Qt::AlignJustify ) + dist = 50; + int x = r.x() - cursor->totalOffsetX() + cursor->x() - dist; + x = QMAX( x, 0 ); + p.setClipRect( QRect( x - contentsX(), + r.y() - cursor->totalOffsetY() + cursor->y() - contentsY(), 2 * dist, h ) ); + doc->drawParagraph( &p, cursor->paragraph(), x, + r.y() - cursor->totalOffsetY() + cursor->y(), 2 * dist, h, pix, cg, visible, cursor ); + } else { + doc->drawParagraph( &p, cursor->paragraph(), r.x() - cursor->totalOffsetX(), + r.y() - cursor->totalOffsetY(), r.width(), r.height(), + pix, cg, visible, cursor ); + } + cursorVisible = visible; +} + +enum { + IdUndo = 0, + IdRedo = 1, + IdCut = 2, + IdCopy = 3, + IdPaste = 4, + IdClear = 5, + IdSelectAll = 6 +}; + +/*! + \reimp +*/ +#ifndef QT_NO_WHEELEVENT +void QTextEdit::contentsWheelEvent( QWheelEvent *e ) +{ + if ( isReadOnly() ) { + if ( e->state() & ControlButton ) { + if ( e->delta() > 0 ) + zoomOut(); + else if ( e->delta() < 0 ) + zoomIn(); + return; + } + } + QScrollView::contentsWheelEvent( e ); +} +#endif + +/*! + \reimp +*/ + +void QTextEdit::contentsMousePressEvent( QMouseEvent *e ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + optimMousePressEvent( e ); + return; + } +#endif + + if ( sendMouseEventToInputContext( e ) ) + return; + + if ( d->trippleClickTimer->isActive() && + ( e->globalPos() - d->trippleClickPoint ).manhattanLength() < + QApplication::startDragDistance() ) { + QTextCursor c1 = *cursor; + QTextCursor c2 = *cursor; + c1.gotoLineStart(); + c2.gotoLineEnd(); + doc->setSelectionStart( QTextDocument::Standard, c1 ); + doc->setSelectionEnd( QTextDocument::Standard, c2 ); + *cursor = c2; + repaintChanged(); + mousePressed = TRUE; + return; + } + + clearUndoRedo(); + QTextCursor oldCursor = *cursor; + QTextCursor c = *cursor; + mousePos = e->pos(); + mightStartDrag = FALSE; + pressedLink = QString::null; + d->pressedName = QString::null; + + if ( e->button() == LeftButton ) { + mousePressed = TRUE; + drawCursor( FALSE ); + placeCursor( e->pos() ); + ensureCursorVisible(); + + if ( isReadOnly() && linksEnabled() ) { + QTextCursor c = *cursor; + placeCursor( e->pos(), &c, TRUE ); + if ( c.paragraph() && c.paragraph()->at( c.index() ) && + c.paragraph()->at( c.index() )->isAnchor() ) { + pressedLink = c.paragraph()->at( c.index() )->anchorHref(); + d->pressedName = c.paragraph()->at( c.index() )->anchorName(); + } + } + +#ifndef QT_NO_DRAGANDDROP + if ( doc->inSelection( QTextDocument::Standard, e->pos() ) ) { + mightStartDrag = TRUE; + drawCursor( TRUE ); + dragStartTimer->start( QApplication::startDragTime(), TRUE ); + dragStartPos = e->pos(); + return; + } +#endif + + bool redraw = FALSE; + if ( doc->hasSelection( QTextDocument::Standard ) ) { + if ( !( e->state() & ShiftButton ) ) { + redraw = doc->removeSelection( QTextDocument::Standard ); + doc->setSelectionStart( QTextDocument::Standard, *cursor ); + } else { + redraw = doc->setSelectionEnd( QTextDocument::Standard, *cursor ) || redraw; + } + } else { + if ( isReadOnly() || !( e->state() & ShiftButton ) ) { + doc->setSelectionStart( QTextDocument::Standard, *cursor ); + } else { + doc->setSelectionStart( QTextDocument::Standard, c ); + redraw = doc->setSelectionEnd( QTextDocument::Standard, *cursor ) || redraw; + } + } + + for ( int i = 1; i < doc->numSelections(); ++i ) // start with 1 as we don't want to remove the Standard-Selection + redraw = doc->removeSelection( i ) || redraw; + + if ( !redraw ) { + drawCursor( TRUE ); + } else { + repaintChanged(); +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif + } + } else if ( e->button() == MidButton ) { + bool redraw = doc->removeSelection( QTextDocument::Standard ); + if ( !redraw ) { + drawCursor( TRUE ); + } else { + repaintChanged(); +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif + } + } + + if ( *cursor != oldCursor ) + updateCurrentFormat(); +} + +/*! + \reimp +*/ + +void QTextEdit::contentsMouseMoveEvent( QMouseEvent *e ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + optimMouseMoveEvent( e ); + return; + } +#endif + if ( sendMouseEventToInputContext( e ) ) { + // don't return from here to avoid cursor vanishing + } else if ( mousePressed ) { +#ifndef QT_NO_DRAGANDDROP + if ( mightStartDrag ) { + dragStartTimer->stop(); + if ( ( e->pos() - dragStartPos ).manhattanLength() > QApplication::startDragDistance() ) { + QGuardedPtr<QTextEdit> guard( this ); + startDrag(); + if (guard.isNull()) // we got deleted during the dnd + return; + } +#ifndef QT_NO_CURSOR + if ( !isReadOnly() ) + viewport()->setCursor( ibeamCursor ); +#endif + return; + } +#endif + mousePos = e->pos(); + handleMouseMove( mousePos ); + oldMousePos = mousePos; + } + +#ifndef QT_NO_CURSOR + if ( !isReadOnly() && !mousePressed ) { + if ( doc->hasSelection( QTextDocument::Standard ) && doc->inSelection( QTextDocument::Standard, e->pos() ) ) + viewport()->setCursor( arrowCursor ); + else + viewport()->setCursor( ibeamCursor ); + } +#endif + updateCursor( e->pos() ); +} + +void QTextEdit::copyToClipboard() +{ +#ifndef QT_NO_CLIPBOARD + if (QApplication::clipboard()->supportsSelection()) { + d->clipboard_mode = QClipboard::Selection; + + // don't listen to selection changes + disconnect( QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0); + copy(); + // listen to selection changes + connect( QApplication::clipboard(), SIGNAL(selectionChanged()), + this, SLOT(clipboardChanged()) ); + + d->clipboard_mode = QClipboard::Clipboard; + } +#endif +} + +/*! + \reimp +*/ + +void QTextEdit::contentsMouseReleaseEvent( QMouseEvent * e ) +{ + if ( !inDoubleClick && !d->composeMode() ) { // could be the release of a dblclick + int para = 0; + int index = charAt( e->pos(), ¶ ); + emit clicked( para, index ); + } +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + optimMouseReleaseEvent( e ); + return; + } +#endif + if ( sendMouseEventToInputContext( e ) ) + return; + QTextCursor oldCursor = *cursor; + if ( scrollTimer->isActive() ) + scrollTimer->stop(); +#ifndef QT_NO_DRAGANDDROP + if ( dragStartTimer->isActive() ) + dragStartTimer->stop(); + if ( mightStartDrag ) { + selectAll( FALSE ); + mousePressed = FALSE; + } +#endif + bool mouseWasPressed = mousePressed; + if ( mousePressed ) { + mousePressed = FALSE; + copyToClipboard(); + } +#ifndef QT_NO_CLIPBOARD + else if ( e->button() == MidButton && !isReadOnly() ) { + // only do middle-click pasting on systems that have selections (ie. X11) + if (QApplication::clipboard()->supportsSelection()) { + drawCursor( FALSE ); + placeCursor( e->pos() ); + ensureCursorVisible(); + doc->setSelectionStart( QTextDocument::Standard, oldCursor ); + bool redraw = FALSE; + if ( doc->hasSelection( QTextDocument::Standard ) ) { + redraw = doc->removeSelection( QTextDocument::Standard ); + doc->setSelectionStart( QTextDocument::Standard, *cursor ); + } else { + doc->setSelectionStart( QTextDocument::Standard, *cursor ); + } + // start with 1 as we don't want to remove the Standard-Selection + for ( int i = 1; i < doc->numSelections(); ++i ) + redraw = doc->removeSelection( i ) || redraw; + if ( !redraw ) { + drawCursor( TRUE ); + } else { + repaintChanged(); +#ifndef QT_NO_CURSOR + viewport()->setCursor( ibeamCursor ); +#endif + } + d->clipboard_mode = QClipboard::Selection; + paste(); + d->clipboard_mode = QClipboard::Clipboard; + } + } +#endif + emit cursorPositionChanged( cursor ); + emit cursorPositionChanged( cursor->paragraph()->paragId(), cursor->index() ); + if ( oldCursor != *cursor ) + updateCurrentFormat(); + inDoubleClick = FALSE; + +#ifndef QT_NO_NETWORKPROTOCOL + if ( ( (!onLink.isEmpty() && onLink == pressedLink) + || (!d->onName.isEmpty() && d->onName == d->pressedName)) + && linksEnabled() && mouseWasPressed ) { + if (!onLink.isEmpty()) { + QUrl u( doc->context(), onLink, TRUE ); + emitLinkClicked( u.toString( FALSE, FALSE ) ); + } + if (::qt_cast<QTextBrowser*>(this)) { // change for 4.0 + QConnectionList *clist = receivers( + "anchorClicked(const QString&,const QString&)"); + if (!signalsBlocked() && clist) { + QUObject o[3]; + static_QUType_QString.set(o+1, d->onName); + static_QUType_QString.set(o+2, onLink); + activate_signal( clist, o); + } + } + + // emitting linkClicked() may result in that the cursor winds + // up hovering over a different valid link - check this and + // set the appropriate cursor shape + updateCursor( e->pos() ); + } +#endif + drawCursor( TRUE ); + if ( !doc->hasSelection( QTextDocument::Standard, TRUE ) ) + doc->removeSelection( QTextDocument::Standard ); + + emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) ); + emit selectionChanged(); +} + +/*! + \reimp +*/ + +void QTextEdit::contentsMouseDoubleClickEvent( QMouseEvent * e ) +{ + if ( e->button() != Qt::LeftButton && !d->composeMode() ) { + e->ignore(); + return; + } + int para = 0; + int index = charAt( e->pos(), ¶ ); +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + QString str = d->od->lines[ LOGOFFSET(para) ]; + int startIdx = index, endIdx = index, i; + if ( !str[ index ].isSpace() ) { + i = startIdx; + // find start of word + while ( i >= 0 && !str[ i ].isSpace() ) { + startIdx = i--; + } + i = endIdx; + // find end of word.. + while ( (uint) i < str.length() && !str[ i ].isSpace() ) { + endIdx = ++i; + } + // ..and start of next + while ( (uint) i < str.length() && str[ i ].isSpace() ) { + endIdx = ++i; + } + optimSetSelection( para, startIdx, para, endIdx ); + repaintContents( FALSE ); + } + } else +#endif + { + if ( sendMouseEventToInputContext( e ) ) + return; + + QTextCursor c1 = *cursor; + QTextCursor c2 = *cursor; +#if defined(Q_OS_MAC) + QTextParagraph *para = cursor->paragraph(); + if ( cursor->isValid() ) { + if ( para->at( cursor->index() )->c.isLetterOrNumber() ) { + while ( c1.index() > 0 && + c1.paragraph()->at( c1.index()-1 )->c.isLetterOrNumber() ) + c1.gotoPreviousLetter(); + while ( c2.paragraph()->at( c2.index() )->c.isLetterOrNumber() && + !c2.atParagEnd() ) + c2.gotoNextLetter(); + } else if ( para->at( cursor->index() )->c.isSpace() ) { + while ( c1.index() > 0 && + c1.paragraph()->at( c1.index()-1 )->c.isSpace() ) + c1.gotoPreviousLetter(); + while ( c2.paragraph()->at( c2.index() )->c.isSpace() && + !c2.atParagEnd() ) + c2.gotoNextLetter(); + } else if ( !c2.atParagEnd() ) { + c2.gotoNextLetter(); + } + } +#else + if ( cursor->index() > 0 && !cursor->paragraph()->at( cursor->index()-1 )->c.isSpace() ) + c1.gotoPreviousWord(); + if ( !cursor->paragraph()->at( cursor->index() )->c.isSpace() && !cursor->atParagEnd() ) + c2.gotoNextWord(); +#endif + doc->setSelectionStart( QTextDocument::Standard, c1 ); + doc->setSelectionEnd( QTextDocument::Standard, c2 ); + + *cursor = c2; + + repaintChanged(); + + d->trippleClickTimer->start( qApp->doubleClickInterval(), TRUE ); + d->trippleClickPoint = e->globalPos(); + } + inDoubleClick = TRUE; + mousePressed = TRUE; + emit doubleClicked( para, index ); +} + +#ifndef QT_NO_DRAGANDDROP + +/*! + \reimp +*/ + +void QTextEdit::contentsDragEnterEvent( QDragEnterEvent *e ) +{ + if ( isReadOnly() || !QTextDrag::canDecode( e ) ) { + e->ignore(); + return; + } + e->acceptAction(); + inDnD = TRUE; +} + +/*! + \reimp +*/ + +void QTextEdit::contentsDragMoveEvent( QDragMoveEvent *e ) +{ + if ( isReadOnly() || !QTextDrag::canDecode( e ) ) { + e->ignore(); + return; + } + drawCursor( FALSE ); + placeCursor( e->pos(), cursor ); + drawCursor( TRUE ); + e->acceptAction(); +} + +/*! + \reimp +*/ + +void QTextEdit::contentsDragLeaveEvent( QDragLeaveEvent * ) +{ + drawCursor( FALSE ); + inDnD = FALSE; +} + +/*! + \reimp +*/ + +void QTextEdit::contentsDropEvent( QDropEvent *e ) +{ + if ( isReadOnly() ) + return; + inDnD = FALSE; + e->acceptAction(); + bool intern = FALSE; + if ( QRichTextDrag::canDecode( e ) ) { + bool hasSel = doc->hasSelection( QTextDocument::Standard ); + bool internalDrag = e->source() == this || e->source() == viewport(); + int dropId, dropIndex; + QTextCursor insertCursor = *cursor; + dropId = cursor->paragraph()->paragId(); + dropIndex = cursor->index(); + if ( hasSel && internalDrag ) { + QTextCursor c1, c2; + int selStartId, selStartIndex; + int selEndId, selEndIndex; + c1 = doc->selectionStartCursor( QTextDocument::Standard ); + c1.restoreState(); + c2 = doc->selectionEndCursor( QTextDocument::Standard ); + c2.restoreState(); + selStartId = c1.paragraph()->paragId(); + selStartIndex = c1.index(); + selEndId = c2.paragraph()->paragId(); + selEndIndex = c2.index(); + if ( ( ( dropId > selStartId ) || + ( dropId == selStartId && dropIndex > selStartIndex ) ) && + ( ( dropId < selEndId ) || + ( dropId == selEndId && dropIndex <= selEndIndex ) ) ) + insertCursor = c1; + if ( dropId == selEndId && dropIndex > selEndIndex ) { + insertCursor = c1; + if ( selStartId == selEndId ) { + insertCursor.setIndex( dropIndex - + ( selEndIndex - selStartIndex ) ); + } else { + insertCursor.setIndex( dropIndex - selEndIndex + + selStartIndex ); + } + } + } + + if ( internalDrag && e->action() == QDropEvent::Move ) { + removeSelectedText(); + intern = TRUE; + doc->removeSelection( QTextDocument::Standard ); + } else { + doc->removeSelection( QTextDocument::Standard ); +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif + } + drawCursor( FALSE ); + cursor->setParagraph( insertCursor.paragraph() ); + cursor->setIndex( insertCursor.index() ); + drawCursor( TRUE ); + if ( !cursor->nestedDepth() ) { + QString subType = "plain"; + if ( textFormat() != PlainText ) { + if ( e->provides( "application/x-qrichtext" ) ) + subType = "x-qrichtext"; + } +#ifndef QT_NO_CLIPBOARD + pasteSubType( subType.latin1(), e ); +#endif + // emit appropriate signals. + emit selectionChanged(); + emit cursorPositionChanged( cursor ); + emit cursorPositionChanged( cursor->paragraph()->paragId(), cursor->index() ); + } else { + if ( intern ) + undo(); + e->ignore(); + } + } +} + +#endif + +/*! + \reimp +*/ +void QTextEdit::contentsContextMenuEvent( QContextMenuEvent *e ) +{ + e->accept(); +#ifndef QT_NO_IM + if ( d->composeMode() ) + return; +#endif + + clearUndoRedo(); + mousePressed = FALSE; + +#ifndef QT_NO_POPUPMENU + QGuardedPtr<QTextEdit> that = this; + QGuardedPtr<QPopupMenu> popup = createPopupMenu( e->pos() ); + if ( !popup ) + popup = createPopupMenu(); + if ( !popup ) + return; + + int r = popup->exec( e->globalPos() ); + delete popup; + if (!that) + return; + + if ( r == d->id[ IdClear ] ) + clear(); + else if ( r == d->id[ IdSelectAll ] ) { + selectAll(); +#ifndef QT_NO_CLIPBOARD + // if the clipboard support selections, put the newly selected text into + // the clipboard + if (QApplication::clipboard()->supportsSelection()) { + d->clipboard_mode = QClipboard::Selection; + + // don't listen to selection changes + disconnect( QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0); + copy(); + // listen to selection changes + connect( QApplication::clipboard(), SIGNAL(selectionChanged()), + this, SLOT(clipboardChanged()) ); + + d->clipboard_mode = QClipboard::Clipboard; + } +#endif + } else if ( r == d->id[ IdUndo ] ) + undo(); + else if ( r == d->id[ IdRedo ] ) + redo(); +#ifndef QT_NO_CLIPBOARD + else if ( r == d->id[ IdCut ] ) + cut(); + else if ( r == d->id[ IdCopy ] ) + copy(); + else if ( r == d->id[ IdPaste ] ) + paste(); +#endif +#endif +} + + +void QTextEdit::autoScrollTimerDone() +{ + if ( mousePressed ) + handleMouseMove( viewportToContents( viewport()->mapFromGlobal( QCursor::pos() ) ) ); +} + +void QTextEdit::handleMouseMove( const QPoint& pos ) +{ + if ( !mousePressed ) + return; + + if ( !scrollTimer->isActive() && pos.y() < contentsY() || pos.y() > contentsY() + visibleHeight() ) + scrollTimer->start( 100, FALSE ); + else if ( scrollTimer->isActive() && pos.y() >= contentsY() && pos.y() <= contentsY() + visibleHeight() ) + scrollTimer->stop(); + + drawCursor( FALSE ); + QTextCursor oldCursor = *cursor; + + placeCursor( pos ); + + if ( inDoubleClick ) { + QTextCursor cl = *cursor; + cl.gotoPreviousWord(); + QTextCursor cr = *cursor; + cr.gotoNextWord(); + + int diff = QABS( oldCursor.paragraph()->at( oldCursor.index() )->x - mousePos.x() ); + int ldiff = QABS( cl.paragraph()->at( cl.index() )->x - mousePos.x() ); + int rdiff = QABS( cr.paragraph()->at( cr.index() )->x - mousePos.x() ); + + + if ( cursor->paragraph()->lineStartOfChar( cursor->index() ) != + oldCursor.paragraph()->lineStartOfChar( oldCursor.index() ) ) + diff = 0xFFFFFF; + + if ( rdiff < diff && rdiff < ldiff ) + *cursor = cr; + else if ( ldiff < diff && ldiff < rdiff ) + *cursor = cl; + else + *cursor = oldCursor; + + } + ensureCursorVisible(); + + bool redraw = FALSE; + if ( doc->hasSelection( QTextDocument::Standard ) ) { + redraw = doc->setSelectionEnd( QTextDocument::Standard, *cursor ) || redraw; + } + + if ( !redraw ) { + drawCursor( TRUE ); + } else { + repaintChanged(); + drawCursor( TRUE ); + } + + if ( currentFormat && currentFormat->key() != cursor->paragraph()->at( cursor->index() )->format()->key() ) { + currentFormat->removeRef(); + currentFormat = doc->formatCollection()->format( cursor->paragraph()->at( cursor->index() )->format() ); + if ( currentFormat->isMisspelled() ) { + currentFormat->removeRef(); + currentFormat = doc->formatCollection()->format( currentFormat->font(), currentFormat->color() ); + } + emit currentFontChanged( currentFormat->font() ); + emit currentColorChanged( currentFormat->color() ); + emit currentVerticalAlignmentChanged( (VerticalAlignment)currentFormat->vAlign() ); + } + + if ( currentAlignment != cursor->paragraph()->alignment() ) { + currentAlignment = cursor->paragraph()->alignment(); + block_set_alignment = TRUE; + emit currentAlignmentChanged( currentAlignment ); + block_set_alignment = FALSE; + } +} + +/*! \internal */ + +void QTextEdit::placeCursor( const QPoint &pos, QTextCursor *c, bool link ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return; +#endif + if ( !c ) + c = cursor; + + resetInputContext(); + c->restoreState(); + QTextParagraph *s = doc->firstParagraph(); + c->place( pos, s, link ); + updateMicroFocusHint(); +} + + +void QTextEdit::updateMicroFocusHint() +{ + QTextCursor c( *cursor ); +#if 0 + // Disabled for Asian input method that shows candidate + // window. This behavior is same as Qt/E 2.3.7 which supports + // Asian input methods. Asian input methods need start point of IM + // selection text to place candidate window as adjacent to the + // selection text. + if ( d->preeditStart != -1 ) { + c.setIndex( d->preeditStart ); + if(doc->hasSelection(QTextDocument::IMSelectionText)) { + int para, index; + doc->selectionStart(QTextDocument::IMSelectionText, para, index); + c.setIndex(index); + } + } +#endif + + if ( hasFocus() || viewport()->hasFocus() ) { + int h = c.paragraph()->lineHeightOfChar( cursor->index() ); + if ( !readonly ) { + QFont f = c.paragraph()->at( c.index() )->format()->font(); + setMicroFocusHint( c.x() - contentsX() + frameWidth(), + c.y() + cursor->paragraph()->rect().y() - contentsY() + frameWidth(), 0, h, TRUE, &f ); + } + } +} + + + +void QTextEdit::formatMore() +{ + if ( !lastFormatted ) + return; + + int bottom = contentsHeight(); + int lastTop = -1; + int lastBottom = -1; + int to = 20; + bool firstVisible = FALSE; + QRect cr( contentsX(), contentsY(), visibleWidth(), visibleHeight() ); + for ( int i = 0; lastFormatted && + ( i < to || ( firstVisible && lastTop < contentsY()+height() ) ); + i++ ) { + lastFormatted->format(); + lastTop = lastFormatted->rect().top(); + lastBottom = lastFormatted->rect().bottom(); + if ( i == 0 ) + firstVisible = lastBottom < cr.bottom(); + bottom = QMAX( bottom, lastBottom ); + lastFormatted = lastFormatted->next(); + } + + if ( bottom > contentsHeight() ) { + resizeContents( contentsWidth(), QMAX( doc->height(), bottom ) ); + } else if ( !lastFormatted && lastBottom < contentsHeight() ) { + resizeContents( contentsWidth(), QMAX( doc->height(), lastBottom ) ); + if ( contentsHeight() < visibleHeight() ) + updateContents( 0, contentsHeight(), visibleWidth(), + visibleHeight() - contentsHeight() ); + } + + if ( lastFormatted ) + formatTimer->start( interval, TRUE ); + else + interval = QMAX( 0, interval ); +} + +void QTextEdit::doResize() +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( !d->optimMode ) +#endif + { + if ( wrapMode == FixedPixelWidth ) + return; + doc->setMinimumWidth( -1 ); + resizeContents( 0, 0 ); + doc->setWidth( visibleWidth() ); + doc->invalidate(); + lastFormatted = doc->firstParagraph(); + interval = 0; + formatMore(); + } + repaintContents( FALSE ); +} + +/*! \internal */ + +void QTextEdit::doChangeInterval() +{ + interval = 0; +} + +/*! + \reimp +*/ + +bool QTextEdit::eventFilter( QObject *o, QEvent *e ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( !d->optimMode && (o == this || o == viewport()) ) { +#else + if ( o == this || o == viewport() ) { +#endif + if ( e->type() == QEvent::FocusIn ) { + if ( QApplication::cursorFlashTime() > 0 ) + blinkTimer->start( QApplication::cursorFlashTime() / 2 ); + drawCursor( TRUE ); + updateMicroFocusHint(); + } else if ( e->type() == QEvent::FocusOut ) { + blinkTimer->stop(); + drawCursor( FALSE ); + } + } + + if ( o == this && e->type() == QEvent::PaletteChange ) { + QColor old( viewport()->colorGroup().color( QColorGroup::Text ) ); + if ( old != colorGroup().color( QColorGroup::Text ) ) { + QColor c( colorGroup().color( QColorGroup::Text ) ); + doc->setMinimumWidth( -1 ); + doc->setDefaultFormat( doc->formatCollection()->defaultFormat()->font(), c ); + lastFormatted = doc->firstParagraph(); + formatMore(); + repaintChanged(); + } + } + + return QScrollView::eventFilter( o, e ); +} + +/*! + \obsolete + */ +void QTextEdit::insert( const QString &text, bool indent, + bool checkNewLine, bool removeSelected ) +{ + uint f = 0; + if ( indent ) + f |= RedoIndentation; + if ( checkNewLine ) + f |= CheckNewLines; + if ( removeSelected ) + f |= RemoveSelected; + insert( text, f ); +} + +/*! + Inserts \a text at the current cursor position. + + The \a insertionFlags define how the text is inserted. If \c + RedoIndentation is set, the paragraph is re-indented. If \c + CheckNewLines is set, newline characters in \a text result in hard + line breaks (i.e. new paragraphs). If \c checkNewLine is not set, + the behaviour of the editor is undefined if the \a text contains + newlines. (It is not possible to change QTextEdit's newline handling + behavior, but you can use QString::replace() to preprocess text + before inserting it.) If \c RemoveSelected is set, any selected + text (in selection 0) is removed before the text is inserted. + + The default flags are \c CheckNewLines | \c RemoveSelected. + + If the widget is in \c LogText mode this function will do nothing. + + \sa paste() pasteSubType() +*/ + + +void QTextEdit::insert( const QString &text, uint insertionFlags ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return; +#endif + + if ( cursor->nestedDepth() != 0 ) // #### for 3.0, disable editing of tables as this is not advanced enough + return; + + bool indent = insertionFlags & RedoIndentation; + bool checkNewLine = insertionFlags & CheckNewLines; + bool removeSelected = insertionFlags & RemoveSelected; + bool imComposition = insertionFlags & AsIMCompositionText; + bool imSelection = insertionFlags & WithIMSelection; + QString txt( text ); + drawCursor( FALSE ); + if ( !isReadOnly() && doc->hasSelection( QTextDocument::Standard ) && removeSelected ) + removeSelectedText(); + QTextCursor c2 = *cursor; + int oldLen = 0; + + if ( undoEnabled && !isReadOnly() && undoRedoInfo.type != UndoRedoInfo::IME ) { + checkUndoRedoInfo( UndoRedoInfo::Insert ); + + if (undoRedoInfo.valid() && undoRedoInfo.index + undoRedoInfo.d->text.length() != cursor->index()) { + clearUndoRedo(); + undoRedoInfo.type = UndoRedoInfo::Insert; + } + + if ( !undoRedoInfo.valid() ) { + undoRedoInfo.id = cursor->paragraph()->paragId(); + undoRedoInfo.index = cursor->index(); + undoRedoInfo.d->text = QString::null; + } + oldLen = undoRedoInfo.d->text.length(); + } + + lastFormatted = checkNewLine && cursor->paragraph()->prev() ? + cursor->paragraph()->prev() : cursor->paragraph(); + QTextCursor oldCursor = *cursor; + cursor->insert( txt, checkNewLine ); + if ( doc->useFormatCollection() && !doc->preProcessor() ) { + doc->setSelectionStart( QTextDocument::Temp, oldCursor ); + doc->setSelectionEnd( QTextDocument::Temp, *cursor ); + doc->setFormat( QTextDocument::Temp, currentFormat, QTextFormat::Format ); + doc->removeSelection( QTextDocument::Temp ); + } + + if ( indent && ( txt == "{" || txt == "}" || txt == ":" || txt == "#" ) ) + cursor->indent(); + formatMore(); + repaintChanged(); + ensureCursorVisible(); + // Asian users regard selection text as cursor on candidate + // selection phase of input method, so ordinary cursor should be + // invisible if IM selection text exists. + drawCursor( !imSelection ); + + if ( undoEnabled && !isReadOnly() && undoRedoInfo.type != UndoRedoInfo::IME ) { + undoRedoInfo.d->text += txt; + if ( !doc->preProcessor() ) { + for ( int i = 0; i < (int)txt.length(); ++i ) { + if ( txt[ i ] != '\n' && c2.paragraph()->at( c2.index() )->format() ) { + c2.paragraph()->at( c2.index() )->format()->addRef(); + undoRedoInfo.d->text. + setFormat( oldLen + i, + c2.paragraph()->at( c2.index() )->format(), TRUE ); + } + c2.gotoNextLetter(); + } + } + } + + if ( !removeSelected ) { + doc->setSelectionStart( QTextDocument::Standard, oldCursor ); + doc->setSelectionEnd( QTextDocument::Standard, *cursor ); + repaintChanged(); + } + // updateMicroFocusHint() should not be invoked here when this + // function is invoked from imComposeEvent() because cursor + // postion is incorrect yet. imComposeEvent() invokes + // updateMicroFocusHint() later. + if ( !imComposition ) { + updateMicroFocusHint(); + } + setModified(); + emit textChanged(); +} + +/*! + Inserts \a text in the paragraph \a para at position \a index. +*/ + +void QTextEdit::insertAt( const QString &text, int para, int index ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + optimInsert( text, para, index ); + return; + } +#endif + resetInputContext(); + QTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return; + removeSelection( QTextDocument::Standard ); + QTextCursor tmp = *cursor; + cursor->setParagraph( p ); + cursor->setIndex( index ); + insert( text, FALSE, TRUE, FALSE ); + *cursor = tmp; + removeSelection( QTextDocument::Standard ); +} + +/*! + Inserts \a text as a new paragraph at position \a para. If \a para + is -1, the text is appended. Use append() if the append operation + is performance critical. +*/ + +void QTextEdit::insertParagraph( const QString &text, int para ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + optimInsert( text + "\n", para, 0 ); + return; + } +#endif + resetInputContext(); + for ( int i = 0; i < (int)doc->numSelections(); ++i ) + doc->removeSelection( i ); + + QTextParagraph *p = doc->paragAt( para ); + + bool append = !p; + if ( !p ) + p = doc->lastParagraph(); + + QTextCursor old = *cursor; + drawCursor( FALSE ); + + cursor->setParagraph( p ); + cursor->setIndex( 0 ); + clearUndoRedo(); + qtextedit_ignore_readonly = TRUE; + if ( append && cursor->paragraph()->length() > 1 ) { + cursor->setIndex( cursor->paragraph()->length() - 1 ); + doKeyboardAction( ActionReturn ); + } + insert( text, FALSE, TRUE, TRUE ); + doKeyboardAction( ActionReturn ); + qtextedit_ignore_readonly = FALSE; + + drawCursor( FALSE ); + *cursor = old; + drawCursor( TRUE ); + + repaintChanged(); +} + +/*! + Removes the paragraph \a para. +*/ + +void QTextEdit::removeParagraph( int para ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return; +#endif + resetInputContext(); + QTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return; + + for ( int i = 0; i < doc->numSelections(); ++i ) + doc->removeSelection( i ); + + QTextCursor start( doc ); + QTextCursor end( doc ); + start.setParagraph( p ); + start.setIndex( 0 ); + end.setParagraph( p ); + end.setIndex( p->length() - 1 ); + + if ( !(p == doc->firstParagraph() && p == doc->lastParagraph()) ) { + if ( p->next() ) { + end.setParagraph( p->next() ); + end.setIndex( 0 ); + } else if ( p->prev() ) { + start.setParagraph( p->prev() ); + start.setIndex( p->prev()->length() - 1 ); + } + } + + doc->setSelectionStart( QTextDocument::Temp, start ); + doc->setSelectionEnd( QTextDocument::Temp, end ); + removeSelectedText( QTextDocument::Temp ); +} + +/*! + Undoes the last operation. + + If there is no operation to undo, i.e. there is no undo step in + the undo/redo history, nothing happens. + + \sa undoAvailable() redo() undoDepth() +*/ + +void QTextEdit::undo() +{ + clearUndoRedo(); + if ( isReadOnly() || !doc->commands()->isUndoAvailable() || !undoEnabled ) + return; + + resetInputContext(); + for ( int i = 0; i < (int)doc->numSelections(); ++i ) + doc->removeSelection( i ); + +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif + + clearUndoRedo(); + drawCursor( FALSE ); + QTextCursor *c = doc->undo( cursor ); + if ( !c ) { + drawCursor( TRUE ); + return; + } + lastFormatted = 0; + repaintChanged(); + ensureCursorVisible(); + drawCursor( TRUE ); + updateMicroFocusHint(); + setModified(); + // ### If we get back to a completely blank textedit, it + // is possible that cursor is invalid and further actions + // might not fix the problem, so reset the cursor here. + // This is copied from removeSeletedText(), it might be + // okay to just call that. + if ( !cursor->isValid() ) { + delete cursor; + cursor = new QTextCursor( doc ); + drawCursor( TRUE ); + repaintContents( TRUE ); + } + emit undoAvailable( isUndoAvailable() ); + emit redoAvailable( isRedoAvailable() ); + emit textChanged(); +} + +/*! + Redoes the last operation. + + If there is no operation to redo, i.e. there is no redo step in + the undo/redo history, nothing happens. + + \sa redoAvailable() undo() undoDepth() +*/ + +void QTextEdit::redo() +{ + if ( isReadOnly() || !doc->commands()->isRedoAvailable() || !undoEnabled ) + return; + + resetInputContext(); + for ( int i = 0; i < (int)doc->numSelections(); ++i ) + doc->removeSelection( i ); + +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif + + clearUndoRedo(); + drawCursor( FALSE ); + QTextCursor *c = doc->redo( cursor ); + if ( !c ) { + drawCursor( TRUE ); + return; + } + lastFormatted = 0; + ensureCursorVisible(); + repaintChanged(); + ensureCursorVisible(); + drawCursor( TRUE ); + updateMicroFocusHint(); + setModified(); + emit undoAvailable( isUndoAvailable() ); + emit redoAvailable( isRedoAvailable() ); + emit textChanged(); +} + +/*! + Pastes the text from the clipboard into the text edit at the + current cursor position. Only plain text is pasted. + + If there is no text in the clipboard nothing happens. + + \sa pasteSubType() cut() QTextEdit::copy() +*/ + +void QTextEdit::paste() +{ +#ifndef QT_NO_MIMECLIPBOARD + if ( isReadOnly() ) + return; + QString subType = "plain"; + if ( textFormat() != PlainText ) { + QMimeSource *m = QApplication::clipboard()->data( d->clipboard_mode ); + if ( !m ) + return; + if ( m->provides( "application/x-qrichtext" ) ) + subType = "x-qrichtext"; + } + + pasteSubType( subType.latin1() ); + updateMicroFocusHint(); +#endif +} + +void QTextEdit::checkUndoRedoInfo( UndoRedoInfo::Type t ) +{ + if ( undoRedoInfo.valid() && t != undoRedoInfo.type ) { + clearUndoRedo(); + } + undoRedoInfo.type = t; +} + +/*! + Repaints any paragraphs that have changed. + + Although used extensively internally you shouldn't need to call + this yourself. +*/ + +void QTextEdit::repaintChanged() +{ + if ( !isUpdatesEnabled() || !viewport()->isUpdatesEnabled() ) + return; + + QPainter p( viewport() ); +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + optimDrawContents( &p, contentsX(), contentsY(), visibleWidth(), visibleHeight() ); + return; + } +#endif + p.translate( -contentsX(), -contentsY() ); + paintDocument( FALSE, &p, contentsX(), contentsY(), visibleWidth(), visibleHeight() ); +} + +#ifndef QT_NO_MIME +QTextDrag *QTextEdit::dragObject( QWidget *parent ) const +{ + if ( !doc->hasSelection( QTextDocument::Standard ) || + doc->selectedText( QTextDocument::Standard ).isEmpty() ) + return 0; + if ( textFormat() != RichText ) + return new QTextDrag( doc->selectedText( QTextDocument::Standard ), parent ); + QRichTextDrag *drag = new QRichTextDrag( parent ); + drag->setPlainText( doc->selectedText( QTextDocument::Standard ) ); + drag->setRichText( doc->selectedText( QTextDocument::Standard, TRUE ) ); + return drag; +} +#endif + +/*! + Copies the selected text (from selection 0) to the clipboard and + deletes it from the text edit. + + If there is no selected text (in selection 0) nothing happens. + + \sa QTextEdit::copy() paste() pasteSubType() +*/ + +void QTextEdit::cut() +{ + if ( isReadOnly() ) + return; + resetInputContext(); + normalCopy(); + removeSelectedText(); + updateMicroFocusHint(); +} + +void QTextEdit::normalCopy() +{ +#ifndef QT_NO_MIME + QTextDrag *drag = dragObject(); + if ( !drag ) + return; +#ifndef QT_NO_MIMECLIPBOARD + QApplication::clipboard()->setData( drag, d->clipboard_mode ); +#endif // QT_NO_MIMECLIPBOARD +#endif // QT_NO_MIME +} + +/*! + Copies any selected text (from selection 0) to the clipboard. + + \sa hasSelectedText() copyAvailable() +*/ + +void QTextEdit::copy() +{ +#ifndef QT_NO_CLIPBOARD +# ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode && optimHasSelection() ) + QApplication::clipboard()->setText( optimSelectedText(), d->clipboard_mode ); + else + normalCopy(); +# else + normalCopy(); +# endif +#endif +} + +/*! + \internal + + Re-indents the current paragraph. +*/ + +void QTextEdit::indent() +{ + if ( isReadOnly() ) + return; + + drawCursor( FALSE ); + if ( !doc->hasSelection( QTextDocument::Standard ) ) + cursor->indent(); + else + doc->indentSelection( QTextDocument::Standard ); + repaintChanged(); + drawCursor( TRUE ); + setModified(); + emit textChanged(); +} + +/*! + Reimplemented to allow tabbing through links. If \a n is TRUE the + tab moves the focus to the next child; if \a n is FALSE the tab + moves the focus to the previous child. Returns TRUE if the focus + was moved; otherwise returns FALSE. + */ + +bool QTextEdit::focusNextPrevChild( bool n ) +{ + if ( !isReadOnly() || !linksEnabled() ) + return FALSE; + bool b = doc->focusNextPrevChild( n ); + repaintChanged(); + if ( b ) { + QTextParagraph *p = doc->focusIndicator.parag; + int start = doc->focusIndicator.start; + int len = doc->focusIndicator.len; + + int y = p->rect().y(); + while ( p + && len == 0 + && p->at( start )->isCustom() + && p->at( start )->customItem()->isNested() ) { + + QTextTable *t = (QTextTable*)p->at( start )->customItem(); + QPtrList<QTextTableCell> cells = t->tableCells(); + QTextTableCell *c; + for ( c = cells.first(); c; c = cells.next() ) { + QTextDocument *cellDoc = c->richText(); + if ( cellDoc->hasFocusParagraph() ) { + y += c->geometry().y() + c->verticalAlignmentOffset(); + + p = cellDoc->focusIndicator.parag; + start = cellDoc->focusIndicator.start; + len = cellDoc->focusIndicator.len; + if ( p ) + y += p->rect().y(); + + break; + } + } + } + setContentsPos( contentsX(), QMIN( y, contentsHeight() - visibleHeight() ) ); + } + return b; +} + +/*! + \internal + + This functions sets the current format to \a f. Only the fields of \a + f which are specified by the \a flags are used. +*/ + +void QTextEdit::setFormat( QTextFormat *f, int flags ) +{ + if ( doc->hasSelection( QTextDocument::Standard ) ) { + drawCursor( FALSE ); + QTextCursor c1 = doc->selectionStartCursor( QTextDocument::Standard ); + c1.restoreState(); + QTextCursor c2 = doc->selectionEndCursor( QTextDocument::Standard ); + c2.restoreState(); + if ( undoEnabled ) { + clearUndoRedo(); + undoRedoInfo.type = UndoRedoInfo::Format; + undoRedoInfo.id = c1.paragraph()->paragId(); + undoRedoInfo.index = c1.index(); + undoRedoInfo.eid = c2.paragraph()->paragId(); + undoRedoInfo.eindex = c2.index(); + readFormats( c1, c2, undoRedoInfo.d->text ); + undoRedoInfo.format = f; + undoRedoInfo.flags = flags; + clearUndoRedo(); + } + doc->setFormat( QTextDocument::Standard, f, flags ); + repaintChanged(); + formatMore(); + drawCursor( TRUE ); + setModified(); + emit textChanged(); + } + if ( currentFormat && currentFormat->key() != f->key() ) { + currentFormat->removeRef(); + currentFormat = doc->formatCollection()->format( f ); + if ( currentFormat->isMisspelled() ) { + currentFormat->removeRef(); + currentFormat = doc->formatCollection()->format( currentFormat->font(), + currentFormat->color() ); + } + emit currentFontChanged( currentFormat->font() ); + emit currentColorChanged( currentFormat->color() ); + emit currentVerticalAlignmentChanged( (VerticalAlignment)currentFormat->vAlign() ); + if ( cursor->index() == cursor->paragraph()->length() - 1 ) { + currentFormat->addRef(); + cursor->paragraph()->string()->setFormat( cursor->index(), currentFormat, TRUE ); + if ( cursor->paragraph()->length() == 1 ) { + cursor->paragraph()->invalidate( 0 ); + cursor->paragraph()->format(); + repaintChanged(); + } + } + } +} + +/*! + \reimp +*/ + +void QTextEdit::setPalette( const QPalette &p ) +{ + QScrollView::setPalette( p ); + if ( textFormat() == PlainText ) { + QTextFormat *f = doc->formatCollection()->defaultFormat(); + f->setColor( colorGroup().text() ); + updateContents(); + } +} + +/*! \internal + \warning In Qt 3.1 we will provide a cleaer API for the + functionality which is provided by this function and in Qt 4.0 this + function will go away. + + Sets the paragraph style of the current paragraph + to \a dm. If \a dm is QStyleSheetItem::DisplayListItem, the + type of the list item is set to \a listStyle. + + \sa setAlignment() +*/ + +void QTextEdit::setParagType( QStyleSheetItem::DisplayMode dm, + QStyleSheetItem::ListStyle listStyle ) +{ + if ( isReadOnly() ) + return; + + drawCursor( FALSE ); + QTextParagraph *start = cursor->paragraph(); + QTextParagraph *end = start; + if ( doc->hasSelection( QTextDocument::Standard ) ) { + start = doc->selectionStartCursor( QTextDocument::Standard ).topParagraph(); + end = doc->selectionEndCursor( QTextDocument::Standard ).topParagraph(); + if ( end->paragId() < start->paragId() ) + return; // do not trust our selections + } + + clearUndoRedo(); + undoRedoInfo.type = UndoRedoInfo::Style; + undoRedoInfo.id = start->paragId(); + undoRedoInfo.eid = end->paragId(); + undoRedoInfo.styleInformation = QTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid ); + + while ( start != end->next() ) { + start->setListStyle( listStyle ); + if ( dm == QStyleSheetItem::DisplayListItem ) { + start->setListItem( TRUE ); + if( start->listDepth() == 0 ) + start->setListDepth( 1 ); + } else if ( start->isListItem() ) { + start->setListItem( FALSE ); + start->setListDepth( QMAX( start->listDepth()-1, 0 ) ); + } + start = start->next(); + } + + clearUndoRedo(); + repaintChanged(); + formatMore(); + drawCursor( TRUE ); + setModified(); + emit textChanged(); +} + +/*! + Sets the alignment of the current paragraph to \a a. Valid + alignments are \c Qt::AlignLeft, \c Qt::AlignRight, + \c Qt::AlignJustify and \c Qt::AlignCenter (which centers + horizontally). +*/ + +void QTextEdit::setAlignment( int a ) +{ + if ( isReadOnly() || block_set_alignment ) + return; + + drawCursor( FALSE ); + QTextParagraph *start = cursor->paragraph(); + QTextParagraph *end = start; + if ( doc->hasSelection( QTextDocument::Standard ) ) { + start = doc->selectionStartCursor( QTextDocument::Standard ).topParagraph(); + end = doc->selectionEndCursor( QTextDocument::Standard ).topParagraph(); + if ( end->paragId() < start->paragId() ) + return; // do not trust our selections + } + + clearUndoRedo(); + undoRedoInfo.type = UndoRedoInfo::Style; + undoRedoInfo.id = start->paragId(); + undoRedoInfo.eid = end->paragId(); + undoRedoInfo.styleInformation = QTextStyleCommand::readStyleInformation( doc, undoRedoInfo.id, undoRedoInfo.eid ); + + while ( start != end->next() ) { + start->setAlignment( a ); + start = start->next(); + } + + clearUndoRedo(); + repaintChanged(); + formatMore(); + drawCursor( TRUE ); + if ( currentAlignment != a ) { + currentAlignment = a; + emit currentAlignmentChanged( currentAlignment ); + } + setModified(); + emit textChanged(); +} + +void QTextEdit::updateCurrentFormat() +{ + int i = cursor->index(); + if ( i > 0 ) + --i; + if ( doc->useFormatCollection() && + ( !currentFormat || currentFormat->key() != cursor->paragraph()->at( i )->format()->key() ) ) { + if ( currentFormat ) + currentFormat->removeRef(); + currentFormat = doc->formatCollection()->format( cursor->paragraph()->at( i )->format() ); + if ( currentFormat->isMisspelled() ) { + currentFormat->removeRef(); + currentFormat = doc->formatCollection()->format( currentFormat->font(), currentFormat->color() ); + } + emit currentFontChanged( currentFormat->font() ); + emit currentColorChanged( currentFormat->color() ); + emit currentVerticalAlignmentChanged( (VerticalAlignment)currentFormat->vAlign() ); + } + + if ( currentAlignment != cursor->paragraph()->alignment() ) { + currentAlignment = cursor->paragraph()->alignment(); + block_set_alignment = TRUE; + emit currentAlignmentChanged( currentAlignment ); + block_set_alignment = FALSE; + } +} + +/*! + If \a b is TRUE sets the current format to italic; otherwise sets + the current format to non-italic. + + \sa italic() +*/ + +void QTextEdit::setItalic( bool b ) +{ + QTextFormat f( *currentFormat ); + f.setItalic( b ); + QTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat(f2, QTextFormat::Italic ); +} + +/*! + If \a b is TRUE sets the current format to bold; otherwise sets + the current format to non-bold. + + \sa bold() +*/ + +void QTextEdit::setBold( bool b ) +{ + QTextFormat f( *currentFormat ); + f.setBold( b ); + QTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat( f2, QTextFormat::Bold ); +} + +/*! + If \a b is TRUE sets the current format to underline; otherwise + sets the current format to non-underline. + + \sa underline() +*/ + +void QTextEdit::setUnderline( bool b ) +{ + QTextFormat f( *currentFormat ); + f.setUnderline( b ); + QTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat( f2, QTextFormat::Underline ); +} + +/*! + Sets the font family of the current format to \a fontFamily. + + \sa family() setCurrentFont() +*/ + +void QTextEdit::setFamily( const QString &fontFamily ) +{ + QTextFormat f( *currentFormat ); + f.setFamily( fontFamily ); + QTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat( f2, QTextFormat::Family ); +} + +/*! + Sets the point size of the current format to \a s. + + Note that if \a s is zero or negative, the behaviour of this + function is not defined. + + \sa pointSize() setCurrentFont() setFamily() +*/ + +void QTextEdit::setPointSize( int s ) +{ + QTextFormat f( *currentFormat ); + f.setPointSize( s ); + QTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat( f2, QTextFormat::Size ); +} + +/*! + Sets the color of the current format, i.e. of the text, to \a c. + + \sa color() setPaper() +*/ + +void QTextEdit::setColor( const QColor &c ) +{ + QTextFormat f( *currentFormat ); + f.setColor( c ); + QTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat( f2, QTextFormat::Color ); +} + +/*! + Sets the vertical alignment of the current format, i.e. of the + text, to \a a. + + \sa color() setPaper() +*/ + +void QTextEdit::setVerticalAlignment( VerticalAlignment a ) +{ + QTextFormat f( *currentFormat ); + f.setVAlign( (QTextFormat::VerticalAlignment)a ); + QTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat( f2, QTextFormat::VAlign ); +} + +void QTextEdit::setFontInternal( const QFont &f_ ) +{ + QTextFormat f( *currentFormat ); + f.setFont( f_ ); + QTextFormat *f2 = doc->formatCollection()->format( &f ); + setFormat( f2, QTextFormat::Font ); +} + + +QString QTextEdit::text() const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return optimText(); +#endif + + QTextParagraph *p = doc->firstParagraph(); + if ( !p || (!p->next() && p->length() <= 1) ) + return QString::fromLatin1(""); + + if ( isReadOnly() ) + return doc->originalText(); + return doc->text(); +} + +/*! + \overload + + Returns the text of paragraph \a para. + + If textFormat() is \c RichText the text will contain HTML + formatting tags. +*/ + +QString QTextEdit::text( int para ) const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode && (d->od->numLines >= para) ) { + QString paraStr = d->od->lines[ LOGOFFSET(para) ]; + if ( paraStr.isEmpty() ) + paraStr = "\n"; + return paraStr; + } else +#endif + return doc->text( para ); +} + +/*! + \overload + + Changes the text of the text edit to the string \a text and the + context to \a context. Any previous text is removed. + + \a text may be interpreted either as plain text or as rich text, + depending on the textFormat(). The default setting is \c AutoText, + i.e. the text edit auto-detects the format from \a text. + + For rich text the rendering style and available tags are defined + by a styleSheet(); see QStyleSheet for details. + + The optional \a context is a path which the text edit's + QMimeSourceFactory uses to resolve the locations of files and + images. (See \l{QTextEdit::QTextEdit()}.) It is passed to the text + edit's QMimeSourceFactory when quering data. + + Note that the undo/redo history is cleared by this function. + + \sa text(), setTextFormat() +*/ + +void QTextEdit::setText( const QString &text, const QString &context ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + optimSetText( text ); + return; + } +#endif + resetInputContext(); + if ( !isModified() && isReadOnly() && + this->context() == context && this->text() == text ) + return; + + emit undoAvailable( FALSE ); + emit redoAvailable( FALSE ); + undoRedoInfo.clear(); + doc->commands()->clear(); + + lastFormatted = 0; + int oldCursorPos = cursor->index(); + int oldCursorPar = cursor->paragraph()->paragId(); + cursor->restoreState(); + delete cursor; + doc->setText( text, context ); + + if ( wrapMode == FixedPixelWidth ) { + resizeContents( wrapWidth, 0 ); + doc->setWidth( wrapWidth ); + doc->setMinimumWidth( wrapWidth ); + } else { + doc->setMinimumWidth( -1 ); + resizeContents( 0, 0 ); + } + + lastFormatted = doc->firstParagraph(); + cursor = new QTextCursor( doc ); + updateContents(); + + if ( isModified() ) + setModified( FALSE ); + emit textChanged(); + if ( cursor->index() != oldCursorPos || cursor->paragraph()->paragId() != oldCursorPar ) { + emit cursorPositionChanged( cursor ); + emit cursorPositionChanged( cursor->paragraph()->paragId(), cursor->index() ); + } + formatMore(); + updateCurrentFormat(); + d->scrollToAnchor = QString::null; +} + +/*! + \property QTextEdit::text + \brief the text edit's text + + There is no default text. + + On setting, any previous text is deleted. + + The text may be interpreted either as plain text or as rich text, + depending on the textFormat(). The default setting is \c AutoText, + i.e. the text edit auto-detects the format of the text. + + For richtext, calling text() on an editable QTextEdit will cause + the text to be regenerated from the textedit. This may mean that + the QString returned may not be exactly the same as the one that + was set. + + \sa textFormat +*/ + + +/*! + \property QTextEdit::readOnly + \brief whether the text edit is read-only + + In a read-only text edit the user can only navigate through the + text and select text; modifying the text is not possible. + + This property's default is FALSE. +*/ + +/*! + Finds the next occurrence of the string, \a expr. Returns TRUE if + \a expr was found; otherwise returns FALSE. + + If \a para and \a index are both 0 the search begins from the + current cursor position. If \a para and \a index are both not 0, + the search begins from the \a *index character position in the + \a *para paragraph. + + If \a cs is TRUE the search is case sensitive, otherwise it is + case insensitive. If \a wo is TRUE the search looks for whole word + matches only; otherwise it searches for any matching text. If \a + forward is TRUE (the default) the search works forward from the + starting position to the end of the text, otherwise it works + backwards to the beginning of the text. + + If \a expr is found the function returns TRUE. If \a index and \a + para are not 0, the number of the paragraph in which the first + character of the match was found is put into \a *para, and the + index position of that character within the paragraph is put into + \a *index. + + If \a expr is not found the function returns FALSE. If \a index + and \a para are not 0 and \a expr is not found, \a *index + and \a *para are undefined. + + Please note that this function will make the next occurrence of + the string (if found) the current selection, and will thus + modify the cursor position. + + Using the \a para and \a index parameters will not work correctly + in case the document contains tables. +*/ + +bool QTextEdit::find( const QString &expr, bool cs, bool wo, bool forward, + int *para, int *index ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return optimFind( expr, cs, wo, forward, para, index ); +#endif + drawCursor( FALSE ); +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif + QTextCursor findcur = *cursor; + if ( para && index ) { + if ( doc->paragAt( *para ) ) + findcur.gotoPosition( doc->paragAt(*para), *index ); + else + findcur.gotoEnd(); + } else if ( doc->hasSelection( QTextDocument::Standard ) ){ + // maks sure we do not find the same selection again + if ( forward ) + findcur.gotoNextLetter(); + else + findcur.gotoPreviousLetter(); + } else if (!forward && findcur.index() == 0 && findcur.paragraph() == findcur.topParagraph()) { + findcur.gotoEnd(); + } + removeSelection( QTextDocument::Standard ); + bool found = doc->find( findcur, expr, cs, wo, forward ); + if ( found ) { + if ( para ) + *para = findcur.paragraph()->paragId(); + if ( index ) + *index = findcur.index(); + *cursor = findcur; + repaintChanged(); + ensureCursorVisible(); + } + drawCursor( TRUE ); + if (found) { + emit cursorPositionChanged( cursor ); + emit cursorPositionChanged( cursor->paragraph()->paragId(), cursor->index() ); + } + return found; +} + +void QTextEdit::blinkCursor() +{ + if ( !cursorVisible ) + return; + bool cv = cursorVisible; + blinkCursorVisible = !blinkCursorVisible; + drawCursor( blinkCursorVisible ); + cursorVisible = cv; +} + +/*! + Sets the cursor to position \a index in paragraph \a para. + + \sa getCursorPosition() +*/ + +void QTextEdit::setCursorPosition( int para, int index ) +{ + QTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return; + + resetInputContext(); + if ( index > p->length() - 1 ) + index = p->length() - 1; + + drawCursor( FALSE ); + cursor->setParagraph( p ); + cursor->setIndex( index ); + ensureCursorVisible(); + drawCursor( TRUE ); + updateCurrentFormat(); + emit cursorPositionChanged( cursor ); + emit cursorPositionChanged( cursor->paragraph()->paragId(), cursor->index() ); +} + +/*! + This function sets the \a *para and \a *index parameters to the + current cursor position. \a para and \a index must not be 0. + + \sa setCursorPosition() +*/ + +void QTextEdit::getCursorPosition( int *para, int *index ) const +{ + if ( !para || !index ) + return; + *para = cursor->paragraph()->paragId(); + *index = cursor->index(); +} + +/*! + Sets a selection which starts at position \a indexFrom in + paragraph \a paraFrom and ends at position \a indexTo in paragraph + \a paraTo. + + Any existing selections which have a different id (\a selNum) are + left alone, but if an existing selection has the same id as \a + selNum it is removed and replaced by this selection. + + Uses the selection settings of selection \a selNum. If \a selNum + is 0, this is the default selection. + + The cursor is moved to the end of the selection if \a selNum is 0, + otherwise the cursor position remains unchanged. + + \sa getSelection() selectedText +*/ + +void QTextEdit::setSelection( int paraFrom, int indexFrom, + int paraTo, int indexTo, int selNum ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if (d->optimMode) { + optimSetSelection(paraFrom, indexFrom, paraTo, indexTo); + repaintContents(FALSE); + return; + } +#endif + resetInputContext(); + if ( doc->hasSelection( selNum ) ) { + doc->removeSelection( selNum ); + repaintChanged(); + } + if ( selNum > doc->numSelections() - 1 ) + doc->addSelection( selNum ); + QTextParagraph *p1 = doc->paragAt( paraFrom ); + if ( !p1 ) + return; + QTextParagraph *p2 = doc->paragAt( paraTo ); + if ( !p2 ) + return; + + if ( indexFrom > p1->length() - 1 ) + indexFrom = p1->length() - 1; + if ( indexTo > p2->length() - 1 ) + indexTo = p2->length() - 1; + + drawCursor( FALSE ); + QTextCursor c = *cursor; + QTextCursor oldCursor = *cursor; + c.setParagraph( p1 ); + c.setIndex( indexFrom ); + cursor->setParagraph( p2 ); + cursor->setIndex( indexTo ); + doc->setSelectionStart( selNum, c ); + doc->setSelectionEnd( selNum, *cursor ); + repaintChanged(); + ensureCursorVisible(); + if ( selNum != QTextDocument::Standard ) + *cursor = oldCursor; + drawCursor( TRUE ); +} + +/*! + If there is a selection, \a *paraFrom is set to the number of the + paragraph in which the selection begins and \a *paraTo is set to + the number of the paragraph in which the selection ends. (They + could be the same.) \a *indexFrom is set to the index at which the + selection begins within \a *paraFrom, and \a *indexTo is set to + the index at which the selection ends within \a *paraTo. + + If there is no selection, \a *paraFrom, \a *indexFrom, \a *paraTo + and \a *indexTo are all set to -1. + + If \a paraFrom, \a indexFrom, \a paraTo or \a indexTo is 0 this + function does nothing. + + The \a selNum is the number of the selection (multiple selections + are supported). It defaults to 0 (the default selection). + + \sa setSelection() selectedText +*/ + +void QTextEdit::getSelection( int *paraFrom, int *indexFrom, + int *paraTo, int *indexTo, int selNum ) const +{ + if ( !paraFrom || !paraTo || !indexFrom || !indexTo ) + return; +#ifdef QT_TEXTEDIT_OPTIMIZATION + if (d->optimMode) { + *paraFrom = d->od->selStart.line; + *paraTo = d->od->selEnd.line; + *indexFrom = d->od->selStart.index; + *indexTo = d->od->selEnd.index; + return; + } +#endif + if ( !doc->hasSelection( selNum ) ) { + *paraFrom = -1; + *indexFrom = -1; + *paraTo = -1; + *indexTo = -1; + return; + } + + doc->selectionStart( selNum, *paraFrom, *indexFrom ); + doc->selectionEnd( selNum, *paraTo, *indexTo ); +} + +/*! + \property QTextEdit::textFormat + \brief the text format: rich text, plain text, log text or auto text. + + The text format is one of the following: + \list + \i PlainText - all characters, except newlines, are displayed + verbatim, including spaces. Whenever a newline appears in the text + the text edit inserts a hard line break and begins a new + paragraph. + \i RichText - rich text rendering. The available styles are + defined in the default stylesheet QStyleSheet::defaultSheet(). + \i LogText - optimized mode for very large texts. Supports a very + limited set of formatting tags (color, bold, underline and italic + settings). + \i AutoText - this is the default. The text edit autodetects which + rendering style is best, \c PlainText or \c RichText. This is done + by using the QStyleSheet::mightBeRichText() function. + \endlist +*/ + +void QTextEdit::setTextFormat( TextFormat format ) +{ + doc->setTextFormat( format ); +#ifdef QT_TEXTEDIT_OPTIMIZATION + checkOptimMode(); +#endif +} + +Qt::TextFormat QTextEdit::textFormat() const +{ + return doc->textFormat(); +} + +/*! + Returns the number of paragraphs in the text; an empty textedit is always + considered to have one paragraph, so 1 is returned in this case. +*/ + +int QTextEdit::paragraphs() const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + return d->od->numLines; + } +#endif + return doc->lastParagraph()->paragId() + 1; +} + +/*! + Returns the number of lines in paragraph \a para, or -1 if there + is no paragraph with index \a para. +*/ + +int QTextEdit::linesOfParagraph( int para ) const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + if ( d->od->numLines >= para ) + return 1; + else + return -1; + } +#endif + QTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return -1; + return p->lines(); +} + +/*! + Returns the length of the paragraph \a para (i.e. the number of + characters), or -1 if there is no paragraph with index \a para. + + This function ignores newlines. +*/ + +int QTextEdit::paragraphLength( int para ) const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + if ( d->od->numLines >= para ) { + if ( d->od->lines[ LOGOFFSET(para) ].isEmpty() ) // CR + return 1; + else + return d->od->lines[ LOGOFFSET(para) ].length(); + } + return -1; + } +#endif + QTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return -1; + return p->length() - 1; +} + +/*! + Returns the number of lines in the text edit; this could be 0. + + \warning This function may be slow. Lines change all the time + during word wrapping, so this function has to iterate over all the + paragraphs and get the number of lines from each one individually. +*/ + +int QTextEdit::lines() const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + return d->od->numLines; + } +#endif + QTextParagraph *p = doc->firstParagraph(); + int l = 0; + while ( p ) { + l += p->lines(); + p = p->next(); + } + + return l; +} + +/*! + Returns the line number of the line in paragraph \a para in which + the character at position \a index appears. The \a index position is + relative to the beginning of the paragraph. If there is no such + paragraph or no such character at the \a index position (e.g. the + index is out of range) -1 is returned. +*/ + +int QTextEdit::lineOfChar( int para, int index ) +{ + QTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return -1; + + int idx, line; + QTextStringChar *c = p->lineStartOfChar( index, &idx, &line ); + if ( !c ) + return -1; + + return line; +} + +void QTextEdit::setModified( bool m ) +{ + bool oldModified = modified; + modified = m; + if ( modified && doc->oTextValid ) + doc->invalidateOriginalText(); + if ( oldModified != modified ) + emit modificationChanged( modified ); +} + +/*! + \property QTextEdit::modified + \brief whether the document has been modified by the user +*/ + +bool QTextEdit::isModified() const +{ + return modified; +} + +void QTextEdit::setModified() +{ + if ( !isModified() ) + setModified( TRUE ); +} + +/*! + Returns TRUE if the current format is italic; otherwise returns FALSE. + + \sa setItalic() +*/ + +bool QTextEdit::italic() const +{ + return currentFormat->font().italic(); +} + +/*! + Returns TRUE if the current format is bold; otherwise returns FALSE. + + \sa setBold() +*/ + +bool QTextEdit::bold() const +{ + return currentFormat->font().bold(); +} + +/*! + Returns TRUE if the current format is underlined; otherwise returns + FALSE. + + \sa setUnderline() +*/ + +bool QTextEdit::underline() const +{ + return currentFormat->font().underline(); +} + +/*! + Returns the font family of the current format. + + \sa setFamily() setCurrentFont() setPointSize() +*/ + +QString QTextEdit::family() const +{ + return currentFormat->font().family(); +} + +/*! + Returns the point size of the font of the current format. + + \sa setFamily() setCurrentFont() setPointSize() +*/ + +int QTextEdit::pointSize() const +{ + return currentFormat->font().pointSize(); +} + +/*! + Returns the color of the current format. + + \sa setColor() setPaper() +*/ + +QColor QTextEdit::color() const +{ + return currentFormat->color(); +} + +/*! + \obsolete + + Returns QScrollView::font() + + \warning In previous versions this function returned the font of + the current format. This lead to confusion. Please use + currentFont() instead. +*/ + +QFont QTextEdit::font() const +{ + return QScrollView::font(); +} + +/*! + Returns the font of the current format. + + \sa setCurrentFont() setFamily() setPointSize() +*/ + +QFont QTextEdit::currentFont() const +{ + return currentFormat->font(); +} + + +/*! + Returns the alignment of the current paragraph. + + \sa setAlignment() +*/ + +int QTextEdit::alignment() const +{ + return currentAlignment; +} + +void QTextEdit::startDrag() +{ +#ifndef QT_NO_DRAGANDDROP + mousePressed = FALSE; + inDoubleClick = FALSE; + QDragObject *drag = dragObject( viewport() ); + if ( !drag ) + return; + if ( isReadOnly() ) { + drag->dragCopy(); + } else { + if ( drag->drag() && QDragObject::target() != this && QDragObject::target() != viewport() ) + removeSelectedText(); + } +#endif +} + +/*! + If \a select is TRUE (the default), all the text is selected as + selection 0. If \a select is FALSE any selected text is + unselected, i.e. the default selection (selection 0) is cleared. + + \sa selectedText +*/ + +void QTextEdit::selectAll( bool select ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + if ( select ) + optimSelectAll(); + else + optimRemoveSelection(); + return; + } +#endif + if ( !select ) + doc->removeSelection( QTextDocument::Standard ); + else + doc->selectAll( QTextDocument::Standard ); + repaintChanged(); + emit copyAvailable( doc->hasSelection( QTextDocument::Standard ) ); + emit selectionChanged(); +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif +} + +void QTextEdit::UndoRedoInfo::clear() +{ + if ( valid() ) { + if ( type == Insert || type == Return ) + doc->addCommand( new QTextInsertCommand( doc, id, index, d->text.rawData(), styleInformation ) ); + else if ( type == Format ) + doc->addCommand( new QTextFormatCommand( doc, id, index, eid, eindex, d->text.rawData(), format, flags ) ); + else if ( type == Style ) + doc->addCommand( new QTextStyleCommand( doc, id, eid, styleInformation ) ); + else if ( type != Invalid ) { + doc->addCommand( new QTextDeleteCommand( doc, id, index, d->text.rawData(), styleInformation ) ); + } + } + type = Invalid; + d->text = QString::null; + id = -1; + index = -1; + styleInformation = QByteArray(); +} + + +/*! + If there is some selected text (in selection 0) it is deleted. If + there is no selected text (in selection 0) the character to the + right of the text cursor is deleted. + + \sa removeSelectedText() cut() +*/ + +void QTextEdit::del() +{ + if ( doc->hasSelection( QTextDocument::Standard ) ) { + removeSelectedText(); + return; + } + + doKeyboardAction( ActionDelete ); +} + + +QTextEdit::UndoRedoInfo::UndoRedoInfo( QTextDocument *dc ) + : type( Invalid ), doc( dc ) +{ + d = new QUndoRedoInfoPrivate; + d->text = QString::null; + id = -1; + index = -1; +} + +QTextEdit::UndoRedoInfo::~UndoRedoInfo() +{ + delete d; +} + +bool QTextEdit::UndoRedoInfo::valid() const +{ + return id >= 0 && type != Invalid; +} + +/*! + \internal + + Resets the current format to the default format. +*/ + +void QTextEdit::resetFormat() +{ + setAlignment( Qt::AlignAuto ); + setParagType( QStyleSheetItem::DisplayBlock, QStyleSheetItem::ListDisc ); + setFormat( doc->formatCollection()->defaultFormat(), QTextFormat::Format ); +} + +/*! + Returns the QStyleSheet which is being used by this text edit. + + \sa setStyleSheet() +*/ + +QStyleSheet* QTextEdit::styleSheet() const +{ + return doc->styleSheet(); +} + +/*! + Sets the stylesheet to use with this text edit to \a styleSheet. + Changes will only take effect for new text added with setText() or + append(). + + \sa styleSheet() +*/ + +void QTextEdit::setStyleSheet( QStyleSheet* styleSheet ) +{ + doc->setStyleSheet( styleSheet ); +} + +/*! + \property QTextEdit::paper + \brief the background (paper) brush. + + The brush that is currently used to draw the background of the + text edit. The initial setting is an empty brush. +*/ + +void QTextEdit::setPaper( const QBrush& pap ) +{ + doc->setPaper( new QBrush( pap ) ); + + if ( pap.pixmap() ) { + viewport()->setBackgroundPixmap( *pap.pixmap() ); + } else { + setPaletteBackgroundColor( pap.color() ); + viewport()->setPaletteBackgroundColor( pap.color() ); + } + +#ifdef QT_TEXTEDIT_OPTIMIZATION + // force a repaint of the entire viewport - using updateContents() + // would clip the coords to the content size + if (d->optimMode) + repaintContents(contentsX(), contentsY(), viewport()->width(), viewport()->height()); + else +#endif + updateContents(); +} + +QBrush QTextEdit::paper() const +{ + if ( doc->paper() ) + return *doc->paper(); + return QBrush( colorGroup().base() ); +} + +/*! + \property QTextEdit::linkUnderline + \brief whether hypertext links will be underlined + + If TRUE (the default) hypertext links will be displayed + underlined. If FALSE links will not be displayed underlined. +*/ + +void QTextEdit::setLinkUnderline( bool b ) +{ + if ( doc->underlineLinks() == b ) + return; + doc->setUnderlineLinks( b ); + repaintChanged(); +} + +bool QTextEdit::linkUnderline() const +{ + return doc->underlineLinks(); +} + +/*! + Sets the text edit's mimesource factory to \a factory. See + QMimeSourceFactory for further details. + + \sa mimeSourceFactory() + */ + +#ifndef QT_NO_MIME +void QTextEdit::setMimeSourceFactory( QMimeSourceFactory* factory ) +{ + doc->setMimeSourceFactory( factory ); +} + +/*! + Returns the QMimeSourceFactory which is being used by this text + edit. + + \sa setMimeSourceFactory() +*/ + +QMimeSourceFactory* QTextEdit::mimeSourceFactory() const +{ + return doc->mimeSourceFactory(); +} +#endif + +/*! + Returns how many pixels high the text edit needs to be to display + all the text if the text edit is \a w pixels wide. +*/ + +int QTextEdit::heightForWidth( int w ) const +{ + int oldw = doc->width(); + doc->doLayout( 0, w ); + int h = doc->height(); + doc->setWidth( oldw ); + doc->invalidate(); + ( (QTextEdit*)this )->formatMore(); + return h; +} + +/*! + Appends a new paragraph with \a text to the end of the text edit. Note that + the undo/redo history is cleared by this function, and no undo + history is kept for appends which makes them faster than + insert()s. If you want to append text which is added to the + undo/redo history as well, use insertParagraph(). +*/ + +void QTextEdit::append( const QString &text ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + optimAppend( text ); + return; + } +#endif + // flush and clear the undo/redo stack if necessary + undoRedoInfo.clear(); + doc->commands()->clear(); + + doc->removeSelection( QTextDocument::Standard ); + TextFormat f = doc->textFormat(); + if ( f == AutoText ) { + if ( QStyleSheet::mightBeRichText( text ) ) + f = RichText; + else + f = PlainText; + } + + drawCursor( FALSE ); + QTextCursor oldc( *cursor ); + ensureFormatted( doc->lastParagraph() ); + bool atBottom = contentsY() >= contentsHeight() - visibleHeight(); + cursor->gotoEnd(); + if ( cursor->index() > 0 ) + cursor->splitAndInsertEmptyParagraph(); + QTextCursor oldCursor2 = *cursor; + + if ( f == Qt::PlainText ) { + cursor->insert( text, TRUE ); + if ( doc->useFormatCollection() && !doc->preProcessor() && + currentFormat != cursor->paragraph()->at( cursor->index() )->format() ) { + doc->setSelectionStart( QTextDocument::Temp, oldCursor2 ); + doc->setSelectionEnd( QTextDocument::Temp, *cursor ); + doc->setFormat( QTextDocument::Temp, currentFormat, QTextFormat::Format ); + doc->removeSelection( QTextDocument::Temp ); + } + } else { + cursor->paragraph()->setListItem( FALSE ); + cursor->paragraph()->setListDepth( 0 ); + if ( cursor->paragraph()->prev() ) + cursor->paragraph()->prev()->invalidate(0); // vertical margins might have to change + doc->setRichTextInternal( text ); + } + formatMore(); + repaintChanged(); + if ( atBottom ) + scrollToBottom(); + *cursor = oldc; + if ( !isReadOnly() ) + cursorVisible = TRUE; + setModified(); + emit textChanged(); +} + +/*! + \property QTextEdit::hasSelectedText + \brief whether some text is selected in selection 0 +*/ + +bool QTextEdit::hasSelectedText() const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return optimHasSelection(); + else +#endif + return doc->hasSelection( QTextDocument::Standard ); +} + +/*! + \property QTextEdit::selectedText + \brief The selected text (from selection 0) or an empty string if + there is no currently selected text (in selection 0). + + The text is always returned as \c PlainText if the textFormat() is + \c PlainText or \c AutoText, otherwise it is returned as HTML. + + \sa hasSelectedText +*/ + +QString QTextEdit::selectedText() const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return optimSelectedText(); + else +#endif + return doc->selectedText( QTextDocument::Standard, textFormat() == RichText ); +} + +bool QTextEdit::handleReadOnlyKeyEvent( QKeyEvent *e ) +{ + switch( e->key() ) { + case Key_Down: + setContentsPos( contentsX(), contentsY() + 10 ); + break; + case Key_Up: + setContentsPos( contentsX(), contentsY() - 10 ); + break; + case Key_Left: + setContentsPos( contentsX() - 10, contentsY() ); + break; + case Key_Right: + setContentsPos( contentsX() + 10, contentsY() ); + break; + case Key_PageUp: + setContentsPos( contentsX(), contentsY() - visibleHeight() ); + break; + case Key_PageDown: + setContentsPos( contentsX(), contentsY() + visibleHeight() ); + break; + case Key_Home: + setContentsPos( contentsX(), 0 ); + break; + case Key_End: + setContentsPos( contentsX(), contentsHeight() - visibleHeight() ); + break; + case Key_F16: // Copy key on Sun keyboards + copy(); + break; +#ifndef QT_NO_NETWORKPROTOCOL + case Key_Return: + case Key_Enter: + case Key_Space: { + if (!doc->focusIndicator.href.isEmpty() + || !doc->focusIndicator.name.isEmpty()) { + if (!doc->focusIndicator.href.isEmpty()) { + QUrl u( doc->context(), doc->focusIndicator.href, TRUE ); + emitLinkClicked( u.toString( FALSE, FALSE ) ); + } + if (!doc->focusIndicator.name.isEmpty()) { + if (::qt_cast<QTextBrowser*>(this)) { // change for 4.0 + QConnectionList *clist = receivers( + "anchorClicked(const QString&,const QString&)"); + if (!signalsBlocked() && clist) { + QUObject o[3]; + static_QUType_QString.set(o+1, + doc->focusIndicator.name); + static_QUType_QString.set(o+2, + doc->focusIndicator.href); + activate_signal( clist, o); + } + } + } +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif + } + } break; +#endif + default: + if ( e->state() & ControlButton ) { + switch ( e->key() ) { + case Key_C: case Key_F16: // Copy key on Sun keyboards + copy(); + break; +#ifdef Q_WS_WIN + case Key_Insert: + copy(); + break; + case Key_A: + selectAll(); + break; +#endif + } + + } + return FALSE; + } + return TRUE; +} + +/*! + Returns the context of the text edit. The context is a path which + the text edit's QMimeSourceFactory uses to resolve the locations + of files and images. + + \sa text +*/ + +QString QTextEdit::context() const +{ + return doc->context(); +} + +/*! + \property QTextEdit::documentTitle + \brief the title of the document parsed from the text. + + For \c PlainText the title will be an empty string. For \c + RichText the title will be the text between the \c{<title>} tags, + if present, otherwise an empty string. +*/ + +QString QTextEdit::documentTitle() const +{ + return doc->attributes()[ "title" ]; +} + +void QTextEdit::makeParagVisible( QTextParagraph *p ) +{ + setContentsPos( contentsX(), QMIN( p->rect().y(), contentsHeight() - visibleHeight() ) ); +} + +/*! + Scrolls the text edit to make the text at the anchor called \a + name visible, if it can be found in the document. If the anchor + isn't found no scrolling will occur. An anchor is defined using + the HTML anchor tag, e.g. \c{<a name="target">}. +*/ + +void QTextEdit::scrollToAnchor( const QString& name ) +{ + if ( !isVisible() ) { + d->scrollToAnchor = name; + return; + } + if ( name.isEmpty() ) + return; + sync(); + QTextCursor cursor( doc ); + QTextParagraph* last = doc->lastParagraph(); + for (;;) { + QTextStringChar* c = cursor.paragraph()->at( cursor.index() ); + if( c->isAnchor() ) { + QString a = c->anchorName(); + if ( a == name || + (a.contains( '#' ) && QStringList::split( '#', a ).contains( name ) ) ) { + setContentsPos( contentsX(), QMIN( cursor.paragraph()->rect().top() + cursor.totalOffsetY(), contentsHeight() - visibleHeight() ) ); + break; + } + } + if ( cursor.paragraph() == last && cursor.atParagEnd() ) + break; + cursor.gotoNextLetter(); + } +} + +#if (QT_VERSION-0 >= 0x040000) +#error "function anchorAt(const QPoint& pos) should be merged into function anchorAt(const QPoint& pos, AnchorAttribute attr)" +#endif + +/*! + \overload + + If there is an anchor at position \a pos (in contents + coordinates), its \c href is returned, otherwise QString::null is + returned. +*/ + +QString QTextEdit::anchorAt( const QPoint& pos ) +{ + return anchorAt(pos, AnchorHref); +} + +/*! + If there is an anchor at position \a pos (in contents + coordinates), the text for attribute \a attr is returned, + otherwise QString::null is returned. +*/ + +QString QTextEdit::anchorAt( const QPoint& pos, AnchorAttribute attr ) +{ + QTextCursor c( doc ); + placeCursor( pos, &c ); + switch(attr) { + case AnchorName: + return c.paragraph()->at( c.index() )->anchorName(); + case AnchorHref: + return c.paragraph()->at( c.index() )->anchorHref(); + } + // incase the compiler is really dumb about determining if a function + // returns something :) + return QString::null; +} + +void QTextEdit::documentWidthChanged( int w ) +{ + resizeContents( QMAX( visibleWidth(), w), contentsHeight() ); +} + +/*! \internal + + This function does nothing +*/ + +void QTextEdit::updateStyles() +{ +} + +void QTextEdit::setDocument( QTextDocument *dc ) +{ + if ( dc == 0 ) { + qWarning( "Q3TextEdit::setDocument() called with null Q3TextDocument pointer" ); + return; + } + if ( dc == doc ) + return; + resetInputContext(); + doc = dc; + delete cursor; + cursor = new QTextCursor( doc ); + clearUndoRedo(); + undoRedoInfo.doc = doc; + lastFormatted = 0; +} + +#ifndef QT_NO_CLIPBOARD + +/*! + Pastes the text with format \a subtype from the clipboard into the + text edit at the current cursor position. The \a subtype can be + "plain" or "html". + + If there is no text with format \a subtype in the clipboard + nothing happens. + + \sa paste() cut() QTextEdit::copy() +*/ + +void QTextEdit::pasteSubType( const QCString &subtype ) +{ +#ifndef QT_NO_MIMECLIPBOARD + QMimeSource *m = QApplication::clipboard()->data( d->clipboard_mode ); + pasteSubType( subtype, m ); +#endif +} + +/*! \internal */ + +void QTextEdit::pasteSubType( const QCString& subtype, QMimeSource *m ) +{ +#ifndef QT_NO_MIME + QCString st = subtype; + if ( subtype != "x-qrichtext" ) + st.prepend( "text/" ); + else + st.prepend( "application/" ); + if ( !m ) + return; + if ( doc->hasSelection( QTextDocument::Standard ) ) + removeSelectedText(); + if ( !QRichTextDrag::canDecode( m ) ) + return; + QString t; + if ( !QRichTextDrag::decode( m, t, st.data(), subtype ) ) + return; + if ( st == "application/x-qrichtext" ) { + int start; + if ( (start = t.find( "<!--StartFragment-->" )) != -1 ) { + start += 20; + int end = t.find( "<!--EndFragment-->" ); + QTextCursor oldC = *cursor; + + // during the setRichTextInternal() call the cursors + // paragraph might get joined with the provious one, so + // the cursors one would get deleted and oldC.paragraph() + // would be a dnagling pointer. To avoid that try to go + // one letter back and later go one forward again. + oldC.gotoPreviousLetter(); + bool couldGoBack = oldC != *cursor; + // first para might get deleted, so remember to reset it + bool wasAtFirst = oldC.paragraph() == doc->firstParagraph(); + + if ( start < end ) + t = t.mid( start, end - start ); + else + t = t.mid( start ); + lastFormatted = cursor->paragraph(); + if ( lastFormatted->prev() ) + lastFormatted = lastFormatted->prev(); + doc->setRichTextInternal( t, cursor ); + + // the first para might have been deleted in + // setRichTextInternal(). To be sure, reset it if + // necessary. + if ( wasAtFirst ) { + int index = oldC.index(); + oldC.setParagraph( doc->firstParagraph() ); + oldC.setIndex( index ); + } + + // if we went back one letter before (see last comment), + // go one forward to point to the right position + if ( couldGoBack ) + oldC.gotoNextLetter(); + + if ( undoEnabled && !isReadOnly() ) { + doc->setSelectionStart( QTextDocument::Temp, oldC ); + doc->setSelectionEnd( QTextDocument::Temp, *cursor ); + + checkUndoRedoInfo( UndoRedoInfo::Insert ); + if ( !undoRedoInfo.valid() ) { + undoRedoInfo.id = oldC.paragraph()->paragId(); + undoRedoInfo.index = oldC.index(); + undoRedoInfo.d->text = QString::null; + } + int oldLen = undoRedoInfo.d->text.length(); + if ( !doc->preProcessor() ) { + QString txt = doc->selectedText( QTextDocument::Temp ); + undoRedoInfo.d->text += txt; + for ( int i = 0; i < (int)txt.length(); ++i ) { + if ( txt[ i ] != '\n' && oldC.paragraph()->at( oldC.index() )->format() ) { + oldC.paragraph()->at( oldC.index() )->format()->addRef(); + undoRedoInfo.d->text. + setFormat( oldLen + i, oldC.paragraph()->at( oldC.index() )->format(), TRUE ); + } + oldC.gotoNextLetter(); + } + } + undoRedoInfo.clear(); + removeSelection( QTextDocument::Temp ); + } + + formatMore(); + setModified(); + emit textChanged(); + repaintChanged(); + ensureCursorVisible(); + return; + } + } else { +#if defined(Q_OS_WIN32) + // Need to convert CRLF to LF + t.replace( "\r\n", "\n" ); +#elif defined(Q_OS_MAC) + //need to convert CR to LF + t.replace( '\r', '\n' ); +#endif + QChar *uc = (QChar *)t.unicode(); + for ( int i=0; (uint) i<t.length(); i++ ) { + if ( uc[ i ] < ' ' && uc[ i ] != '\n' && uc[ i ] != '\t' ) + uc[ i ] = ' '; + } + if ( !t.isEmpty() ) + insert( t, FALSE, TRUE ); + } +#endif //QT_NO_MIME +} + +#ifndef QT_NO_MIMECLIPBOARD +/*! + Prompts the user to choose a type from a list of text types + available, then copies text from the clipboard (if there is any) + into the text edit at the current text cursor position. Any + selected text (in selection 0) is first deleted. +*/ +void QTextEdit::pasteSpecial( const QPoint& pt ) +{ + QCString st = pickSpecial( QApplication::clipboard()->data( d->clipboard_mode ), + TRUE, pt ); + if ( !st.isEmpty() ) + pasteSubType( st ); +} +#endif +#ifndef QT_NO_MIME +QCString QTextEdit::pickSpecial( QMimeSource* ms, bool always_ask, const QPoint& pt ) +{ + if ( ms ) { +#ifndef QT_NO_POPUPMENU + QPopupMenu popup( this, "qt_pickspecial_menu" ); + QString fmt; + int n = 0; + QDict<void> done; + for (int i = 0; !( fmt = ms->format( i ) ).isNull(); i++) { + int semi = fmt.find( ";" ); + if ( semi >= 0 ) + fmt = fmt.left( semi ); + if ( fmt.left( 5 ) == "text/" ) { + fmt = fmt.mid( 5 ); + if ( !done.find( fmt ) ) { + done.insert( fmt,(void*)1 ); + popup.insertItem( fmt, i ); + n++; + } + } + } + if ( n ) { + int i = n ==1 && !always_ask ? popup.idAt( 0 ) : popup.exec( pt ); + if ( i >= 0 ) + return popup.text(i).latin1(); + } +#else + QString fmt; + for (int i = 0; !( fmt = ms->format( i ) ).isNull(); i++) { + int semi = fmt.find( ";" ); + if ( semi >= 0 ) + fmt = fmt.left( semi ); + if ( fmt.left( 5 ) == "text/" ) { + fmt = fmt.mid( 5 ); + return fmt.latin1(); + } + } +#endif + } + return QCString(); +} +#endif // QT_NO_MIME +#endif // QT_NO_CLIPBOARD + +/*! + \enum QTextEdit::WordWrap + + This enum defines the QTextEdit's word wrap modes. + + \value NoWrap Do not wrap the text. + + \value WidgetWidth Wrap the text at the current width of the + widget (this is the default). Wrapping is at whitespace by + default; this can be changed with setWrapPolicy(). + + \value FixedPixelWidth Wrap the text at a fixed number of pixels + from the widget's left side. The number of pixels is set with + wrapColumnOrWidth(). + + \value FixedColumnWidth Wrap the text at a fixed number of + character columns from the widget's left side. The number of + characters is set with wrapColumnOrWidth(). This is useful if you + need formatted text that can also be displayed gracefully on + devices with monospaced fonts, for example a standard VT100 + terminal, where you might set wrapColumnOrWidth() to 80. + + \sa setWordWrap() wordWrap() +*/ + +/*! + \property QTextEdit::wordWrap + \brief the word wrap mode + + The default mode is \c WidgetWidth which causes words to be + wrapped at the right edge of the text edit. Wrapping occurs at + whitespace, keeping whole words intact. If you want wrapping to + occur within words use setWrapPolicy(). If you set a wrap mode of + \c FixedPixelWidth or \c FixedColumnWidth you should also call + setWrapColumnOrWidth() with the width you want. + + \sa WordWrap, wrapColumnOrWidth, wrapPolicy, +*/ + +void QTextEdit::setWordWrap( WordWrap mode ) +{ + if ( wrapMode == mode ) + return; + wrapMode = mode; + switch ( mode ) { + case NoWrap: + document()->formatter()->setWrapEnabled( FALSE ); + document()->formatter()->setWrapAtColumn( -1 ); + doc->setWidth( visibleWidth() ); + doc->setMinimumWidth( -1 ); + doc->invalidate(); + updateContents(); + lastFormatted = doc->firstParagraph(); + interval = 0; + formatMore(); + break; + case WidgetWidth: + document()->formatter()->setWrapEnabled( TRUE ); + document()->formatter()->setWrapAtColumn( -1 ); + doResize(); + break; + case FixedPixelWidth: + document()->formatter()->setWrapEnabled( TRUE ); + document()->formatter()->setWrapAtColumn( -1 ); + if ( wrapWidth < 0 ) + wrapWidth = 200; + setWrapColumnOrWidth( wrapWidth ); + break; + case FixedColumnWidth: + if ( wrapWidth < 0 ) + wrapWidth = 80; + document()->formatter()->setWrapEnabled( TRUE ); + document()->formatter()->setWrapAtColumn( wrapWidth ); + setWrapColumnOrWidth( wrapWidth ); + break; + } +#ifdef QT_TEXTEDIT_OPTIMIZATION + checkOptimMode(); +#endif +} + +QTextEdit::WordWrap QTextEdit::wordWrap() const +{ + return wrapMode; +} + +/*! + \property QTextEdit::wrapColumnOrWidth + \brief the position (in pixels or columns depending on the wrap mode) where text will be wrapped + + If the wrap mode is \c FixedPixelWidth, the value is the number of + pixels from the left edge of the text edit at which text should be + wrapped. If the wrap mode is \c FixedColumnWidth, the value is the + column number (in character columns) from the left edge of the + text edit at which text should be wrapped. + + \sa wordWrap +*/ +void QTextEdit::setWrapColumnOrWidth( int value ) +{ + wrapWidth = value; + if ( wrapMode == FixedColumnWidth ) { + document()->formatter()->setWrapAtColumn( wrapWidth ); + resizeContents( 0, 0 ); + doc->setWidth( visibleWidth() ); + doc->setMinimumWidth( -1 ); + } else if (wrapMode == FixedPixelWidth ) { + document()->formatter()->setWrapAtColumn( -1 ); + resizeContents( wrapWidth, 0 ); + doc->setWidth( wrapWidth ); + doc->setMinimumWidth( wrapWidth ); + } else { + return; + } + doc->invalidate(); + updateContents(); + lastFormatted = doc->firstParagraph(); + interval = 0; + formatMore(); +} + +int QTextEdit::wrapColumnOrWidth() const +{ + if ( wrapMode == WidgetWidth ) + return visibleWidth(); + return wrapWidth; +} + + +/*! + \enum QTextEdit::WrapPolicy + + This enum defines where text can be wrapped in word wrap mode. + + \value AtWhiteSpace Don't use this deprecated value (it is a + synonym for \c AtWordBoundary which you should use instead). + \value Anywhere Break anywhere, including within words. + \value AtWordBoundary Break lines at word boundaries, e.g. spaces or + newlines + \value AtWordOrDocumentBoundary Break lines at whitespace, e.g. + spaces or newlines if possible. Break it anywhere otherwise. + + \sa setWrapPolicy() +*/ + +/*! + \property QTextEdit::wrapPolicy + \brief the word wrap policy, at whitespace or anywhere + + Defines where text can be wrapped when word wrap mode is not \c + NoWrap. The choices are \c AtWordBoundary (the default), \c + Anywhere and \c AtWordOrDocumentBoundary + + \sa wordWrap +*/ + +void QTextEdit::setWrapPolicy( WrapPolicy policy ) +{ + if ( wPolicy == policy ) + return; + wPolicy = policy; + QTextFormatter *formatter; + if ( policy == AtWordBoundary || policy == AtWordOrDocumentBoundary ) { + formatter = new QTextFormatterBreakWords; + formatter->setAllowBreakInWords( policy == AtWordOrDocumentBoundary ); + } else { + formatter = new QTextFormatterBreakInWords; + } + formatter->setWrapAtColumn( document()->formatter()->wrapAtColumn() ); + formatter->setWrapEnabled( document()->formatter()->isWrapEnabled( 0 ) ); + document()->setFormatter( formatter ); + doc->invalidate(); + updateContents(); + lastFormatted = doc->firstParagraph(); + interval = 0; + formatMore(); +} + +QTextEdit::WrapPolicy QTextEdit::wrapPolicy() const +{ + return wPolicy; +} + +/*! + Deletes all the text in the text edit. + + \sa cut() removeSelectedText() setText() +*/ + +void QTextEdit::clear() +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + optimSetText(""); + } else +#endif + { + // make clear undoable + doc->selectAll( QTextDocument::Temp ); + removeSelectedText( QTextDocument::Temp ); + setContentsPos( 0, 0 ); + if ( cursor->isValid() ) + cursor->restoreState(); + doc->clear( TRUE ); + delete cursor; + cursor = new QTextCursor( doc ); + lastFormatted = 0; + } + updateContents(); + + emit cursorPositionChanged( cursor ); + emit cursorPositionChanged( cursor->paragraph()->paragId(), cursor->index() ); +} + +int QTextEdit::undoDepth() const +{ + return document()->undoDepth(); +} + +/*! + \property QTextEdit::length + \brief the number of characters in the text +*/ + +int QTextEdit::length() const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) + return d->od->len; + else +#endif + return document()->length(); +} + +/*! + \property QTextEdit::tabStopWidth + \brief the tab stop width in pixels +*/ + +int QTextEdit::tabStopWidth() const +{ + return document()->tabStopWidth(); +} + +void QTextEdit::setUndoDepth( int d ) +{ + document()->setUndoDepth( d ); +} + +void QTextEdit::setTabStopWidth( int ts ) +{ + document()->setTabStops( ts ); + doc->invalidate(); + lastFormatted = doc->firstParagraph(); + interval = 0; + formatMore(); + updateContents(); +} + +/*! + \reimp +*/ + +QSize QTextEdit::sizeHint() const +{ + // cf. QScrollView::sizeHint() + constPolish(); + int f = 2 * frameWidth(); + int h = fontMetrics().height(); + QSize sz( f, f ); + return sz.expandedTo( QSize(12 * h, 8 * h) ); +} + +void QTextEdit::clearUndoRedo() +{ + if ( !undoEnabled ) + return; + undoRedoInfo.clear(); + emit undoAvailable( doc->commands()->isUndoAvailable() ); + emit redoAvailable( doc->commands()->isRedoAvailable() ); +} + +/*! \internal + \warning In Qt 3.1 we will provide a cleaer API for the + functionality which is provided by this function and in Qt 4.0 this + function will go away. + + This function gets the format of the character at position \a + index in paragraph \a para. Sets \a font to the character's font, \a + color to the character's color and \a verticalAlignment to the + character's vertical alignment. + + Returns FALSE if \a para or \a index is out of range otherwise + returns TRUE. +*/ + +bool QTextEdit::getFormat( int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment ) +{ + if ( !font || !color ) + return FALSE; + QTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return FALSE; + if ( index < 0 || index >= p->length() ) + return FALSE; + *font = p->at( index )->format()->font(); + *color = p->at( index )->format()->color(); + *verticalAlignment = (VerticalAlignment)p->at( index )->format()->vAlign(); + return TRUE; +} + +/*! \internal + \warning In Qt 3.1 we will provide a cleaer API for the + functionality which is provided by this function and in Qt 4.0 this + function will go away. + + This function gets the format of the paragraph \a para. Sets \a + font to the paragraphs's font, \a color to the paragraph's color, \a + verticalAlignment to the paragraph's vertical alignment, \a + alignment to the paragraph's alignment, \a displayMode to the + paragraph's display mode, \a listStyle to the paragraph's list style + (if the display mode is QStyleSheetItem::DisplayListItem) and \a + listDepth to the depth of the list (if the display mode is + QStyleSheetItem::DisplayListItem). + + Returns FALSE if \a para is out of range otherwise returns TRUE. +*/ + +bool QTextEdit::getParagraphFormat( int para, QFont *font, QColor *color, + VerticalAlignment *verticalAlignment, int *alignment, + QStyleSheetItem::DisplayMode *displayMode, + QStyleSheetItem::ListStyle *listStyle, + int *listDepth ) +{ + if ( !font || !color || !alignment || !displayMode || !listStyle ) + return FALSE; + QTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return FALSE; + *font = p->at(0)->format()->font(); + *color = p->at(0)->format()->color(); + *verticalAlignment = (VerticalAlignment)p->at(0)->format()->vAlign(); + *alignment = p->alignment(); + *displayMode = p->isListItem() ? QStyleSheetItem::DisplayListItem : QStyleSheetItem::DisplayBlock; + *listStyle = p->listStyle(); + *listDepth = p->listDepth(); + return TRUE; +} + + + +/*! + This function is called to create a right mouse button popup menu + at the document position \a pos. If you want to create a custom + popup menu, reimplement this function and return the created popup + menu. Ownership of the popup menu is transferred to the caller. + + \warning The QPopupMenu ID values 0-7 are reserved, and they map to the + standard operations. When inserting items into your custom popup menu, be + sure to specify ID values larger than 7. +*/ + +QPopupMenu *QTextEdit::createPopupMenu( const QPoint& pos ) +{ + Q_UNUSED( pos ) +#ifndef QT_NO_POPUPMENU + QPopupMenu *popup = new QPopupMenu( this, "qt_edit_menu" ); + if ( !isReadOnly() ) { + d->id[ IdUndo ] = popup->insertItem( tr( "&Undo" ) + ACCEL_KEY( Z ) ); + d->id[ IdRedo ] = popup->insertItem( tr( "&Redo" ) + ACCEL_KEY( Y ) ); + popup->insertSeparator(); + } +#ifndef QT_NO_CLIPBOARD + if ( !isReadOnly() ) + d->id[ IdCut ] = popup->insertItem( tr( "Cu&t" ) + ACCEL_KEY( X ) ); + d->id[ IdCopy ] = popup->insertItem( tr( "&Copy" ) + ACCEL_KEY( C ) ); + if ( !isReadOnly() ) + d->id[ IdPaste ] = popup->insertItem( tr( "&Paste" ) + ACCEL_KEY( V ) ); +#endif + if ( !isReadOnly() ) { + d->id[ IdClear ] = popup->insertItem( tr( "Clear" ) ); + popup->insertSeparator(); + } +#if defined(Q_WS_X11) + d->id[ IdSelectAll ] = popup->insertItem( tr( "Select All" ) ); +#else + d->id[ IdSelectAll ] = popup->insertItem( tr( "Select All" ) + ACCEL_KEY( A ) ); +#endif + +#ifndef QT_NO_IM + QInputContext *qic = getInputContext(); + if ( qic ) + qic->addMenusTo( popup ); +#endif + + popup->setItemEnabled( d->id[ IdUndo ], !isReadOnly() && doc->commands()->isUndoAvailable() ); + popup->setItemEnabled( d->id[ IdRedo ], !isReadOnly() && doc->commands()->isRedoAvailable() ); +#ifndef QT_NO_CLIPBOARD + popup->setItemEnabled( d->id[ IdCut ], !isReadOnly() && doc->hasSelection( QTextDocument::Standard, TRUE ) ); +#ifdef QT_TEXTEDIT_OPTIMIZATION + popup->setItemEnabled( d->id[ IdCopy ], d->optimMode ? optimHasSelection() : doc->hasSelection( QTextDocument::Standard, TRUE ) ); +#else + popup->setItemEnabled( d->id[ IdCopy ], doc->hasSelection( QTextDocument::Standard, TRUE ) ); +#endif + popup->setItemEnabled( d->id[ IdPaste ], !isReadOnly() && !QApplication::clipboard()->text( d->clipboard_mode ).isEmpty() ); +#endif + const bool isEmptyDocument = (length() == 0); + popup->setItemEnabled( d->id[ IdClear ], !isReadOnly() && !isEmptyDocument ); + popup->setItemEnabled( d->id[ IdSelectAll ], !isEmptyDocument ); + return popup; +#else + return 0; +#endif +} + +/*! \overload + \obsolete + This function is called to create a right mouse button popup menu. + If you want to create a custom popup menu, reimplement this function + and return the created popup menu. Ownership of the popup menu is + transferred to the caller. + + This function is only called if createPopupMenu( const QPoint & ) + returns 0. +*/ + +QPopupMenu *QTextEdit::createPopupMenu() +{ + return 0; +} + +/*! + \reimp +*/ + +void QTextEdit::setFont( const QFont &f ) +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + QScrollView::setFont( f ); + doc->setDefaultFormat( f, doc->formatCollection()->defaultFormat()->color() ); + // recalculate the max string width + QFontMetrics fm(f); + int i, sw; + d->od->maxLineWidth = 0; + for ( i = 0; i < d->od->numLines; i++ ) { + sw = fm.width(d->od->lines[LOGOFFSET(i)]); + if (d->od->maxLineWidth < sw) + d->od->maxLineWidth = sw; + } + resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1); + return; + } +#endif + QScrollView::setFont( f ); + doc->setMinimumWidth( -1 ); + doc->setDefaultFormat( f, doc->formatCollection()->defaultFormat()->color() ); + lastFormatted = doc->firstParagraph(); + formatMore(); + repaintChanged(); +} + +/*! + \fn QTextEdit::zoomIn() + + \overload + + Zooms in on the text by making the base font size one point + larger and recalculating all font sizes to be the new size. This + does not change the size of any images. + + \sa zoomOut() +*/ + +/*! + \fn QTextEdit::zoomOut() + + \overload + + Zooms out on the text by making the base font size one point + smaller and recalculating all font sizes to be the new size. This + does not change the size of any images. + + \sa zoomIn() +*/ + + +/*! + Zooms in on the text by making the base font size \a range + points larger and recalculating all font sizes to be the new size. + This does not change the size of any images. + + \sa zoomOut() +*/ + +void QTextEdit::zoomIn( int range ) +{ + QFont f( QScrollView::font() ); + f.setPointSize( QFontInfo(f).pointSize() + range ); + setFont( f ); +} + +/*! + Zooms out on the text by making the base font size \a range points + smaller and recalculating all font sizes to be the new size. This + does not change the size of any images. + + \sa zoomIn() +*/ + +void QTextEdit::zoomOut( int range ) +{ + QFont f( QScrollView::font() ); + f.setPointSize( QMAX( 1, QFontInfo(f).pointSize() - range ) ); + setFont( f ); +} + +/*! + Zooms the text by making the base font size \a size points and + recalculating all font sizes to be the new size. This does not + change the size of any images. +*/ + +void QTextEdit::zoomTo( int size ) +{ + QFont f( QScrollView::font() ); + f.setPointSize( size ); + setFont( f ); +} + +/*! + QTextEdit is optimized for large amounts text. One of its + optimizations is to format only the visible text, formatting the rest + on demand, e.g. as the user scrolls, so you don't usually need to + call this function. + + In some situations you may want to force the whole text + to be formatted. For example, if after calling setText(), you wanted + to know the height of the document (using contentsHeight()), you + would call this function first. +*/ + +void QTextEdit::sync() +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + QFontMetrics fm( QScrollView::font() ); + resizeContents( d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1 ); + } else +#endif + { + while ( lastFormatted ) { + lastFormatted->format(); + lastFormatted = lastFormatted->next(); + } + resizeContents( contentsWidth(), doc->height() ); + } + updateScrollBars(); +} + +/*! + \reimp +*/ + +void QTextEdit::setEnabled( bool b ) +{ + QScrollView::setEnabled( b ); + if ( textFormat() == PlainText ) { + QTextFormat *f = doc->formatCollection()->defaultFormat(); + f->setColor( colorGroup().text() ); + updateContents(); + } +} + +/*! + Sets the background color of selection number \a selNum to \a back + and specifies whether the text of this selection should be + inverted with \a invertText. + + This only works for \a selNum > 0. The default selection (\a + selNum == 0) gets its attributes from the text edit's + colorGroup(). +*/ + +void QTextEdit::setSelectionAttributes( int selNum, const QColor &back, bool invertText ) +{ + if ( selNum < 1 ) + return; + if ( selNum > doc->numSelections() ) + doc->addSelection( selNum ); + doc->setSelectionColor( selNum, back ); + doc->setInvertSelectionText( selNum, invertText ); +} + +/*! + \reimp +*/ +void QTextEdit::windowActivationChange( bool oldActive ) +{ + if ( oldActive && scrollTimer ) + scrollTimer->stop(); + if ( palette().active() != palette().inactive() ) + updateContents(); + QScrollView::windowActivationChange( oldActive ); +} + +void QTextEdit::setReadOnly( bool b ) +{ + if ( (bool) readonly == b ) + return; + readonly = b; +#ifndef QT_NO_CURSOR + if ( readonly ) + viewport()->setCursor( arrowCursor ); + else + viewport()->setCursor( ibeamCursor ); + setInputMethodEnabled( !readonly ); +#endif +#ifdef QT_TEXTEDIT_OPTIMIZATION + checkOptimMode(); +#endif +} + +/*! + Scrolls to the bottom of the document and does formatting if + required. +*/ + +void QTextEdit::scrollToBottom() +{ + sync(); + setContentsPos( contentsX(), contentsHeight() - visibleHeight() ); +} + +/*! + Returns the rectangle of the paragraph \a para in contents + coordinates, or an invalid rectangle if \a para is out of range. +*/ + +QRect QTextEdit::paragraphRect( int para ) const +{ + QTextEdit *that = (QTextEdit *)this; + that->sync(); + QTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return QRect( -1, -1, -1, -1 ); + return p->rect(); +} + +/*! + Returns the paragraph which is at position \a pos (in contents + coordinates). +*/ + +int QTextEdit::paragraphAt( const QPoint &pos ) const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + QFontMetrics fm( QScrollView::font() ); + int parag = pos.y() / fm.lineSpacing(); + if ( parag <= d->od->numLines ) + return parag; + else + return 0; + } +#endif + QTextCursor c( doc ); + c.place( pos, doc->firstParagraph() ); + if ( c.paragraph() ) + return c.paragraph()->paragId(); + return -1; // should never happen.. +} + +/*! + Returns the index of the character (relative to its paragraph) at + position \a pos (in contents coordinates). If \a para is not 0, + \a *para is set to the character's paragraph. +*/ + +int QTextEdit::charAt( const QPoint &pos, int *para ) const +{ +#ifdef QT_TEXTEDIT_OPTIMIZATION + if ( d->optimMode ) { + int par = paragraphAt( pos ); + if ( para ) + *para = par; + return optimCharIndex( d->od->lines[ LOGOFFSET(par) ], pos.x() ); + } +#endif + QTextCursor c( doc ); + c.place( pos, doc->firstParagraph() ); + if ( c.paragraph() ) { + if ( para ) + *para = c.paragraph()->paragId(); + return c.index(); + } + return -1; // should never happen.. +} + +/*! + Sets the background color of the paragraph \a para to \a bg. +*/ + +void QTextEdit::setParagraphBackgroundColor( int para, const QColor &bg ) +{ + QTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return; + p->setBackgroundColor( bg ); + repaintChanged(); +} + +/*! + Clears the background color of the paragraph \a para, so that the + default color is used again. +*/ + +void QTextEdit::clearParagraphBackground( int para ) +{ + QTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return; + p->clearBackgroundColor(); + repaintChanged(); +} + +/*! + Returns the background color of the paragraph \a para or an + invalid color if \a para is out of range or the paragraph has no + background set +*/ + +QColor QTextEdit::paragraphBackgroundColor( int para ) const +{ + QTextParagraph *p = doc->paragAt( para ); + if ( !p ) + return QColor(); + QColor *c = p->backgroundColor(); + if ( c ) + return *c; + return QColor(); +} + +/*! + \property QTextEdit::undoRedoEnabled + \brief whether undo/redo is enabled + + When changing this property, the undo/redo history is cleared. + + The default is TRUE. +*/ + +void QTextEdit::setUndoRedoEnabled( bool b ) +{ + undoRedoInfo.clear(); + doc->commands()->clear(); + + undoEnabled = b; +} + +bool QTextEdit::isUndoRedoEnabled() const +{ + return undoEnabled; +} + +/*! + Returns TRUE if undo is available; otherwise returns FALSE. +*/ + +bool QTextEdit::isUndoAvailable() const +{ + return undoEnabled && (doc->commands()->isUndoAvailable() || undoRedoInfo.valid()); +} + +/*! + Returns TRUE if redo is available; otherwise returns FALSE. +*/ + +bool QTextEdit::isRedoAvailable() const +{ + return undoEnabled && doc->commands()->isRedoAvailable(); +} + +void QTextEdit::ensureFormatted( QTextParagraph *p ) +{ + while ( !p->isValid() ) { + if ( !lastFormatted ) + return; + formatMore(); + } +} + +/*! \internal */ +void QTextEdit::updateCursor( const QPoint & pos ) +{ + if ( isReadOnly() && linksEnabled() ) { + QTextCursor c = *cursor; + placeCursor( pos, &c, TRUE ); + +#ifndef QT_NO_NETWORKPROTOCOL + bool insideParagRect = TRUE; + if (c.paragraph() == doc->lastParagraph() + && c.paragraph()->rect().y() + c.paragraph()->rect().height() < pos.y()) + insideParagRect = FALSE; + if (insideParagRect && c.paragraph() && c.paragraph()->at( c.index() ) && + c.paragraph()->at( c.index() )->isAnchor()) { + if (!c.paragraph()->at( c.index() )->anchorHref().isEmpty() + && c.index() < c.paragraph()->length() - 1 ) + onLink = c.paragraph()->at( c.index() )->anchorHref(); + else + onLink = QString::null; + + if (!c.paragraph()->at( c.index() )->anchorName().isEmpty() + && c.index() < c.paragraph()->length() - 1 ) + d->onName = c.paragraph()->at( c.index() )->anchorName(); + else + d->onName = QString::null; + + if (!c.paragraph()->at( c.index() )->anchorHref().isEmpty() ) { +#ifndef QT_NO_CURSOR + viewport()->setCursor( onLink.isEmpty() ? arrowCursor : pointingHandCursor ); +#endif + QUrl u( doc->context(), onLink, TRUE ); + emitHighlighted( u.toString( FALSE, FALSE ) ); + } + } else { +#ifndef QT_NO_CURSOR + viewport()->setCursor( isReadOnly() ? arrowCursor : ibeamCursor ); +#endif + onLink = QString::null; + emitHighlighted( QString::null ); + } +#endif + } +} + +/*! + Places the cursor \a c at the character which is closest to position + \a pos (in contents coordinates). If \a c is 0, the default text + cursor is used. + + \sa setCursorPosition() +*/ +void QTextEdit::placeCursor( const QPoint &pos, QTextCursor *c ) +{ + placeCursor( pos, c, FALSE ); +} + +/*! \internal */ +void QTextEdit::clipboardChanged() +{ +#ifndef QT_NO_CLIPBOARD + // don't listen to selection changes + disconnect( QApplication::clipboard(), SIGNAL(selectionChanged()), this, 0); +#endif + selectAll(FALSE); +} + +/*! \property QTextEdit::tabChangesFocus + \brief whether TAB changes focus or is accepted as input + + In some occasions text edits should not allow the user to input + tabulators or change indentation using the TAB key, as this breaks + the focus chain. The default is FALSE. + +*/ + +void QTextEdit::setTabChangesFocus( bool b ) +{ + d->tabChangesFocus = b; +} + +bool QTextEdit::tabChangesFocus() const +{ + return d->tabChangesFocus; +} + +#ifdef QT_TEXTEDIT_OPTIMIZATION +/* Implementation of optimized LogText mode follows */ + +static void qSwap( int * a, int * b ) +{ + if ( !a || !b ) + return; + int tmp = *a; + *a = *b; + *b = tmp; +} + +/*! \internal */ +bool QTextEdit::checkOptimMode() +{ + bool oldMode = d->optimMode; + if ( textFormat() == LogText ) { + setReadOnly( TRUE ); + d->optimMode = TRUE; + } else { + d->optimMode = FALSE; + } + + // when changing mode - try to keep selections and text + if ( oldMode != d->optimMode ) { + if ( d->optimMode ) { + d->od = new QTextEditOptimPrivate; + connect( scrollTimer, SIGNAL( timeout() ), this, SLOT( optimDoAutoScroll() ) ); + disconnect( doc, SIGNAL( minimumWidthChanged(int) ), this, SLOT( documentWidthChanged(int) ) ); + disconnect( scrollTimer, SIGNAL( timeout() ), this, SLOT( autoScrollTimerDone() ) ); + disconnect( formatTimer, SIGNAL( timeout() ), this, SLOT( formatMore() ) ); + optimSetText( doc->originalText() ); + doc->clear(TRUE); + delete cursor; + cursor = new QTextCursor( doc ); + } else { + disconnect( scrollTimer, SIGNAL( timeout() ), this, SLOT( optimDoAutoScroll() ) ); + connect( doc, SIGNAL( minimumWidthChanged(int) ), this, SLOT( documentWidthChanged(int) ) ); + connect( scrollTimer, SIGNAL( timeout() ), this, SLOT( autoScrollTimerDone() ) ); + connect( formatTimer, SIGNAL( timeout() ), this, SLOT( formatMore() ) ); + setText( optimText() ); + delete d->od; + d->od = 0; + } + } + return d->optimMode; +} + +/*! \internal */ +QString QTextEdit::optimText() const +{ + QString str, tmp; + + if ( d->od->len == 0 ) + return str; + + // concatenate all strings + int i; + int offset; + QMapConstIterator<int,QTextEditOptimPrivate::Tag *> it; + QTextEditOptimPrivate::Tag * ftag = 0; + for ( i = 0; i < d->od->numLines; i++ ) { + if ( d->od->lines[ LOGOFFSET(i) ].isEmpty() ) { // CR lines are empty + str += "\n"; + } else { + tmp = d->od->lines[ LOGOFFSET(i) ] + "\n"; + // inject the tags for this line + if ( (it = d->od->tagIndex.find( LOGOFFSET(i) )) != d->od->tagIndex.end() ) + ftag = it.data(); + offset = 0; + while ( ftag && ftag->line == i ) { + tmp.insert( ftag->index + offset, "<" + ftag->tag + ">" ); + offset += ftag->tag.length() + 2; // 2 -> the '<' and '>' chars + ftag = ftag->next; + } + str += tmp; + } + } + return str; +} + +/*! \internal */ +void QTextEdit::optimSetText( const QString &str ) +{ + optimRemoveSelection(); +// this is just too slow - but may have to go in due to compatibility reasons +// if ( str == optimText() ) +// return; + d->od->numLines = 0; + d->od->lines.clear(); + d->od->maxLineWidth = 0; + d->od->len = 0; + d->od->clearTags(); + QFontMetrics fm( QScrollView::font() ); + if ( !(str.isEmpty() || str.isNull() || d->maxLogLines == 0) ) { + QStringList strl = QStringList::split( '\n', str, TRUE ); + int lWidth = 0; + for ( QStringList::Iterator it = strl.begin(); it != strl.end(); ++it ) { + optimParseTags( &*it ); + optimCheckLimit( *it ); + lWidth = fm.width( *it ); + if ( lWidth > d->od->maxLineWidth ) + d->od->maxLineWidth = lWidth; + } + } + resizeContents( d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1 ); + repaintContents(); + emit textChanged(); +} + +/*! \internal + + Append \a tag to the tag list. +*/ +QTextEditOptimPrivate::Tag * QTextEdit::optimAppendTag( int index, const QString & tag ) +{ + QTextEditOptimPrivate::Tag * t = new QTextEditOptimPrivate::Tag, * tmp; + + if ( d->od->tags == 0 ) + d->od->tags = t; + t->bold = t->italic = t->underline = FALSE; + t->line = d->od->numLines; + t->index = index; + t->tag = tag; + t->leftTag = 0; + t->parent = 0; + t->prev = d->od->lastTag; + if ( d->od->lastTag ) + d->od->lastTag->next = t; + t->next = 0; + d->od->lastTag = t; + tmp = d->od->tagIndex[ LOGOFFSET(t->line) ]; + if ( !tmp || (tmp && tmp->index > t->index) ) { + d->od->tagIndex.replace( LOGOFFSET(t->line), t ); + } + return t; +} + + /*! \internal + + Insert \a tag in the tag - according to line and index numbers +*/ + +QTextEditOptimPrivate::Tag *QTextEdit::optimInsertTag(int line, int index, const QString &tag) +{ + QTextEditOptimPrivate::Tag *t = new QTextEditOptimPrivate::Tag, *tmp; + + if (d->od->tags == 0) + d->od->tags = t; + t->bold = t->italic = t->underline = FALSE; + t->line = line; + t->index = index; + t->tag = tag; + t->leftTag = 0; + t->parent = 0; + t->next = 0; + t->prev = 0; + + // find insertion pt. in tag struct. + QMap<int,QTextEditOptimPrivate::Tag *>::ConstIterator it; + if ((it = d->od->tagIndex.find(LOGOFFSET(line))) != d->od->tagIndex.end()) { + tmp = *it; + if (tmp->index >= index) { // the exisiting tag may be placed AFTER the one we want to insert + tmp = tmp->prev; + } else { + while (tmp && tmp->next && tmp->next->line == line && tmp->next->index <= index) + tmp = tmp->next; + } + } else { + tmp = d->od->tags; + while (tmp && tmp->next && tmp->next->line < line) + tmp = tmp->next; + if (tmp == d->od->tags) + tmp = 0; + } + + t->prev = tmp; + t->next = tmp ? tmp->next : 0; + if (t->next) + t->next->prev = t; + if (tmp) + tmp->next = t; + + tmp = d->od->tagIndex[LOGOFFSET(t->line)]; + if (!tmp || (tmp && tmp->index >= t->index)) { + d->od->tagIndex.replace(LOGOFFSET(t->line), t); + } + return t; +} + + +/*! \internal + + Find tags in \a line, remove them from \a line and put them in a + structure. + + A tag is delimited by '<' and '>'. The characters '<', '>' and '&' + are escaped by using '<', '>' and '&'. Left-tags marks + the starting point for formatting, while right-tags mark the ending + point. A right-tag is the same as a left-tag, but with a '/' + appearing before the tag keyword. E.g a valid left-tag: <b>, and + a valid right-tag: </b>. Tags can be nested, but they have to be + closed in the same order as they are opened. E.g: + <font color=red><font color=blue>blue</font>red</font> - is valid, while: + <font color=red><b>bold red</font> just bold</b> - is invalid since the font tag is + closed before the bold tag. Note that a tag does not have to be + closed: '<font color=blue>Lots of text - and then some..' is perfectly valid for + setting all text appearing after the tag to blue. A tag can be used + to change the color of a piece of text, or set one of the following + formatting attributes: bold, italic and underline. These attributes + are set using the <b>, <i> and <u> tags. Example of valid tags: + <font color=red>, </font>, <b>, <u>, <i>, </i>. + Example of valid text: + This is some <font color=red>red text</font>, while this is some <font color=green>green + text</font>. <font color=blue><font color=yellow>This is yellow</font>, while this is + blue.</font> + + Note that only the color attribute of the HTML font tag is supported. + + Limitations: + 1. A tag cannot span several lines. + 2. Very limited error checking - mismatching left/right-tags is the + only thing that is detected. + +*/ +void QTextEdit::optimParseTags( QString * line, int lineNo, int indexOffset ) +{ + int len = line->length(); + int i, startIndex = -1, endIndex = -1, escIndex = -1; + int state = 0; // 0 = outside tag, 1 = inside tag + bool tagOpen, tagClose; + int bold = 0, italic = 0, underline = 0; + QString tagStr; + QPtrStack<QTextEditOptimPrivate::Tag> tagStack; + + for ( i = 0; i < len; i++ ) { + tagOpen = (*line)[i] == '<'; + tagClose = (*line)[i] == '>'; + + // handle '<' and '>' and '&' + if ( (*line)[i] == '&' ) { + escIndex = i; + continue; + } else if ( escIndex != -1 && (*line)[i] == ';' ) { + QString esc = line->mid( escIndex, i - escIndex + 1 ); + QString c; + if ( esc == "<" ) + c = '<'; + else if ( esc == ">" ) + c = '>'; + else if ( esc == "&" ) + c = '&'; + line->replace( escIndex, i - escIndex + 1, c ); + len = line->length(); + i -= i-escIndex; + escIndex = -1; + continue; + } + + if ( state == 0 && tagOpen ) { + state = 1; + startIndex = i; + continue; + } + if ( state == 1 && tagClose ) { + state = 0; + endIndex = i; + if ( !tagStr.isEmpty() ) { + QTextEditOptimPrivate::Tag * tag, * cur, * tmp; + bool format = TRUE; + + if ( tagStr == "b" ) + bold++; + else if ( tagStr == "/b" ) + bold--; + else if ( tagStr == "i" ) + italic++; + else if ( tagStr == "/i" ) + italic--; + else if ( tagStr == "u" ) + underline++; + else if ( tagStr == "/u" ) + underline--; + else + format = FALSE; + if ( lineNo > -1 ) + tag = optimInsertTag( lineNo, startIndex + indexOffset, tagStr ); + else + tag = optimAppendTag( startIndex, tagStr ); + // everything that is not a b, u or i tag is considered + // to be a color tag. + tag->type = format ? QTextEditOptimPrivate::Format + : QTextEditOptimPrivate::Color; + if ( tagStr[0] == '/' ) { + // this is a right-tag - search for the left-tag + // and possible parent tag + cur = tag->prev; + if ( !cur ) { +#ifdef QT_CHECK_RANGE + qWarning( "QTextEdit::optimParseTags: no left-tag for '<%s>' in line %d.", tag->tag.ascii(), tag->line + 1 ); +#endif + return; // something is wrong - give up + } + while ( cur ) { + if ( cur->leftTag ) { // push right-tags encountered + tagStack.push( cur ); + } else { + tmp = tagStack.pop(); + if ( !tmp ) { + if ( (("/" + cur->tag) == tag->tag) || + (tag->tag == "/font" && cur->tag.left(4) == "font") ) { + // set up the left and parent of this tag + tag->leftTag = cur; + tmp = cur->prev; + if ( tmp && tmp->parent ) { + tag->parent = tmp->parent; + } else if ( tmp && !tmp->leftTag ) { + tag->parent = tmp; + } + break; + } else if ( !cur->leftTag ) { +#ifdef QT_CHECK_RANGE + qWarning( "QTextEdit::optimParseTags: mismatching %s-tag for '<%s>' in line %d.", cur->tag[0] == '/' ? "left" : "right", cur->tag.ascii(), cur->line + 1 ); +#endif + return; // something is amiss - give up + } + } + } + cur = cur->prev; + } + } else { + tag->bold = bold > 0; + tag->italic = italic > 0; + tag->underline = underline > 0; + tmp = tag->prev; + while ( tmp && tmp->leftTag ) { + tmp = tmp->leftTag->parent; + } + if ( tmp ) { + tag->bold |= tmp->bold; + tag->italic |= tmp->italic; + tag->underline |= tmp->underline; + } + } + } + if ( startIndex != -1 ) { + int l = (endIndex == -1) ? + line->length() - startIndex : endIndex - startIndex; + line->remove( startIndex, l+1 ); + len = line->length(); + i -= l+1; + } + tagStr = ""; + continue; + } + + if ( state == 1 ) { + tagStr += (*line)[i]; + } + } +} + +// calculate the width of a string in pixels inc. tabs +static int qStrWidth(const QString& str, int tabWidth, const QFontMetrics& fm) +{ + int tabs = str.contains('\t'); + + if (!tabs) + return fm.width(str); + + int newIdx = 0; + int lastIdx = 0; + int strWidth = 0; + int tn; + for (tn = 1; tn <= tabs; ++tn) { + newIdx = str.find('\t', newIdx); + strWidth += fm.width(str.mid(lastIdx, newIdx - lastIdx)); + if (strWidth >= tn * tabWidth) { + int u = tn; + while (strWidth >= u * tabWidth) + ++u; + strWidth = u * tabWidth; + } else { + strWidth = tn * tabWidth; + } + lastIdx = ++newIdx; + } + if ((int)str.length() > newIdx) + strWidth += fm.width(str.mid(newIdx)); + return strWidth; +} + +bool QTextEdit::optimHasBoldMetrics(int line) +{ + QTextEditOptimPrivate::Tag *t; + QMapConstIterator<int,QTextEditOptimPrivate::Tag *> it; + if ((it = d->od->tagIndex.find(line)) != d->od->tagIndex.end()) { + t = *it; + while (t && t->line == line) { + if (t->bold) + return TRUE; + t = t->next; + } + } else if ((t = optimPreviousLeftTag(line)) && t->bold) { + return TRUE; + } + return FALSE; +} + +/*! \internal + + Append \a str to the current text buffer. Parses each line to find + formatting tags. +*/ +void QTextEdit::optimAppend( const QString &str ) +{ + if ( str.isEmpty() || str.isNull() || d->maxLogLines == 0 ) + return; + + QStringList strl = QStringList::split( '\n', str, TRUE ); + QStringList::Iterator it = strl.begin(); + + QFontMetrics fm(QScrollView::font()); + int lWidth = 0; + + for ( ; it != strl.end(); ++it ) { + optimParseTags( &*it ); + optimCheckLimit( *it ); + if (optimHasBoldMetrics(d->od->numLines-1)) { + QFont fnt = QScrollView::font(); + fnt.setBold(TRUE); + fm = QFontMetrics(fnt); + } + lWidth = qStrWidth(*it, tabStopWidth(), fm) + 4; + if ( lWidth > d->od->maxLineWidth ) + d->od->maxLineWidth = lWidth; + } + bool scrollToEnd = contentsY() >= contentsHeight() - visibleHeight(); + resizeContents( d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1 ); + if ( scrollToEnd ) { + updateScrollBars(); + ensureVisible( contentsX(), contentsHeight(), 0, 0 ); + } + // when a max log size is set, the text may not be redrawn because + // the size of the viewport may not have changed + if ( d->maxLogLines > -1 ) + viewport()->update(); + emit textChanged(); +} + + +static void qStripTags(QString *line) +{ + int len = line->length(); + int i, startIndex = -1, endIndex = -1, escIndex = -1; + int state = 0; // 0 = outside tag, 1 = inside tag + bool tagOpen, tagClose; + + for ( i = 0; i < len; i++ ) { + tagOpen = (*line)[i] == '<'; + tagClose = (*line)[i] == '>'; + + // handle '<' and '>' and '&' + if ( (*line)[i] == '&' ) { + escIndex = i; + continue; + } else if ( escIndex != -1 && (*line)[i] == ';' ) { + QString esc = line->mid( escIndex, i - escIndex + 1 ); + QString c; + if ( esc == "<" ) + c = '<'; + else if ( esc == ">" ) + c = '>'; + else if ( esc == "&" ) + c = '&'; + line->replace( escIndex, i - escIndex + 1, c ); + len = line->length(); + i -= i-escIndex; + escIndex = -1; + continue; + } + + if ( state == 0 && tagOpen ) { + state = 1; + startIndex = i; + continue; + } + if ( state == 1 && tagClose ) { + state = 0; + endIndex = i; + if ( startIndex != -1 ) { + int l = (endIndex == -1) ? + line->length() - startIndex : endIndex - startIndex; + line->remove( startIndex, l+1 ); + len = line->length(); + i -= l+1; + } + continue; + } + } +} + +/*! \internal + + Inserts the text into \a line at index \a index. +*/ + +void QTextEdit::optimInsert(const QString& text, int line, int index) +{ + if (text.isEmpty() || d->maxLogLines == 0) + return; + if (line < 0) + line = 0; + if (line > d->od->numLines-1) + line = d->od->numLines-1; + if (index < 0) + index = 0; + if (index > (int) d->od->lines[line].length()) + index = d->od->lines[line].length(); + + QStringList strl = QStringList::split('\n', text, TRUE); + int numNewLines = (int)strl.count() - 1; + QTextEditOptimPrivate::Tag *tag = 0; + QMap<int,QTextEditOptimPrivate::Tag *>::ConstIterator ii; + int x; + + if (numNewLines == 0) { + // Case 1. Fast single line case - just inject it! + QString stripped = text; + qStripTags( &stripped ); + d->od->lines[LOGOFFSET(line)].insert(index, stripped); + // move the tag indices following the insertion pt. + if ((ii = d->od->tagIndex.find(LOGOFFSET(line))) != d->od->tagIndex.end()) { + tag = *ii; + while (tag && (LOGOFFSET(tag->line) == line && tag->index < index)) + tag = tag->next; + while (tag && (LOGOFFSET(tag->line) == line)) { + tag->index += stripped.length(); + tag = tag->next; + } + } + stripped = text; + optimParseTags(&stripped, line, index); + } else if (numNewLines > 0) { + // Case 2. We have at least 1 newline char - split at + // insertion pt. and make room for new lines - complex and slow! + QString left = d->od->lines[LOGOFFSET(line)].left(index); + QString right = d->od->lines[LOGOFFSET(line)].mid(index); + + // rearrange lines for insertion + for (x = d->od->numLines - 1; x > line; x--) + d->od->lines[x + numNewLines] = d->od->lines[x]; + d->od->numLines += numNewLines; + + // fix the tag index and the tag line/index numbers - this + // might take a while.. + for (x = line; x < d->od->numLines; x++) { + if ((ii = d->od->tagIndex.find(LOGOFFSET(line))) != d->od->tagIndex.end()) { + tag = ii.data(); + if (LOGOFFSET(tag->line) == line) + while (tag && (LOGOFFSET(tag->line) == line && tag->index < index)) + tag = tag->next; + } + } + + // relabel affected tags with new line numbers and new index + // positions + while (tag) { + if (LOGOFFSET(tag->line) == line) + tag->index -= index; + tag->line += numNewLines; + tag = tag->next; + } + + // generate a new tag index + d->od->tagIndex.clear(); + tag = d->od->tags; + while (tag) { + if (!((ii = d->od->tagIndex.find(LOGOFFSET(tag->line))) != d->od->tagIndex.end())) + d->od->tagIndex[LOGOFFSET(tag->line)] = tag; + tag = tag->next; + } + + // update the tag indices on the spliced line - needs to be done before new tags are added + QString stripped = strl[strl.count() - 1]; + qStripTags(&stripped); + if ((ii = d->od->tagIndex.find(LOGOFFSET(line + numNewLines))) != d->od->tagIndex.end()) { + tag = *ii; + while (tag && (LOGOFFSET(tag->line) == line + numNewLines)) { + tag->index += stripped.length(); + tag = tag->next; + } + } + + // inject the new lines + QStringList::Iterator it = strl.begin(); + x = line; + int idx; + for (; it != strl.end(); ++it) { + stripped = *it; + qStripTags(&stripped); + if (x == line) { + stripped = left + stripped; + idx = index; + } else { + idx = 0; + } + d->od->lines[LOGOFFSET(x)] = stripped; + optimParseTags(&*it, x++, idx); + } + d->od->lines[LOGOFFSET(x - 1)] += right; + } + // recalculate the pixel width of the longest injected line - + QFontMetrics fm(QScrollView::font()); + int lWidth = 0; + + for (x = line; x < line + numNewLines; x++) { + if (optimHasBoldMetrics(x)) { + QFont fnt = QScrollView::font(); + fnt.setBold(TRUE); + fm = QFontMetrics(fnt); + } + lWidth = fm.width(d->od->lines[x]) + 4; + if (lWidth > d->od->maxLineWidth) + d->od->maxLineWidth = lWidth; + } + resizeContents(d->od->maxLineWidth + 4, d->od->numLines * fm.lineSpacing() + 1); + repaintContents(); + emit textChanged(); +} + + + +/*! \internal + + Returns the first open left-tag appearing before line \a line. + */ +QTextEditOptimPrivate::Tag * QTextEdit::optimPreviousLeftTag( int line ) +{ + QTextEditOptimPrivate::Tag * ftag = 0; + QMapConstIterator<int,QTextEditOptimPrivate::Tag *> it; + if ( (it = d->od->tagIndex.find( LOGOFFSET(line) )) != d->od->tagIndex.end() ) + ftag = it.data(); + if ( !ftag ) { + // start searching for an open tag + ftag = d->od->tags; + while ( ftag ) { + if ( ftag->line > line || ftag->next == 0 ) { + if ( ftag->line > line ) + ftag = ftag->prev; + break; + } + ftag = ftag->next; + } + } else { + ftag = ftag->prev; + } + + if ( ftag ) { + if ( ftag && ftag->parent ) // use the open parent tag + ftag = ftag->parent; + else if ( ftag && ftag->leftTag ) // this is a right-tag with no parent + ftag = 0; + } + return ftag; +} + +/*! \internal + + Set the format for the string starting at index \a start and ending + at \a end according to \a tag. If \a tag is a Format tag, find the + first open color tag appearing before \a tag and use that tag to + color the string. +*/ +void QTextEdit::optimSetTextFormat( QTextDocument * td, QTextCursor * cur, + QTextFormat * f, int start, int end, + QTextEditOptimPrivate::Tag * tag ) +{ + int formatFlags = QTextFormat::Bold | QTextFormat::Italic | + QTextFormat::Underline; + cur->setIndex( start ); + td->setSelectionStart( 0, *cur ); + cur->setIndex( end ); + td->setSelectionEnd( 0, *cur ); + QStyleSheetItem * ssItem = styleSheet()->item( tag->tag ); + if ( !ssItem || tag->type == QTextEditOptimPrivate::Format ) { + f->setBold( tag->bold ); + f->setItalic( tag->italic ); + f->setUnderline( tag->underline ); + if ( tag->type == QTextEditOptimPrivate::Format ) { + // check to see if there are any open color tags prior to + // this format tag + tag = tag->prev; + while ( tag && (tag->type == QTextEditOptimPrivate::Format || + tag->leftTag) ) { + tag = tag->leftTag ? tag->parent : tag->prev; + } + } + if ( tag ) { + QString col = tag->tag.simplifyWhiteSpace(); + if ( col.left( 10 ) == "font color" ) { + int i = col.find( '=', 10 ); + col = col.mid( i + 1 ).simplifyWhiteSpace(); + if ( col[0] == '\"' ) + col = col.mid( 1, col.length() - 2 ); + } + QColor color = QColor( col ); + if ( color.isValid() ) { + formatFlags |= QTextFormat::Color; + f->setColor( color ); + } + } + } else { // use the stylesheet tag definition + if ( ssItem->color().isValid() ) { + formatFlags |= QTextFormat::Color; + f->setColor( ssItem->color() ); + } + f->setBold( ssItem->fontWeight() == QFont::Bold ); + f->setItalic( ssItem->fontItalic() ); + f->setUnderline( ssItem->fontUnderline() ); + } + td->setFormat( 0, f, formatFlags ); + td->removeSelection( 0 ); +} + +/*! \internal */ +void QTextEdit::optimDrawContents( QPainter * p, int clipx, int clipy, + int clipw, int cliph ) +{ + QFontMetrics fm( QScrollView::font() ); + int startLine = clipy / fm.lineSpacing(); + + // we always have to fetch at least two lines for drawing because the + // painter may be translated so that parts of two lines cover the area + // of a single line + int nLines = (cliph / fm.lineSpacing()) + 2; + int endLine = startLine + nLines; + + if ( startLine >= d->od->numLines ) + return; + if ( (startLine + nLines) > d->od->numLines ) + nLines = d->od->numLines - startLine; + + int i = 0; + QString str; + for ( i = startLine; i < (startLine + nLines); i++ ) + str.append( d->od->lines[ LOGOFFSET(i) ] + "\n" ); + + QTextDocument * td = new QTextDocument( 0 ); + td->setDefaultFormat( QScrollView::font(), QColor() ); + td->setPlainText( str ); + td->setFormatter( new QTextFormatterBreakWords ); // deleted by QTextDoc + td->formatter()->setWrapEnabled( FALSE ); + td->setTabStops(doc->tabStopWidth()); + + // get the current text color from the current format + td->selectAll( QTextDocument::Standard ); + QTextFormat f; + f.setColor( colorGroup().text() ); + f.setFont( QScrollView::font() ); + td->setFormat( QTextDocument::Standard, &f, + QTextFormat::Color | QTextFormat::Font ); + td->removeSelection( QTextDocument::Standard ); + + // add tag formatting + if ( d->od->tags ) { + int i = startLine; + QMapConstIterator<int,QTextEditOptimPrivate::Tag *> it; + QTextEditOptimPrivate::Tag * tag = 0, * tmp = 0; + QTextCursor cur( td ); + // Step 1 - find previous left-tag + tmp = optimPreviousLeftTag( i ); + for ( ; i < startLine + nLines; i++ ) { + if ( (it = d->od->tagIndex.find( LOGOFFSET(i) )) != d->od->tagIndex.end() ) + tag = it.data(); + // Step 2 - iterate over tags on the current line + int lastIndex = 0; + while ( tag && tag->line == i ) { + tmp = 0; + if ( tag->prev && !tag->prev->leftTag ) { + tmp = tag->prev; + } else if ( tag->prev && tag->prev->parent ) { + tmp = tag->prev->parent; + } + if ( (tag->index - lastIndex) > 0 && tmp ) { + optimSetTextFormat( td, &cur, &f, lastIndex, tag->index, tmp ); + } + lastIndex = tag->index; + tmp = tag; + tag = tag->next; + } + // Step 3 - color last part of the line - if necessary + if ( tmp && tmp->parent ) + tmp = tmp->parent; + if ( (cur.paragraph()->length()-1 - lastIndex) > 0 && tmp && !tmp->leftTag ) { + optimSetTextFormat( td, &cur, &f, lastIndex, + cur.paragraph()->length() - 1, tmp ); + } + cur.setParagraph( cur.paragraph()->next() ); + } + // useful debug info + // +// tag = d->od->tags; +// qWarning("###"); +// while ( tag ) { +// qWarning( "Tag: %p, parent: %09p, leftTag: %09p, Name: %-15s, ParentName: %s, %d%d%d", tag, +// tag->parent, tag->leftTag, tag->tag.latin1(), tag->parent ? tag->parent->tag.latin1():"<none>", +// tag->bold, tag->italic, tag->underline ); +// tag = tag->next; +// } + } + + // if there is a selection, make sure that the selection in the + // part we need to redraw is set correctly + if ( optimHasSelection() ) { + QTextCursor c1( td ); + QTextCursor c2( td ); + int selStart = d->od->selStart.line; + int idxStart = d->od->selStart.index; + int selEnd = d->od->selEnd.line; + int idxEnd = d->od->selEnd.index; + if ( selEnd < selStart ) { + qSwap( &selStart, &selEnd ); + qSwap( &idxStart, &idxEnd ); + } + if ( selEnd > d->od->numLines-1 ) { + selEnd = d->od->numLines-1; + } + if ( startLine <= selStart && endLine >= selEnd ) { + // case 1: area to paint covers entire selection + int paragS = selStart - startLine; + int paragE = paragS + (selEnd - selStart); + QTextParagraph * parag = td->paragAt( paragS ); + if ( parag ) { + c1.setParagraph( parag ); + if ( td->text( paragS ).length() >= (uint) idxStart ) + c1.setIndex( idxStart ); + } + parag = td->paragAt( paragE ); + if ( parag ) { + c2.setParagraph( parag ); + if ( td->text( paragE ).length() >= (uint) idxEnd ) + c2.setIndex( idxEnd ); + } + } else if ( startLine > selStart && endLine < selEnd ) { + // case 2: area to paint is all part of the selection + td->selectAll( QTextDocument::Standard ); + } else if ( startLine > selStart && endLine >= selEnd && + startLine <= selEnd ) { + // case 3: area to paint starts inside a selection, ends past it + c1.setParagraph( td->firstParagraph() ); + c1.setIndex( 0 ); + int paragE = selEnd - startLine; + QTextParagraph * parag = td->paragAt( paragE ); + if ( parag ) { + c2.setParagraph( parag ); + if ( td->text( paragE ).length() >= (uint) idxEnd ) + c2.setIndex( idxEnd ); + } + } else if ( startLine <= selStart && endLine < selEnd && + endLine > selStart ) { + // case 4: area to paint starts before a selection, ends inside it + int paragS = selStart - startLine; + QTextParagraph * parag = td->paragAt( paragS ); + if ( parag ) { + c1.setParagraph( parag ); + c1.setIndex( idxStart ); + } + c2.setParagraph( td->lastParagraph() ); + c2.setIndex( td->lastParagraph()->string()->toString().length() - 1 ); + + } + // previously selected? + if ( !td->hasSelection( QTextDocument::Standard ) ) { + td->setSelectionStart( QTextDocument::Standard, c1 ); + td->setSelectionEnd( QTextDocument::Standard, c2 ); + } + } + td->doLayout( p, contentsWidth() ); + + // have to align the painter so that partly visible lines are + // drawn at the correct position within the area that needs to be + // painted + int offset = clipy % fm.lineSpacing() + 2; + QRect r( clipx, 0, clipw, cliph + offset ); + p->translate( 0, clipy - offset ); + td->draw( p, r.x(), r.y(), r.width(), r.height(), colorGroup() ); + p->translate( 0, -(clipy - offset) ); + delete td; +} + +/*! \internal */ +void QTextEdit::optimMousePressEvent( QMouseEvent * e ) +{ + if ( e->button() != LeftButton ) + return; + + QFontMetrics fm( QScrollView::font() ); + mousePressed = TRUE; + mousePos = e->pos(); + d->od->selStart.line = e->y() / fm.lineSpacing(); + if ( d->od->selStart.line > d->od->numLines-1 ) { + d->od->selStart.line = d->od->numLines-1; + d->od->selStart.index = d->od->lines[ LOGOFFSET(d->od->numLines-1) ].length(); + } else { + QString str = d->od->lines[ LOGOFFSET(d->od->selStart.line) ]; + d->od->selStart.index = optimCharIndex( str, mousePos.x() ); + } + d->od->selEnd.line = d->od->selStart.line; + d->od->selEnd.index = d->od->selStart.index; + oldMousePos = e->pos(); + repaintContents( FALSE ); +} + +/*! \internal */ +void QTextEdit::optimMouseReleaseEvent( QMouseEvent * e ) +{ + if ( e->button() != LeftButton ) + return; + + if ( scrollTimer->isActive() ) + scrollTimer->stop(); + if ( !inDoubleClick ) { + QFontMetrics fm( QScrollView::font() ); + d->od->selEnd.line = e->y() / fm.lineSpacing(); + if ( d->od->selEnd.line > d->od->numLines-1 ) { + d->od->selEnd.line = d->od->numLines-1; + } + QString str = d->od->lines[ LOGOFFSET(d->od->selEnd.line) ]; + mousePos = e->pos(); + d->od->selEnd.index = optimCharIndex( str, mousePos.x() ); + if ( d->od->selEnd.line < d->od->selStart.line ) { + qSwap( &d->od->selStart.line, &d->od->selEnd.line ); + qSwap( &d->od->selStart.index, &d->od->selEnd.index ); + } else if ( d->od->selStart.line == d->od->selEnd.line && + d->od->selStart.index > d->od->selEnd.index ) { + qSwap( &d->od->selStart.index, &d->od->selEnd.index ); + } + oldMousePos = e->pos(); + repaintContents( FALSE ); + } + if ( mousePressed ) { + mousePressed = FALSE; + copyToClipboard(); + } + + inDoubleClick = FALSE; + emit copyAvailable( optimHasSelection() ); + emit selectionChanged(); +} + +/*! \internal */ +void QTextEdit::optimMouseMoveEvent( QMouseEvent * e ) +{ + mousePos = e->pos(); + optimDoAutoScroll(); + oldMousePos = mousePos; +} + +/*! \internal */ +void QTextEdit::optimDoAutoScroll() +{ + if ( !mousePressed ) + return; + + QFontMetrics fm( QScrollView::font() ); + QPoint pos( mapFromGlobal( QCursor::pos() ) ); + bool doScroll = FALSE; + int xx = contentsX() + pos.x(); + int yy = contentsY() + pos.y(); + + // find out how much we have to scroll in either dir. + if ( pos.x() < 0 || pos.x() > viewport()->width() || + pos.y() < 0 || pos.y() > viewport()->height() ) { + int my = yy; + if ( pos.x() < 0 ) + xx = contentsX() - fm.width( 'w'); + else if ( pos.x() > viewport()->width() ) + xx = contentsX() + viewport()->width() + fm.width('w'); + + if ( pos.y() < 0 ) { + my = contentsY() - 1; + yy = (my / fm.lineSpacing()) * fm.lineSpacing() + 1; + } else if ( pos.y() > viewport()->height() ) { + my = contentsY() + viewport()->height() + 1; + yy = (my / fm.lineSpacing() + 1) * fm.lineSpacing() - 1; + } + d->od->selEnd.line = my / fm.lineSpacing(); + mousePos.setX( xx ); + mousePos.setY( my ); + doScroll = TRUE; + } else { + d->od->selEnd.line = mousePos.y() / fm.lineSpacing(); + } + + if ( d->od->selEnd.line < 0 ) { + d->od->selEnd.line = 0; + } else if ( d->od->selEnd.line > d->od->numLines-1 ) { + d->od->selEnd.line = d->od->numLines-1; + } + + QString str = d->od->lines[ LOGOFFSET(d->od->selEnd.line) ]; + d->od->selEnd.index = optimCharIndex( str, mousePos.x() ); + + // have to have a valid index before generating a paint event + if ( doScroll ) + ensureVisible( xx, yy, 1, 1 ); + + // if the text document is smaller than the heigth of the viewport + // - redraw the whole thing otherwise calculate the rect that + // needs drawing. + if ( d->od->numLines * fm.lineSpacing() < viewport()->height() ) { + repaintContents( contentsX(), contentsY(), width(), height(), FALSE ); + } else { + int h = QABS(mousePos.y() - oldMousePos.y()) + fm.lineSpacing() * 2; + int y; + if ( oldMousePos.y() < mousePos.y() ) { + y = oldMousePos.y() - fm.lineSpacing(); + } else { + // expand paint area for a fully selected line + h += fm.lineSpacing(); + y = mousePos.y() - fm.lineSpacing()*2; + } + if ( y < 0 ) + y = 0; + repaintContents( contentsX(), y, width(), h, FALSE ); + } + + if ( !scrollTimer->isActive() && pos.y() < 0 || pos.y() > height() ) + scrollTimer->start( 100, FALSE ); + else if ( scrollTimer->isActive() && pos.y() >= 0 && pos.y() <= height() ) + scrollTimer->stop(); +} + +/*! \internal + + Returns the index of the character in the string \a str that is + currently under the mouse pointer. +*/ +int QTextEdit::optimCharIndex( const QString &str, int mx ) const +{ + QFontMetrics fm(QScrollView::font()); + uint i = 0; + int dd, dist = 10000000; + int curpos = 0; + int strWidth; + mx = mx - 4; // ### get the real margin from somewhere + + if (!str.contains('\t') && mx > fm.width(str)) + return str.length(); + + while (i < str.length()) { + strWidth = qStrWidth(str.left(i), tabStopWidth(), fm); + dd = strWidth - mx; + if (QABS(dd) <= dist) { + dist = QABS(dd); + if (mx >= strWidth) + curpos = i; + } + ++i; + } + return curpos; +} + +/*! \internal */ +void QTextEdit::optimSelectAll() +{ + d->od->selStart.line = d->od->selStart.index = 0; + d->od->selEnd.line = d->od->numLines - 1; + d->od->selEnd.index = d->od->lines[ LOGOFFSET(d->od->selEnd.line) ].length(); + + repaintContents( FALSE ); + emit copyAvailable( optimHasSelection() ); + emit selectionChanged(); +} + +/*! \internal */ +void QTextEdit::optimRemoveSelection() +{ + d->od->selStart.line = d->od->selEnd.line = -1; + d->od->selStart.index = d->od->selEnd.index = -1; + repaintContents( FALSE ); +} + +/*! \internal */ +void QTextEdit::optimSetSelection( int startLine, int startIdx, + int endLine, int endIdx ) +{ + d->od->selStart.line = startLine; + d->od->selEnd.line = endLine; + d->od->selStart.index = startIdx; + d->od->selEnd.index = endIdx; +} + +/*! \internal */ +bool QTextEdit::optimHasSelection() const +{ + if ( d->od->selStart.line != d->od->selEnd.line || + d->od->selStart.index != d->od->selEnd.index ) + return TRUE; + return FALSE; +} + +/*! \internal */ +QString QTextEdit::optimSelectedText() const +{ + QString str; + + if ( !optimHasSelection() ) + return str; + + // concatenate all strings + if ( d->od->selStart.line == d->od->selEnd.line ) { + str = d->od->lines[ LOGOFFSET(d->od->selEnd.line) ].mid( d->od->selStart.index, + d->od->selEnd.index - d->od->selStart.index ); + } else { + int i = d->od->selStart.line; + str = d->od->lines[ LOGOFFSET(i) ].right( d->od->lines[ LOGOFFSET(i) ].length() - + d->od->selStart.index ) + "\n"; + i++; + for ( ; i < d->od->selEnd.line; i++ ) { + if ( d->od->lines[ LOGOFFSET(i) ].isEmpty() ) // CR lines are empty + str += "\n"; + else + str += d->od->lines[ LOGOFFSET(i) ] + "\n"; + } + str += d->od->lines[ LOGOFFSET(d->od->selEnd.line) ].left( d->od->selEnd.index ); + } + return str; +} + +/*! \internal */ +bool QTextEdit::optimFind( const QString & expr, bool cs, bool /*wo*/, + bool fw, int * para, int * index ) +{ + bool found = FALSE; + int parag = para ? *para : d->od->search.line, + idx = index ? *index : d->od->search.index, i; + + if ( d->od->len == 0 ) + return FALSE; + + for ( i = parag; fw ? i < d->od->numLines : i >= 0; fw ? i++ : i-- ) { + idx = fw ? d->od->lines[ LOGOFFSET(i) ].find( expr, idx, cs ) : + d->od->lines[ LOGOFFSET(i) ].findRev( expr, idx, cs ); + if ( idx != -1 ) { + found = TRUE; + break; + } else if ( fw ) + idx = 0; + } + + if ( found ) { + if ( index ) + *index = idx; + if ( para ) + *para = i; + d->od->search.index = idx + 1; + d->od->search.line = i; + optimSetSelection( i, idx, i, idx + expr.length() ); + QFontMetrics fm( QScrollView::font() ); + int h = fm.lineSpacing(); + int x = fm.width( d->od->lines[ LOGOFFSET(i) ].left( idx + expr.length()) ) + 4; + ensureVisible( x, i * h + h / 2, 1, h / 2 + 2 ); + repaintContents(); // could possibly be optimized + } + return found; +} + +/*! \reimp */ +void QTextEdit::polish() +{ + // this will ensure that the last line is visible if text have + // been added to the widget before it is shown + if ( d->optimMode ) + scrollToBottom(); + QWidget::polish(); +} + +/*! + Sets the maximum number of lines a QTextEdit can hold in \c + LogText mode to \a limit. If \a limit is -1 (the default), this + signifies an unlimited number of lines. + + \warning Never use formatting tags that span more than one line + when the maximum log lines is set. When lines are removed from the + top of the buffer it could result in an unbalanced tag pair, i.e. + the left formatting tag is removed before the right one. + */ +void QTextEdit::setMaxLogLines( int limit ) +{ + d->maxLogLines = limit; + if ( d->maxLogLines < -1 ) + d->maxLogLines = -1; + if ( d->maxLogLines == -1 ) + d->logOffset = 0; +} + +/*! + Returns the maximum number of lines QTextEdit can hold in \c + LogText mode. By default the number of lines is unlimited, which + is signified by a value of -1. + */ +int QTextEdit::maxLogLines() +{ + return d->maxLogLines; +} + +/*! + Check if the number of lines in the buffer is limited, and uphold + that limit when appending new lines. + */ +void QTextEdit::optimCheckLimit( const QString& str ) +{ + if ( d->maxLogLines > -1 && d->maxLogLines <= d->od->numLines ) { + // NB! Removing the top line in the buffer will potentially + // destroy the structure holding the formatting tags - if line + // spanning tags are used. + QTextEditOptimPrivate::Tag *t = d->od->tags, *tmp, *itr; + QPtrList<QTextEditOptimPrivate::Tag> lst; + while ( t ) { + t->line -= 1; + // unhook the ptr from the tag structure + if ( ((uint) LOGOFFSET(t->line) < (uint) d->logOffset && + (uint) LOGOFFSET(t->line) < (uint) LOGOFFSET(d->od->numLines) && + (uint) LOGOFFSET(d->od->numLines) > (uint) d->logOffset) ) + { + if ( t->prev ) + t->prev->next = t->next; + if ( t->next ) + t->next->prev = t->prev; + if ( d->od->tags == t ) + d->od->tags = t->next; + if ( d->od->lastTag == t ) { + if ( t->prev ) + d->od->lastTag = t->prev; + else + d->od->lastTag = d->od->tags; + } + tmp = t; + t = t->next; + lst.append( tmp ); + delete tmp; + } else { + t = t->next; + } + } + // Remove all references to the ptrs we just deleted + itr = d->od->tags; + while ( itr ){ + for ( tmp = lst.first(); tmp; tmp = lst.next() ) { + if ( itr->parent == tmp ) + itr->parent = 0; + if ( itr->leftTag == tmp ) + itr->leftTag = 0; + } + itr = itr->next; + } + // ...in the tag index as well + QMapIterator<int, QTextEditOptimPrivate::Tag *> idx; + if ( (idx = d->od->tagIndex.find( d->logOffset )) != d->od->tagIndex.end() ) + d->od->tagIndex.remove( idx ); + + QMapIterator<int,QString> it; + if ( (it = d->od->lines.find( d->logOffset )) != d->od->lines.end() ) { + d->od->len -= (*it).length(); + d->od->lines.remove( it ); + d->od->numLines--; + d->logOffset = LOGOFFSET(1); + } + } + d->od->len += str.length(); + d->od->lines[ LOGOFFSET(d->od->numLines++) ] = str; +} + +#endif // QT_TEXTEDIT_OPTIMIZATION + +/*! + \property QTextEdit::autoFormatting + \brief the enabled set of auto formatting features + + The value can be any combination of the values in the \c + AutoFormatting enum. The default is \c AutoAll. Choose \c AutoNone + to disable all automatic formatting. + + Currently, the only automatic formatting feature provided is \c + AutoBulletList; future versions of Qt may offer more. +*/ + +void QTextEdit::setAutoFormatting( uint features ) +{ + d->autoFormatting = features; +} + +uint QTextEdit::autoFormatting() const +{ + return d->autoFormatting; +} + +/*! + Returns the QSyntaxHighlighter set on this QTextEdit. 0 is + returned if no syntax highlighter is set. + */ +QSyntaxHighlighter * QTextEdit::syntaxHighlighter() const +{ + if (document()->preProcessor()) + return ((QSyntaxHighlighterInternal *) document()->preProcessor())->highlighter; + else + return 0; +} + +#endif //QT_NO_TEXTEDIT diff --git a/src/widgets/qtextedit.h b/src/widgets/qtextedit.h new file mode 100644 index 0000000..c015c2c --- /dev/null +++ b/src/widgets/qtextedit.h @@ -0,0 +1,616 @@ +/**************************************************************************** +** +** Definition of the QTextEdit class +** +** Created : 990101 +** +** 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. +** +**********************************************************************/ + +#ifndef QTEXTEDIT_H +#define QTEXTEDIT_H + +#ifndef QT_H +#include "qscrollview.h" +#include "qstylesheet.h" +#include "qptrvector.h" +#include "qvaluelist.h" +#include "qptrlist.h" +#endif // QT_H + +#ifndef QT_NO_TEXTEDIT +// uncomment below to enable optimization mode - also uncomment the +// optimDoAutoScroll() private slot since moc ignores #ifdefs.. +#define QT_TEXTEDIT_OPTIMIZATION + +class QPainter; +class QTextDocument; +class QTextCursor; +class QKeyEvent; +class QResizeEvent; +class QMouseEvent; +class QTimer; +class QTextString; +class QTextCommand; +class QTextParagraph; +class QTextFormat; +class QFont; +class QColor; +class QTextEdit; +class QTextBrowser; +class QTextString; +struct QUndoRedoInfoPrivate; +class QPopupMenu; +class QTextEditPrivate; +class QSyntaxHighlighter; + +#ifdef QT_TEXTEDIT_OPTIMIZATION +class QTextEditOptimPrivate +{ +public: + // Note: no left-tag has any value for leftTag or parent, and + // no right-tag has any formatting flags set. + enum TagType { Color = 0, Format = 1 }; + struct Tag { + TagType type:2; + bool bold:1; + bool italic:1; + bool underline:1; + int line; + int index; + Tag * leftTag; // ptr to left-tag in a left-right tag pair + Tag * parent; // ptr to parent left-tag in a nested tag + Tag * prev; + Tag * next; + QString tag; + }; + QTextEditOptimPrivate() + { + len = numLines = maxLineWidth = 0; + selStart.line = selStart.index = -1; + selEnd.line = selEnd.index = -1; + search.line = search.index = 0; + tags = lastTag = 0; + } + void clearTags() + { + Tag * itr = tags; + while ( tags ) { + itr = tags; + tags = tags->next; + delete itr; + } + tags = lastTag = 0; + tagIndex.clear(); + } + ~QTextEditOptimPrivate() + { + clearTags(); + } + int len; + int numLines; + int maxLineWidth; + struct Selection { + int line; + int index; + }; + Selection selStart, selEnd, search; + Tag * tags, * lastTag; + QMap<int, QString> lines; + QMap<int, Tag *> tagIndex; +}; +#endif + +class Q_EXPORT QTextEdit : public QScrollView +{ + friend class QTextBrowser; + friend class QSyntaxHighlighter; + + Q_OBJECT + Q_ENUMS( WordWrap WrapPolicy ) + Q_SETS( AutoFormatting ) + Q_PROPERTY( TextFormat textFormat READ textFormat WRITE setTextFormat ) + Q_PROPERTY( QString text READ text WRITE setText ) + Q_PROPERTY( QBrush paper READ paper WRITE setPaper ) + Q_PROPERTY( bool linkUnderline READ linkUnderline WRITE setLinkUnderline ) + Q_PROPERTY( QString documentTitle READ documentTitle ) + Q_PROPERTY( int length READ length ) + Q_PROPERTY( WordWrap wordWrap READ wordWrap WRITE setWordWrap ) + Q_PROPERTY( int wrapColumnOrWidth READ wrapColumnOrWidth WRITE setWrapColumnOrWidth ) + Q_PROPERTY( WrapPolicy wrapPolicy READ wrapPolicy WRITE setWrapPolicy ) + Q_PROPERTY( bool hasSelectedText READ hasSelectedText ) + Q_PROPERTY( QString selectedText READ selectedText ) + Q_PROPERTY( int undoDepth READ undoDepth WRITE setUndoDepth ) + Q_PROPERTY( bool overwriteMode READ isOverwriteMode WRITE setOverwriteMode ) + Q_PROPERTY( bool modified READ isModified WRITE setModified DESIGNABLE false ) + Q_PROPERTY( bool readOnly READ isReadOnly WRITE setReadOnly ) + Q_PROPERTY( bool undoRedoEnabled READ isUndoRedoEnabled WRITE setUndoRedoEnabled ) + Q_PROPERTY( int tabStopWidth READ tabStopWidth WRITE setTabStopWidth ) + Q_PROPERTY( bool tabChangesFocus READ tabChangesFocus WRITE setTabChangesFocus ) + Q_PROPERTY( AutoFormatting autoFormatting READ autoFormatting WRITE setAutoFormatting ) + +public: + enum WordWrap { + NoWrap, + WidgetWidth, + FixedPixelWidth, + FixedColumnWidth + }; + + enum WrapPolicy { + AtWordBoundary, + AtWhiteSpace = AtWordBoundary, // AtWhiteSpace is deprecated + Anywhere, + AtWordOrDocumentBoundary + }; + + enum AutoFormatting { + AutoNone = 0, + AutoBulletList = 0x00000001, + AutoAll = 0xffffffff + }; + + enum KeyboardAction { + ActionBackspace, + ActionDelete, + ActionReturn, + ActionKill, + ActionWordBackspace, + ActionWordDelete + }; + + enum CursorAction { + MoveBackward, + MoveForward, + MoveWordBackward, + MoveWordForward, + MoveUp, + MoveDown, + MoveLineStart, + MoveLineEnd, + MoveHome, + MoveEnd, + MovePgUp, + MovePgDown + }; + + enum VerticalAlignment { + AlignNormal, + AlignSuperScript, + AlignSubScript + }; + + enum TextInsertionFlags { + RedoIndentation = 0x0001, + CheckNewLines = 0x0002, + RemoveSelected = 0x0004, + AsIMCompositionText = 0x0008, // internal use + WithIMSelection = 0x0010 // internal use + }; + + QTextEdit( const QString& text, const QString& context = QString::null, + QWidget* parent=0, const char* name=0); + QTextEdit( QWidget* parent=0, const char* name=0 ); + virtual ~QTextEdit(); + void setPalette( const QPalette & ); + + QString text() const; + QString text( int para ) const; + TextFormat textFormat() const; + QString context() const; + QString documentTitle() const; + + void getSelection( int *paraFrom, int *indexFrom, + int *paraTo, int *indexTo, int selNum = 0 ) const; + virtual bool find( const QString &expr, bool cs, bool wo, bool forward = TRUE, + int *para = 0, int *index = 0 ); + + int paragraphs() const; + int lines() const; + int linesOfParagraph( int para ) const; + int lineOfChar( int para, int chr ); + int length() const; + QRect paragraphRect( int para ) const; + int paragraphAt( const QPoint &pos ) const; + int charAt( const QPoint &pos, int *para ) const; + int paragraphLength( int para ) const; + + QStyleSheet* styleSheet() const; +#ifndef QT_NO_MIME + QMimeSourceFactory* mimeSourceFactory() const; +#endif + QBrush paper() const; + bool linkUnderline() const; + + int heightForWidth( int w ) const; + + bool hasSelectedText() const; + QString selectedText() const; + bool isUndoAvailable() const; + bool isRedoAvailable() const; + + WordWrap wordWrap() const; + int wrapColumnOrWidth() const; + WrapPolicy wrapPolicy() const; + + int tabStopWidth() const; + + QString anchorAt( const QPoint& pos ); + QString anchorAt( const QPoint& pos, AnchorAttribute a ); + + QSize sizeHint() const; + + bool isReadOnly() const { return readonly; } + + void getCursorPosition( int *parag, int *index ) const; + + bool isModified() const; + bool italic() const; + bool bold() const; + bool underline() const; + QString family() const; + int pointSize() const; + QColor color() const; + QFont font() const; + QFont currentFont() const; + int alignment() const; + int undoDepth() const; + + // do not use, will go away + virtual bool getFormat( int para, int index, QFont *font, QColor *color, VerticalAlignment *verticalAlignment ); + // do not use, will go away + virtual bool getParagraphFormat( int para, QFont *font, QColor *color, + VerticalAlignment *verticalAlignment, int *alignment, + QStyleSheetItem::DisplayMode *displayMode, + QStyleSheetItem::ListStyle *listStyle, + int *listDepth ); + + + bool isOverwriteMode() const { return overWrite; } + QColor paragraphBackgroundColor( int para ) const; + + bool isUndoRedoEnabled() const; + bool eventFilter( QObject *o, QEvent *e ); + bool tabChangesFocus() const; + + void setAutoFormatting( uint features ); + uint autoFormatting() const; + QSyntaxHighlighter *syntaxHighlighter() const; + +public slots: + void setEnabled( bool ); +#ifndef QT_NO_MIME + virtual void setMimeSourceFactory( QMimeSourceFactory* factory ); +#endif + virtual void setStyleSheet( QStyleSheet* styleSheet ); + virtual void scrollToAnchor( const QString& name ); + virtual void setPaper( const QBrush& pap ); + virtual void setLinkUnderline( bool ); + + virtual void setWordWrap( WordWrap mode ); + virtual void setWrapColumnOrWidth( int ); + virtual void setWrapPolicy( WrapPolicy policy ); + + virtual void copy(); + virtual void append( const QString& text ); + + void setText( const QString &txt ) { setText( txt, QString::null ); } + virtual void setText( const QString &txt, const QString &context ); + virtual void setTextFormat( TextFormat f ); + + virtual void selectAll( bool select = TRUE ); + virtual void setTabStopWidth( int ts ); + virtual void zoomIn( int range ); + virtual void zoomIn() { zoomIn( 1 ); } + virtual void zoomOut( int range ); + virtual void zoomOut() { zoomOut( 1 ); } + virtual void zoomTo( int size ); + + virtual void sync(); + virtual void setReadOnly( bool b ); + + virtual void undo(); + virtual void redo(); + virtual void cut(); + virtual void paste(); +#ifndef QT_NO_CLIPBOARD + virtual void pasteSubType( const QCString &subtype ); +#endif + virtual void clear(); + virtual void del(); + virtual void indent(); + virtual void setItalic( bool b ); + virtual void setBold( bool b ); + virtual void setUnderline( bool b ); + virtual void setFamily( const QString &f ); + virtual void setPointSize( int s ); + virtual void setColor( const QColor &c ); + virtual void setFont( const QFont &f ); + virtual void setVerticalAlignment( VerticalAlignment a ); + virtual void setAlignment( int a ); + + // do not use, will go away + virtual void setParagType( QStyleSheetItem::DisplayMode dm, QStyleSheetItem::ListStyle listStyle ); + + virtual void setCursorPosition( int parag, int index ); + virtual void setSelection( int parag_from, int index_from, int parag_to, int index_to, int selNum = 0 ); + virtual void setSelectionAttributes( int selNum, const QColor &back, bool invertText ); + virtual void setModified( bool m ); + virtual void resetFormat(); + virtual void setUndoDepth( int d ); + virtual void setFormat( QTextFormat *f, int flags ); + virtual void ensureCursorVisible(); + virtual void placeCursor( const QPoint &pos, QTextCursor *c = 0 ); + virtual void moveCursor( CursorAction action, bool select ); + virtual void doKeyboardAction( KeyboardAction action ); + virtual void removeSelectedText( int selNum = 0 ); + virtual void removeSelection( int selNum = 0 ); + virtual void setCurrentFont( const QFont &f ); + virtual void setOverwriteMode( bool b ) { overWrite = b; } + + virtual void scrollToBottom(); + + void insert( const QString &text, uint insertionFlags = CheckNewLines | RemoveSelected ); // ## virtual in 4.0 + + // obsolete + virtual void insert( const QString &text, bool, bool = TRUE, bool = TRUE ); + + virtual void insertAt( const QString &text, int para, int index ); + virtual void removeParagraph( int para ); + virtual void insertParagraph( const QString &text, int para ); + + virtual void setParagraphBackgroundColor( int para, const QColor &bg ); + virtual void clearParagraphBackground( int para ); + + virtual void setUndoRedoEnabled( bool b ); + void setTabChangesFocus( bool b ); // ### make virtual in 4.0 + +#ifdef QT_TEXTEDIT_OPTIMIZATION + void polish(); + void setMaxLogLines( int numLines ); + int maxLogLines(); +#endif + +signals: + void textChanged(); + void selectionChanged(); + void copyAvailable( bool ); + void undoAvailable( bool yes ); + void redoAvailable( bool yes ); + void currentFontChanged( const QFont &f ); + void currentColorChanged( const QColor &c ); + void currentAlignmentChanged( int a ); + void currentVerticalAlignmentChanged( VerticalAlignment a ); + void cursorPositionChanged( QTextCursor *c ); + void cursorPositionChanged( int para, int pos ); + void returnPressed(); + void modificationChanged( bool m ); + void clicked( int parag, int index ); + void doubleClicked( int parag, int index ); + +protected: + void repaintChanged(); + void updateStyles(); + void drawContents( QPainter *p, int cx, int cy, int cw, int ch ); + bool event( QEvent *e ); + void keyPressEvent( QKeyEvent *e ); + void resizeEvent( QResizeEvent *e ); + void viewportResizeEvent( QResizeEvent* ); + void contentsMousePressEvent( QMouseEvent *e ); + void contentsMouseMoveEvent( QMouseEvent *e ); + void contentsMouseReleaseEvent( QMouseEvent *e ); + void contentsMouseDoubleClickEvent( QMouseEvent *e ); +#ifndef QT_NO_WHEELEVENT + void contentsWheelEvent( QWheelEvent *e ); +#endif + void imStartEvent( QIMEvent * ); + void imComposeEvent( QIMEvent * ); + void imEndEvent( QIMEvent * ); +#ifndef QT_NO_DRAGANDDROP + void contentsDragEnterEvent( QDragEnterEvent *e ); + void contentsDragMoveEvent( QDragMoveEvent *e ); + void contentsDragLeaveEvent( QDragLeaveEvent *e ); + void contentsDropEvent( QDropEvent *e ); +#endif + void contentsContextMenuEvent( QContextMenuEvent *e ); + bool sendMouseEventToInputContext( QMouseEvent *e ); + bool focusNextPrevChild( bool next ); + QTextDocument *document() const; + QTextCursor *textCursor() const; + void setDocument( QTextDocument *doc ); + virtual QPopupMenu *createPopupMenu( const QPoint& pos ); + virtual QPopupMenu *createPopupMenu(); + void drawCursor( bool visible ); + + void windowActivationChange( bool ); + +protected slots: + virtual void doChangeInterval(); + void sliderReleased(); // ### make virtual in 4.0 +#if (QT_VERSION >= 0x040000) +#error "Some functions need to be changed to virtual for Qt 4.0" +#endif + +private slots: + void formatMore(); + void doResize(); + void autoScrollTimerDone(); + void blinkCursor(); + void setModified(); + void startDrag(); + void documentWidthChanged( int w ); + void clipboardChanged(); + +private: + struct Q_EXPORT UndoRedoInfo { + enum Type { Invalid, Insert, Delete, Backspace, Return, RemoveSelected, Format, Style, IME }; + + UndoRedoInfo( QTextDocument *dc ); + ~UndoRedoInfo(); + void clear(); + bool valid() const; + + QUndoRedoInfoPrivate *d; + int id; + int index; + int eid; + int eindex; + QTextFormat *format; + int flags; + Type type; + QTextDocument *doc; + QByteArray styleInformation; + }; + +private: + void updateCursor( const QPoint & pos ); + void handleMouseMove( const QPoint& pos ); + void drawContents( QPainter * ); + virtual bool linksEnabled() const { return FALSE; } + void init(); + void checkUndoRedoInfo( UndoRedoInfo::Type t ); + void updateCurrentFormat(); + bool handleReadOnlyKeyEvent( QKeyEvent *e ); + void makeParagVisible( QTextParagraph *p ); + void normalCopy(); + void copyToClipboard(); +#ifndef QT_NO_MIME + QCString pickSpecial(QMimeSource* ms, bool always_ask, const QPoint&); + QTextDrag *dragObject( QWidget *parent = 0 ) const; +#endif +#ifndef QT_NO_MIMECLIPBOARD + void pasteSpecial(const QPoint&); +#endif + void setFontInternal( const QFont &f ); + + virtual void emitHighlighted( const QString & ) {} + virtual void emitLinkClicked( const QString & ) {} + + void readFormats( QTextCursor &c1, QTextCursor &c2, QTextString &text, bool fillStyles = FALSE ); + void clearUndoRedo(); + void paintDocument( bool drawAll, QPainter *p, int cx = -1, int cy = -1, int cw = -1, int ch = -1 ); + void moveCursor( CursorAction action ); + void ensureFormatted( QTextParagraph *p ); + void placeCursor( const QPoint &pos, QTextCursor *c, bool link ); + void updateMicroFocusHint(); + +#ifdef QT_TEXTEDIT_OPTIMIZATION + bool checkOptimMode(); + QString optimText() const; + void optimSetText( const QString &str ); + void optimAppend( const QString &str ); + void optimInsert( const QString &str, int line, int index ); + void optimDrawContents( QPainter * p, int cx, int cy, int cw, int ch ); + void optimMousePressEvent( QMouseEvent * e ); + void optimMouseReleaseEvent( QMouseEvent * e ); + void optimMouseMoveEvent( QMouseEvent * e ); + int optimCharIndex( const QString &str, int mx ) const; + void optimSelectAll(); + void optimRemoveSelection(); + void optimSetSelection( int startLine, int startIdx, int endLine, + int endIdx ); + bool optimHasSelection() const; + QString optimSelectedText() const; + bool optimFind( const QString & str, bool, bool, bool, int *, int * ); + void optimParseTags( QString * str, int lineNo = -1, int indexOffset = 0 ); + QTextEditOptimPrivate::Tag * optimPreviousLeftTag( int line ); + void optimSetTextFormat( QTextDocument *, QTextCursor *, QTextFormat * f, + int, int, QTextEditOptimPrivate::Tag * t ); + QTextEditOptimPrivate::Tag * optimAppendTag( int index, const QString & tag ); + QTextEditOptimPrivate::Tag * optimInsertTag( int line, int index, const QString & tag ); + void optimCheckLimit( const QString& str ); + bool optimHasBoldMetrics( int line ); + +private slots: + void optimDoAutoScroll(); +#endif // QT_TEXTEDIT_OPTIMIZATION + +private: +#ifndef QT_NO_CLIPBOARD + void pasteSubType( const QCString &subtype, QMimeSource *m ); +#endif + +private: + QTextDocument *doc; + QTextCursor *cursor; + QTimer *formatTimer, *scrollTimer, *changeIntervalTimer, *blinkTimer, *dragStartTimer; + QTextParagraph *lastFormatted; + int interval; + UndoRedoInfo undoRedoInfo; + QTextFormat *currentFormat; + int currentAlignment; + QPoint oldMousePos, mousePos; + QPoint dragStartPos; + QString onLink; + WordWrap wrapMode; + WrapPolicy wPolicy; + int wrapWidth; + QString pressedLink; + QTextEditPrivate *d; + bool inDoubleClick : 1; + bool mousePressed : 1; + bool cursorVisible : 1; + bool blinkCursorVisible : 1; + bool readOnly : 1; + bool modified : 1; + bool mightStartDrag : 1; + bool inDnD : 1; + bool readonly : 1; + bool undoEnabled : 1; + bool overWrite : 1; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QTextEdit( const QTextEdit & ); + QTextEdit &operator=( const QTextEdit & ); +#endif +}; + +inline QTextDocument *QTextEdit::document() const +{ + return doc; +} + +inline QTextCursor *QTextEdit::textCursor() const +{ + return cursor; +} + +inline void QTextEdit::setCurrentFont( const QFont &f ) +{ + QTextEdit::setFontInternal( f ); +} + +#endif //QT_NO_TEXTEDIT +#endif //QTEXTVIEW_H diff --git a/src/widgets/qtextview.cpp b/src/widgets/qtextview.cpp new file mode 100644 index 0000000..67276e8 --- /dev/null +++ b/src/widgets/qtextview.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Implementation of the QTextView class +** +** Created : 990101 +** +** 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 "qtextview.h" + +#ifndef QT_NO_TEXTVIEW + +/*! \class QTextView + \brief The QTextView class provides a rich-text viewer. + + \obsolete + + This class wraps a read-only \l QTextEdit. + Use a \l QTextEdit instead, and call setReadOnly(TRUE) + to disable editing. +*/ + +/*! \reimp */ + +QTextView::QTextView( const QString& text, const QString& context, + QWidget *parent, const char *name ) + : QTextEdit( text, context, parent, name ) +{ + setReadOnly( TRUE ); +} + +/*! \reimp */ + +QTextView::QTextView( QWidget *parent, const char *name ) + : QTextEdit( parent, name ) +{ + setReadOnly( TRUE ); +} + +/*! \reimp */ + +QTextView::~QTextView() +{ +} + +/*! + \property QTextView::undoDepth + \brief the number of undoable steps +*/ + +/*! + \property QTextView::overwriteMode + \brief whether new text overwrites or pushes aside existing text +*/ + +/*! + \property QTextView::modified + \brief Whether the text view's contents have been modified. +*/ + +/*! + \property QTextView::readOnly + \brief Whether the text view's contents are read only. +*/ + +/*! + \property QTextView::undoRedoEnabled + \brief Whether undo and redo are enabled. +*/ + +#endif diff --git a/src/widgets/qtextview.h b/src/widgets/qtextview.h new file mode 100644 index 0000000..b17874d --- /dev/null +++ b/src/widgets/qtextview.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Definition of the QTextView class +** +** Created : 990101 +** +** 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. +** +**********************************************************************/ + +#ifndef QTEXTVIEW_H +#define QTEXTVIEW_H + +#ifndef QT_H +#include "qtextedit.h" +#endif // QT_H + +#ifndef QT_NO_TEXTVIEW + +class Q_EXPORT QTextView : public QTextEdit +{ + Q_OBJECT + Q_OVERRIDE( int undoDepth DESIGNABLE false SCRIPTABLE false ) + Q_OVERRIDE( bool overwriteMode DESIGNABLE false SCRIPTABLE false ) + Q_OVERRIDE( bool modified SCRIPTABLE false) + Q_OVERRIDE( bool readOnly DESIGNABLE false SCRIPTABLE false ) + Q_OVERRIDE( bool undoRedoEnabled DESIGNABLE false SCRIPTABLE false ) + +public: + QTextView( const QString& text, const QString& context = QString::null, + QWidget* parent=0, const char* name=0); + QTextView( QWidget* parent=0, const char* name=0 ); + + virtual ~QTextView(); + +private: +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + QTextView( const QTextView & ); + QTextView &operator=( const QTextView & ); +#endif +}; + +#endif //QT_NO_TEXTVIEW +#endif //QTEXTVIEW_H diff --git a/src/widgets/qtitlebar.cpp b/src/widgets/qtitlebar.cpp new file mode 100644 index 0000000..c81ea7d --- /dev/null +++ b/src/widgets/qtitlebar.cpp @@ -0,0 +1,671 @@ +/**************************************************************************** +** +** Implementation of some Qt private functions. +** +** Created : 001101 +** +** Copyright (C) 2000-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 "qplatformdefs.h" + +#include "qtitlebar_p.h" + +#ifndef QT_NO_TITLEBAR + +#include <qcursor.h> +#include "qapplication.h" +#include "qstyle.h" +#include "qdatetime.h" +#include "private/qapplication_p.h" +#include "qtooltip.h" +#include "qimage.h" +#include "qtimer.h" +#include "qpainter.h" +#include "qstyle.h" +#include "private/qinternal_p.h" +#ifndef QT_NO_WORKSPACE +#include "qworkspace.h" +#endif +#if defined(Q_WS_WIN) +#include "qt_windows.h" +#endif + +#ifndef QT_NO_TOOLTIP +class QTitleBarTip : public QToolTip +{ +public: + QTitleBarTip( QWidget * parent ) : QToolTip( parent ) { } + + void maybeTip( const QPoint &pos ) + { + if ( !::qt_cast<QTitleBar*>(parentWidget()) ) + return; + QTitleBar *t = (QTitleBar *)parentWidget(); + + QString tipstring; + QStyle::SubControl ctrl = t->style().querySubControl(QStyle::CC_TitleBar, t, pos); + QSize controlSize = t->style().querySubControlMetrics(QStyle::CC_TitleBar, t, ctrl).size(); + + QWidget *window = t->window(); + if ( window ) { + switch(ctrl) { + case QStyle::SC_TitleBarSysMenu: + if ( t->testWFlags( WStyle_SysMenu ) ) + tipstring = QTitleBar::tr( "System Menu" ); + break; + + case QStyle::SC_TitleBarShadeButton: + if ( t->testWFlags( WStyle_Tool ) && t->testWFlags( WStyle_MinMax ) ) + tipstring = QTitleBar::tr( "Shade" ); + break; + + case QStyle::SC_TitleBarUnshadeButton: + if ( t->testWFlags( WStyle_Tool ) && t->testWFlags( WStyle_MinMax ) ) + tipstring = QTitleBar::tr( "Unshade" ); + break; + + case QStyle::SC_TitleBarNormalButton: + case QStyle::SC_TitleBarMinButton: + if ( !t->testWFlags( WStyle_Tool ) && t->testWFlags( WStyle_Minimize ) ) { + if( window->isMinimized() ) + tipstring = QTitleBar::tr( "Normalize" ); + else + tipstring = QTitleBar::tr( "Minimize" ); + } + break; + + case QStyle::SC_TitleBarMaxButton: + if ( !t->testWFlags( WStyle_Tool ) && t->testWFlags( WStyle_Maximize ) ) + tipstring = QTitleBar::tr( "Maximize" ); + break; + + case QStyle::SC_TitleBarCloseButton: + if ( t->testWFlags( WStyle_SysMenu ) ) + tipstring = QTitleBar::tr( "Close" ); + break; + + default: + break; + } + } +#ifndef QT_NO_WIDGET_TOPEXTRA + if ( tipstring.isEmpty() ) { + if ( t->visibleText() != t->caption() ) + tipstring = t->caption(); + } +#endif + if(!tipstring.isEmpty()) + tip( QRect(pos, controlSize), tipstring ); + } +}; +#endif + +class QTitleBarPrivate +{ +public: + QTitleBarPrivate() + : toolTip( 0 ), act( 0 ), window( 0 ), movable( 1 ), pressed( 0 ), autoraise(0) + { + } + + QStyle::SCFlags buttonDown; + QPoint moveOffset; + QToolTip *toolTip; + bool act :1; + QWidget* window; + bool movable :1; + bool pressed :1; + bool autoraise :1; + QString cuttext; +#ifdef QT_NO_WIDGET_TOPEXTRA + QString cap; +#endif +}; + +QTitleBar::QTitleBar(QWidget* w, QWidget* parent, const char* name) + : QWidget( parent, name, WStyle_Customize | WStyle_NoBorder | WNoAutoErase ) +{ + d = new QTitleBarPrivate(); + +#ifndef QT_NO_TOOLTIP + d->toolTip = new QTitleBarTip( this ); +#endif + d->window = w; + d->buttonDown = QStyle::SC_None; + d->act = 0; + if ( w ) { + setWFlags( ((QTitleBar*)w)->getWFlags() | WNoAutoErase ); + if ( w->minimumSize() == w->maximumSize() ) + clearWFlags( WStyle_Maximize ); +#ifndef QT_NO_WIDGET_TOPEXTRA + setCaption( w->caption() ); +#endif + } else { + setWFlags( WStyle_Customize | WNoAutoErase ); + } + + readColors(); + setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed ) ); + setMouseTracking(TRUE); +} + +QTitleBar::~QTitleBar() +{ +#ifndef QT_NO_TOOLTIP + delete d->toolTip; +#endif + + delete d; + d = 0; +} + +#ifdef Q_WS_WIN +extern QRgb qt_colorref2qrgb(COLORREF col); +#endif + +void QTitleBar::readColors() +{ + QPalette pal = palette(); + + bool colorsInitialized = FALSE; + +#ifdef Q_WS_WIN // ask system properties on windows +#ifndef SPI_GETGRADIENTCAPTIONS +#define SPI_GETGRADIENTCAPTIONS 0x1008 +#endif +#ifndef COLOR_GRADIENTACTIVECAPTION +#define COLOR_GRADIENTACTIVECAPTION 27 +#endif +#ifndef COLOR_GRADIENTINACTIVECAPTION +#define COLOR_GRADIENTINACTIVECAPTION 28 +#endif + if ( QApplication::desktopSettingsAware() ) { + pal.setColor( QPalette::Active, QColorGroup::Highlight, qt_colorref2qrgb(GetSysColor(COLOR_ACTIVECAPTION)) ); + pal.setColor( QPalette::Inactive, QColorGroup::Highlight, qt_colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTION)) ); + pal.setColor( QPalette::Active, QColorGroup::HighlightedText, qt_colorref2qrgb(GetSysColor(COLOR_CAPTIONTEXT)) ); + pal.setColor( QPalette::Inactive, QColorGroup::HighlightedText, qt_colorref2qrgb(GetSysColor(COLOR_INACTIVECAPTIONTEXT)) ); + if ( qt_winver != Qt::WV_95 && qt_winver != WV_NT ) { + colorsInitialized = TRUE; + BOOL gradient; + QT_WA( { + SystemParametersInfo( SPI_GETGRADIENTCAPTIONS, 0, &gradient, 0 ); + } , { + SystemParametersInfoA( SPI_GETGRADIENTCAPTIONS, 0, &gradient, 0 ); + } ); + if ( gradient ) { + pal.setColor( QPalette::Active, QColorGroup::Base, qt_colorref2qrgb(GetSysColor(COLOR_GRADIENTACTIVECAPTION)) ); + pal.setColor( QPalette::Inactive, QColorGroup::Base, qt_colorref2qrgb(GetSysColor(COLOR_GRADIENTINACTIVECAPTION)) ); + } else { + pal.setColor( QPalette::Active, QColorGroup::Base, palette().active().highlight() ); + pal.setColor( QPalette::Inactive, QColorGroup::Base, palette().inactive().highlight() ); + } + } + } +#endif // Q_WS_WIN + if ( !colorsInitialized ) { + pal.setColor( QPalette::Active, QColorGroup::Highlight, palette().active().highlight() ); + pal.setColor( QPalette::Active, QColorGroup::Base, palette().active().highlight() ); + pal.setColor( QPalette::Inactive, QColorGroup::Highlight, palette().inactive().dark() ); + pal.setColor( QPalette::Inactive, QColorGroup::Base, palette().inactive().dark() ); + pal.setColor( QPalette::Inactive, QColorGroup::HighlightedText, palette().inactive().background() ); + } + + setPalette( pal ); + setActive( d->act ); +} + +void QTitleBar::mousePressEvent( QMouseEvent * e) +{ + if ( !d->act ) + emit doActivate(); + if ( e->button() == LeftButton ) { + d->pressed = TRUE; + QStyle::SCFlags ctrl = style().querySubControl(QStyle::CC_TitleBar, this, e->pos()); + switch (ctrl) { + case QStyle::SC_TitleBarSysMenu: + if ( testWFlags( WStyle_SysMenu ) && !testWFlags( WStyle_Tool ) ) { + d->buttonDown = QStyle::SC_None; + static QTime* t = 0; + static QTitleBar* tc = 0; + if ( !t ) + t = new QTime; + if ( tc != this || t->elapsed() > QApplication::doubleClickInterval() ) { + emit showOperationMenu(); + t->start(); + tc = this; + } else { + tc = 0; + emit doClose(); + return; + } + } + break; + + case QStyle::SC_TitleBarShadeButton: + case QStyle::SC_TitleBarUnshadeButton: + if ( testWFlags( WStyle_MinMax ) && testWFlags( WStyle_Tool ) ) + d->buttonDown = ctrl; + break; + + case QStyle::SC_TitleBarNormalButton: + if( testWFlags( WStyle_Minimize ) && !testWFlags( WStyle_Tool ) ) + d->buttonDown = ctrl; + break; + + case QStyle::SC_TitleBarMinButton: + if( testWFlags( WStyle_Minimize ) && !testWFlags( WStyle_Tool ) ) + d->buttonDown = ctrl; + break; + + case QStyle::SC_TitleBarMaxButton: + if ( testWFlags( WStyle_Maximize ) && !testWFlags( WStyle_Tool ) ) + d->buttonDown = ctrl; + break; + + case QStyle::SC_TitleBarCloseButton: + if ( testWFlags( WStyle_SysMenu ) ) + d->buttonDown = ctrl; + break; + + case QStyle::SC_TitleBarLabel: + d->buttonDown = ctrl; + d->moveOffset = mapToParent( e->pos() ); + break; + + default: + break; + } + repaint( FALSE ); + } else { + d->pressed = FALSE; + } +} + +void QTitleBar::contextMenuEvent( QContextMenuEvent *e ) +{ + QStyle::SCFlags ctrl = style().querySubControl(QStyle::CC_TitleBar, this, e->pos()); + if( ctrl == QStyle::SC_TitleBarLabel || ctrl == QStyle::SC_TitleBarSysMenu ) + emit popupOperationMenu(e->globalPos()); + else + e->ignore(); +} + +void QTitleBar::mouseReleaseEvent( QMouseEvent * e) +{ + if ( e->button() == LeftButton && d->pressed) { + QStyle::SCFlags ctrl = style().querySubControl(QStyle::CC_TitleBar, this, e->pos()); + + if (ctrl == d->buttonDown) { + switch(ctrl) { + case QStyle::SC_TitleBarShadeButton: + case QStyle::SC_TitleBarUnshadeButton: + if( testWFlags( WStyle_MinMax ) && testWFlags( WStyle_Tool ) ) + emit doShade(); + break; + + case QStyle::SC_TitleBarNormalButton: + if( testWFlags( WStyle_MinMax ) && !testWFlags( WStyle_Tool ) ) + emit doNormal(); + break; + + case QStyle::SC_TitleBarMinButton: + if( testWFlags( WStyle_Minimize ) && !testWFlags( WStyle_Tool ) ) + emit doMinimize(); + break; + + case QStyle::SC_TitleBarMaxButton: + if( d->window && testWFlags( WStyle_Maximize ) && !testWFlags( WStyle_Tool ) ) { + if(d->window->isMaximized()) + emit doNormal(); + else + emit doMaximize(); + } + break; + + case QStyle::SC_TitleBarCloseButton: + if( testWFlags( WStyle_SysMenu ) ) { + d->buttonDown = QStyle::SC_None; + repaint(FALSE); + emit doClose(); + return; + } + break; + + default: + break; + } + } + d->buttonDown = QStyle::SC_None; + repaint(FALSE); + d->pressed = FALSE; + } +} + +void QTitleBar::mouseMoveEvent( QMouseEvent * e) +{ + switch (d->buttonDown) { + case QStyle::SC_None: + if(autoRaise()) + repaint( FALSE ); + break; + case QStyle::SC_TitleBarSysMenu: + break; + case QStyle::SC_TitleBarShadeButton: + case QStyle::SC_TitleBarUnshadeButton: + case QStyle::SC_TitleBarNormalButton: + case QStyle::SC_TitleBarMinButton: + case QStyle::SC_TitleBarMaxButton: + case QStyle::SC_TitleBarCloseButton: + { + QStyle::SCFlags last_ctrl = d->buttonDown; + d->buttonDown = style().querySubControl(QStyle::CC_TitleBar, this, e->pos()); + if( d->buttonDown != last_ctrl) + d->buttonDown = QStyle::SC_None; + repaint(FALSE); + d->buttonDown = last_ctrl; + } + break; + + case QStyle::SC_TitleBarLabel: + if ( d->buttonDown == QStyle::SC_TitleBarLabel && d->movable && d->pressed ) { + if ( (d->moveOffset - mapToParent( e->pos() ) ).manhattanLength() >= 4 ) { + QPoint p = mapFromGlobal(e->globalPos()); +#ifndef QT_NO_WORKSPACE + if(d->window && d->window->parentWidget()->inherits("QWorkspaceChild")) { + QWorkspace *workspace = ::qt_cast<QWorkspace*>(d->window->parentWidget()->parentWidget()); + if(workspace) { + p = workspace->mapFromGlobal( e->globalPos() ); + if ( !workspace->rect().contains(p) ) { + if ( p.x() < 0 ) + p.rx() = 0; + if ( p.y() < 0 ) + p.ry() = 0; + if ( p.x() > workspace->width() ) + p.rx() = workspace->width(); + if ( p.y() > workspace->height() ) + p.ry() = workspace->height(); + } + } + } +#endif + QPoint pp = p - d->moveOffset; + if (!parentWidget()->isMaximized()) + parentWidget()->move( pp ); + } + } else { + QStyle::SCFlags last_ctrl = d->buttonDown; + d->buttonDown = QStyle::SC_None; + if( d->buttonDown != last_ctrl) + repaint(FALSE); + } + break; + } +} + +void QTitleBar::resizeEvent( QResizeEvent *r) +{ + QWidget::resizeEvent(r); + cutText(); +} + +void QTitleBar::paintEvent(QPaintEvent *) +{ + QStyle::SCFlags ctrls = QStyle::SC_TitleBarLabel; + if ( testWFlags( WStyle_SysMenu) ) { + if ( testWFlags( WStyle_Tool ) ) { + ctrls |= QStyle::SC_TitleBarCloseButton; + if ( d->window && testWFlags( WStyle_MinMax ) ) { + if ( d->window->isMinimized() ) + ctrls |= QStyle::SC_TitleBarUnshadeButton; + else + ctrls |= QStyle::SC_TitleBarShadeButton; + } + } else { + ctrls |= QStyle::SC_TitleBarSysMenu | QStyle::SC_TitleBarCloseButton; + if ( d->window && testWFlags( WStyle_Minimize ) ) { + if( d->window && d->window->isMinimized() ) + ctrls |= QStyle::SC_TitleBarNormalButton; + else + ctrls |= QStyle::SC_TitleBarMinButton; + } + if ( d->window && testWFlags( WStyle_Maximize ) && !d->window->isMaximized() ) + ctrls |= QStyle::SC_TitleBarMaxButton; + } + } + + QStyle::SCFlags under_mouse = QStyle::SC_None; + if( autoRaise() && hasMouse() ) { + QPoint p(mapFromGlobal(QCursor::pos())); + under_mouse = style().querySubControl(QStyle::CC_TitleBar, this, p); + ctrls ^= under_mouse; + } + + QSharedDoubleBuffer buffer( this, rect() ); + style().drawComplexControl(QStyle::CC_TitleBar, buffer.painter(), this, rect(), + colorGroup(), + isEnabled() ? QStyle::Style_Enabled : + QStyle::Style_Default, ctrls, d->buttonDown); + if(under_mouse != QStyle::SC_None) + style().drawComplexControl(QStyle::CC_TitleBar, buffer.painter(), this, rect(), + colorGroup(), + QStyle::Style_MouseOver | + (isEnabled() ? QStyle::Style_Enabled : 0), + under_mouse, d->buttonDown); +} + +void QTitleBar::mouseDoubleClickEvent( QMouseEvent *e ) +{ + if ( e->button() != LeftButton ) + return; + + switch(style().querySubControl(QStyle::CC_TitleBar, this, e->pos())) { + case QStyle::SC_TitleBarLabel: + emit doubleClicked(); + break; + + case QStyle::SC_TitleBarSysMenu: + if ( testWFlags( WStyle_SysMenu ) ) + emit doClose(); + break; + + default: + break; + } +} + +#ifdef QT_NO_WIDGET_TOPEXTRA +// We provide one, since titlebar is useless otherwise. +QString QTitleBar::caption() const +{ + return d->cap; +} +#endif + +void QTitleBar::cutText() +{ + QFontMetrics fm( font() ); + + int maxw = style().querySubControlMetrics(QStyle::CC_TitleBar, this, + QStyle::SC_TitleBarLabel).width(); + if ( !d->window ) + maxw = width() - 20; + const QString txt = caption(); + d->cuttext = txt; + if ( fm.width( txt + "m" ) > maxw ) { + int i = txt.length(); + int dotlength = fm.width( "..." ); + while ( i>0 && fm.width(txt.left( i )) + dotlength > maxw ) + i--; + if(i != (int)txt.length()) + d->cuttext = txt.left( i ) + "..."; + } +} + +void QTitleBar::setCaption( const QString& title ) +{ + if( caption() == title) + return; +#ifndef QT_NO_WIDGET_TOPEXTRA + QWidget::setCaption( title ); +#else + d->cap = title; +#endif + cutText(); + + update(); +} + + +void QTitleBar::setIcon( const QPixmap& icon ) +{ +#ifndef QT_NO_WIDGET_TOPEXTRA +#ifndef QT_NO_IMAGE_SMOOTHSCALE + QRect menur = style().querySubControlMetrics(QStyle::CC_TitleBar, this, + QStyle::SC_TitleBarSysMenu); + + QPixmap theIcon; + if (icon.width() > menur.width()) { + // try to keep something close to the same aspect + int aspect = (icon.height() * 100) / icon.width(); + int newh = (aspect * menur.width()) / 100; + theIcon.convertFromImage( icon.convertToImage().smoothScale(menur.width(), + newh) ); + } else if (icon.height() > menur.height()) { + // try to keep something close to the same aspect + int aspect = (icon.width() * 100) / icon.height(); + int neww = (aspect * menur.height()) / 100; + theIcon.convertFromImage( icon.convertToImage().smoothScale(neww, + menur.height()) ); + } else + theIcon = icon; + + QWidget::setIcon( theIcon ); +#else + QWidget::setIcon( icon ); +#endif + + update(); +#endif +} + +void QTitleBar::leaveEvent( QEvent * ) +{ + if(autoRaise() && !d->pressed) + repaint( FALSE ); +} + +void QTitleBar::enterEvent( QEvent * ) +{ + if(autoRaise() && !d->pressed) + repaint( FALSE ); + QEvent e( QEvent::Leave ); + QApplication::sendEvent( parentWidget(), &e ); +} + +void QTitleBar::setActive( bool active ) +{ + if ( d->act == active ) + return ; + + d->act = active; + update(); +} + +bool QTitleBar::isActive() const +{ + return d->act; +} + +bool QTitleBar::usesActiveColor() const +{ + return ( isActive() && isActiveWindow() ) || + ( !window() && topLevelWidget()->isActiveWindow() ); +} + +QString QTitleBar::visibleText() const +{ + return d->cuttext; +} + +QWidget *QTitleBar::window() const +{ + return d->window; +} + +bool QTitleBar::event( QEvent* e ) +{ + if ( e->type() == QEvent::ApplicationPaletteChange ) { + readColors(); + return TRUE; + } else if ( e->type() == QEvent::WindowActivate ) { + setActive( d->act ); + } else if ( e->type() == QEvent::WindowDeactivate ) { + bool wasActive = d->act; + setActive( FALSE ); + d->act = wasActive; + } + + return QWidget::event( e ); +} + +void QTitleBar::setMovable(bool b) +{ + d->movable = b; +} + +bool QTitleBar::isMovable() const +{ + return d->movable; +} + +void QTitleBar::setAutoRaise(bool b) +{ + d->autoraise = b; +} + +bool QTitleBar::autoRaise() const +{ + return d->autoraise; +} + +QSize QTitleBar::sizeHint() const +{ + constPolish(); + QRect menur = style().querySubControlMetrics(QStyle::CC_TitleBar, this, + QStyle::SC_TitleBarSysMenu); + return QSize( menur.width(), style().pixelMetric( QStyle::PM_TitleBarHeight, this ) ); +} + +#endif //QT_NO_TITLEBAR diff --git a/src/widgets/qtitlebar_p.h b/src/widgets/qtitlebar_p.h new file mode 100644 index 0000000..edd8fe2 --- /dev/null +++ b/src/widgets/qtitlebar_p.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Definition of some Qt private functions. +** +** Created : 000101 +** +** Copyright (C) 2000-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. +** +**********************************************************************/ + +#ifndef QTITLEBAR_P_H +#define QTITLEBAR_P_H + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of qworkspace.cpp and qdockwindow.cpp. This header file may change +// from version to version without notice, or even be removed. +// +// We mean it. +// +// + + +#ifndef QT_H +#include "qbutton.h" +#include "qlabel.h" +#endif // QT_H + +#if !defined(QT_NO_TITLEBAR) + +class QToolTip; +class QTitleBarPrivate; +class QPixmap; + +class Q_EXPORT QTitleBar : public QWidget +{ + Q_OBJECT + Q_PROPERTY( bool autoRaise READ autoRaise WRITE setAutoRaise ) + Q_PROPERTY( bool movable READ isMovable WRITE setMovable ) + +public: + QTitleBar (QWidget* w, QWidget* parent, const char* name=0); + ~QTitleBar(); + + bool isActive() const; + bool usesActiveColor() const; + virtual QString visibleText() const; + + bool isMovable() const; + void setMovable(bool); + + bool autoRaise() const; + void setAutoRaise(bool); + + QWidget *window() const; + + QSize sizeHint() const; + +#ifdef QT_NO_WIDGET_TOPEXTRA + // We provide one, since titlebar is useless otherwise. + QString caption() const; +#endif + +public slots: + void setActive( bool ); + void setCaption( const QString& title ); + void setIcon( const QPixmap& icon ); + +signals: + void doActivate(); + void doNormal(); + void doClose(); + void doMaximize(); + void doMinimize(); + void doShade(); + void showOperationMenu(); + void popupOperationMenu( const QPoint& ); + void doubleClicked(); + +protected: + bool event( QEvent *); + void resizeEvent( QResizeEvent *); + void contextMenuEvent( QContextMenuEvent * ); + void mousePressEvent( QMouseEvent * ); + void mouseDoubleClickEvent( QMouseEvent * ); + void mouseReleaseEvent( QMouseEvent * ); + void mouseMoveEvent( QMouseEvent * ); + void enterEvent( QEvent *e ); + void leaveEvent( QEvent *e ); + void paintEvent( QPaintEvent *p ); + + virtual void cutText(); + +private: + void readColors(); + + QTitleBarPrivate *d; +#if defined(Q_DISABLE_COPY) // Disabled copy constructor and operator= + QTitleBar( const QTitleBar & ); + QTitleBar &operator=( const QTitleBar & ); +#endif +}; + +#endif +#endif //QTITLEBAR_P_H diff --git a/src/widgets/qtoolbar.cpp b/src/widgets/qtoolbar.cpp new file mode 100644 index 0000000..22d4d5d --- /dev/null +++ b/src/widgets/qtoolbar.cpp @@ -0,0 +1,815 @@ +/**************************************************************************** +** +** Implementation of QToolBar class +** +** Created : 980315 +** +** 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 "qtoolbar.h" +#ifndef QT_NO_TOOLBAR + +#include "qmainwindow.h" +#include "qtooltip.h" +#include "qcursor.h" +#include "qlayout.h" +#include "qframe.h" +#include "qobjectlist.h" +#include "qpainter.h" +#include "qdrawutil.h" +#include "qtoolbutton.h" +#include "qpopupmenu.h" +#include "qcombobox.h" +#include "qtimer.h" +#include "qwidgetlist.h" +#include "qstyle.h" + +static const char * const arrow_v_xpm[] = { + "7 9 3 1", + " c None", + ". c #000000", + "+ c none", + ".+++++.", + "..+++..", + "+..+..+", + "++...++", + ".++.++.", + "..+++..", + "+..+..+", + "++...++", + "+++.+++"}; + +static const char * const arrow_h_xpm[] = { + "9 7 3 1", + " c None", + ". c #000000", + "+ c none", + "..++..+++", + "+..++..++", + "++..++..+", + "+++..++..", + "++..++..+", + "+..++..++", + "..++..+++"}; + +class QToolBarExtensionWidget; + +class QToolBarPrivate +{ +public: + QToolBarPrivate() : moving( FALSE ) { + } + + bool moving; + QToolBarExtensionWidget *extension; + QPopupMenu *extensionPopup; +}; + + +class QToolBarSeparator : public QWidget +{ + Q_OBJECT +public: + QToolBarSeparator( Orientation, QToolBar *parent, const char* name=0 ); + + QSize sizeHint() const; + Orientation orientation() const { return orient; } +public slots: + void setOrientation( Orientation ); +protected: + void styleChange( QStyle& ); + void paintEvent( QPaintEvent * ); + +private: + Orientation orient; +}; + +class QToolBarExtensionWidget : public QWidget +{ + Q_OBJECT + +public: + QToolBarExtensionWidget( QWidget *w ); + void setOrientation( Orientation o ); + QToolButton *button() const { return tb; } + +protected: + void resizeEvent( QResizeEvent *e ) { + QWidget::resizeEvent( e ); + layOut(); + } + +private: + void layOut(); + QToolButton *tb; + Orientation orient; + +}; + +QToolBarExtensionWidget::QToolBarExtensionWidget( QWidget *w ) + : QWidget( w, "qt_dockwidget_internal" ) +{ + tb = new QToolButton( this, "qt_toolbar_ext_button" ); + tb->setAutoRaise( TRUE ); + setOrientation( Horizontal ); +} + +void QToolBarExtensionWidget::setOrientation( Orientation o ) +{ + orient = o; + if ( orient == Horizontal ) + tb->setPixmap( QPixmap( (const char **)arrow_h_xpm ) ); + else + tb->setPixmap( QPixmap( (const char **)arrow_v_xpm ) ); + layOut(); +} + +void QToolBarExtensionWidget::layOut() +{ + tb->setGeometry( 2, 2, width() - 4, height() - 4 ); +} + +QToolBarSeparator::QToolBarSeparator(Orientation o , QToolBar *parent, + const char* name ) + : QWidget( parent, name ) +{ + connect( parent, SIGNAL(orientationChanged(Orientation)), + this, SLOT(setOrientation(Orientation)) ); + setOrientation( o ); + setBackgroundMode( parent->backgroundMode() ); + setBackgroundOrigin( ParentOrigin ); + setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ) ); +} + + + +void QToolBarSeparator::setOrientation( Orientation o ) +{ + orient = o; +} + +void QToolBarSeparator::styleChange( QStyle& ) +{ + setOrientation( orient ); +} + +QSize QToolBarSeparator::sizeHint() const +{ + int extent = style().pixelMetric( QStyle::PM_DockWindowSeparatorExtent, + this ); + if ( orient == Horizontal ) + return QSize( extent, 0 ); + else + return QSize( 0, extent ); +} + +void QToolBarSeparator::paintEvent( QPaintEvent * ) +{ + QPainter p( this ); + QStyle::SFlags flags = QStyle::Style_Default; + + if ( orientation() == Horizontal ) + flags |= QStyle::Style_Horizontal; + + style().drawPrimitive( QStyle::PE_DockWindowSeparator, &p, rect(), + colorGroup(), flags ); +} + +#include "qtoolbar.moc" + + +/*! + \class QToolBar qtoolbar.h + \brief The QToolBar class provides a movable panel containing + widgets such as tool buttons. + + \ingroup application + \mainclass + + A toolbar is a panel that contains a set of controls, usually + represented by small icons. It's purpose is to provide quick + access to frequently used commands or options. Within a + QMainWindow the user can drag toolbars within and between the + \link QDockArea dock areas\endlink. Toolbars can also be dragged + out of any dock area to float freely as top-level windows. + + QToolBar is a specialization of QDockWindow, and so provides all + the functionality of a QDockWindow. + + To use QToolBar you simply create a QToolBar as a child of a + QMainWindow, create a number of QToolButton widgets (or other + widgets) in left to right (or top to bottom) order and call + addSeparator() when you want a separator. When a toolbar is + floated the caption used is the label given in the constructor + call. This can be changed with setLabel(). + + \quotefile action/application.cpp + \skipto new QToolBar + \printuntil fileSaveAction + + This extract from the \l application/application.cpp example shows + the creation of a new toolbar as a child of a QMainWindow and + adding two QActions. + + You may use most widgets within a toolbar, with QToolButton and + QComboBox being the most common. + + If you create a new widget on an already visible QToolBar, this + widget will automatically become visible without needing a show() + call. (This differs from every other Qt widget container. We + recommend calling show() anyway since we hope to fix this anomaly + in a future release.) + + QToolBars, like QDockWindows, are located in \l{QDockArea}s or + float as top-level windows. QMainWindow provides four QDockAreas + (top, left, right and bottom). When you create a new toolbar (as + in the example above) as a child of a QMainWindow the toolbar will + be added to the top dock area. You can move it to another dock + area (or float it) by calling QMainWindow::moveDockWindow(). QDock + areas lay out their windows in \link qdockarea.html#lines + Lines\endlink. + + If the main window is resized so that the area occupied by the + toolbar is too small to show all its widgets a little arrow button + (which looks like a right-pointing chevron, '»') will appear + at the right or bottom of the toolbar depending on its + orientation. Clicking this button pops up a menu that shows the + 'overflowing' items. QToolButtons are represented in the menu using + their textLabel property, other QButton subclasses are represented + using their text property, and QComboBoxes are represented as submenus, + with the caption text being used in the submenu item. + + Usually a toolbar will get precisely the space it needs. However, + with setHorizontalStretchable(), setVerticalStretchable() or + setStretchableWidget() you can tell the main window to expand the + toolbar to fill all available space in the specified orientation. + + The toolbar arranges its buttons either horizontally or vertically + (see orientation() for details). Generally, QDockArea will set the + orientation correctly for you, but you can set it yourself with + setOrientation() and track any changes by connecting to the + orientationChanged() signal. + + You can use the clear() method to remove all items from a toolbar. + + \img qdockwindow.png Toolbar (dock window) + \caption A floating QToolbar (dock window) + + \sa QToolButton QMainWindow \link http://www.iarchitect.com/visual.htm Parts of Isys on Visual Design\endlink \link guibooks.html#fowler GUI Design Handbook: Tool Bar\endlink. +*/ + +/*! + \fn QToolBar::QToolBar( const QString &label, + QMainWindow *, ToolBarDock = Top, + bool newLine = FALSE, const char * name = 0 ); + \obsolete +*/ + +/*! + Constructs an empty toolbar. + + The toolbar is called \a name and is a child of \a parent and is + managed by \a parent. It is initially located in dock area \a dock + and is labeled \a label. If \a newLine is TRUE the toolbar will be + placed on a new line in the dock area. +*/ + +QToolBar::QToolBar( const QString &label, + QMainWindow * parent, QMainWindow::ToolBarDock dock, + bool newLine, const char * name ) + : QDockWindow( InDock, parent, name, 0, TRUE ) +{ + mw = parent; + init(); + + if ( parent ) + parent->addToolBar( this, label, dock, newLine ); +} + + +/*! + Constructs an empty horizontal toolbar. + + The toolbar is called \a name and is a child of \a parent and is + managed by \a mainWindow. The \a label and \a newLine parameters + are passed straight to QMainWindow::addDockWindow(). \a name and + the widget flags \a f are passed on to the QDockWindow constructor. + + Use this constructor if you want to create torn-off (undocked, + floating) toolbars or toolbars in the \link QStatusBar status + bar\endlink. +*/ + +QToolBar::QToolBar( const QString &label, QMainWindow * mainWindow, + QWidget * parent, bool newLine, const char * name, + WFlags f ) + : QDockWindow( InDock, parent, name, f, TRUE ) +{ + mw = mainWindow; + init(); + + clearWFlags( WType_Dialog | WStyle_Customize | WStyle_NoBorder ); + reparent( parent, QPoint( 0, 0 ), FALSE ); + + if ( mainWindow ) + mainWindow->addToolBar( this, label, QMainWindow::DockUnmanaged, newLine ); +} + + +/*! + \overload + + Constructs an empty toolbar called \a name, with parent \a parent, + in its \a parent's top dock area, without any label and without + requiring a newline. +*/ + +QToolBar::QToolBar( QMainWindow * parent, const char * name ) + : QDockWindow( InDock, parent, name, 0, TRUE ) +{ + mw = parent; + init(); + + if ( parent ) + parent->addToolBar( this, QString::null, QMainWindow::DockTop ); +} + +/*! + \internal + + Common initialization code. Requires that \c mw and \c o are set. + Does not call QMainWindow::addDockWindow(). +*/ +void QToolBar::init() +{ + d = new QToolBarPrivate; + d->extension = 0; + d->extensionPopup = 0; + sw = 0; + + setBackgroundMode( PaletteButton); + setFocusPolicy( NoFocus ); + setFrameStyle( QFrame::ToolBarPanel | QFrame::Raised); + boxLayout()->setSpacing(style().pixelMetric(QStyle::PM_ToolBarItemSpacing)); +} + +/*! + \reimp +*/ + +QToolBar::~QToolBar() +{ + delete d; + d = 0; +} + +/*! + \reimp +*/ + +void QToolBar::setOrientation( Orientation o ) +{ + QDockWindow::setOrientation( o ); + if (d->extension) + d->extension->setOrientation( o ); + QObjectList *childs = queryList( "QToolBarSeparator" ); + if ( childs ) { + QObject *ob = 0; + for ( ob = childs->first(); ob; ob = childs->next() ) { + QToolBarSeparator* w = (QToolBarSeparator*)ob; + w->setOrientation( o ); + } + } + delete childs; +} + +/*! + Adds a separator to the right/bottom of the toolbar. +*/ + +void QToolBar::addSeparator() +{ + (void) new QToolBarSeparator( orientation(), this, "toolbar separator" ); +} + +/*! + \reimp +*/ + +void QToolBar::styleChange( QStyle& ) +{ + QObjectList *childs = queryList( "QWidget" ); + if ( childs ) { + QObject *ob = 0; + for ( ob = childs->first(); ob; ob = childs->next() ) { + QWidget *w = (QWidget*)ob; + if ( ::qt_cast<QToolButton*>(w) || ::qt_cast<QToolBarSeparator*>(w) ) + w->setStyle( &style() ); + } + } + delete childs; + boxLayout()->setSpacing(style().pixelMetric(QStyle::PM_ToolBarItemSpacing)); +} + +/*! + \reimp. +*/ + +void QToolBar::show() +{ + QDockWindow::show(); + if ( mw ) + mw->triggerLayout( FALSE ); + checkForExtension( size() ); +} + + +/*! + \reimp +*/ + +void QToolBar::hide() +{ + QDockWindow::hide(); + if ( mw ) + mw->triggerLayout( FALSE ); +} + +/*! + Returns a pointer to the QMainWindow which manages this toolbar. +*/ + +QMainWindow * QToolBar::mainWindow() const +{ + return mw; +} + + +/*! + Sets the widget \a w to be expanded if this toolbar is requested + to stretch. + + The request to stretch might occur because QMainWindow + right-justifies the dock area the toolbar is in, or because this + toolbar's isVerticalStretchable() or isHorizontalStretchable() is + set to TRUE. + + If you call this function and the toolbar is not yet stretchable, + setStretchable() is called. + + \sa QMainWindow::setRightJustification(), setVerticalStretchable(), + setHorizontalStretchable() +*/ + +void QToolBar::setStretchableWidget( QWidget * w ) +{ + sw = w; + boxLayout()->setStretchFactor( w, 1 ); + + if ( !isHorizontalStretchable() && !isVerticalStretchable() ) { + if ( orientation() == Horizontal ) + setHorizontalStretchable( TRUE ); + else + setVerticalStretchable( TRUE ); + } +} + + +/*! + \reimp +*/ + +bool QToolBar::event( QEvent * e ) +{ + bool r = QDockWindow::event( e ); + // After the event filters have dealt with it, do our stuff. + if ( e->type() == QEvent::ChildInserted ) { + QObject * child = ((QChildEvent*)e)->child(); + if ( child && child->isWidgetType() && !((QWidget*)child)->isTopLevel() + && child->parent() == this + && qstrcmp("qt_dockwidget_internal", child->name()) != 0 ) { + boxLayout()->addWidget( (QWidget*)child ); + if ( isVisible() ) { + if ( ((QWidget*)child)->testWState( WState_CreatedHidden ) ) + ((QWidget*)child)->show(); + checkForExtension( size() ); + } + } + if ( child && child->isWidgetType() && ((QWidget*)child) == sw ) + boxLayout()->setStretchFactor( (QWidget*)child, 1 ); + } else if ( e->type() == QEvent::Show ) { + layout()->activate(); + } else if ( e->type() == QEvent::LayoutHint && place() == OutsideDock ) { + adjustSize(); + } + return r; +} + + +/*! + \property QToolBar::label + \brief the toolbar's label. + + If the toolbar is floated the label becomes the toolbar window's + caption. There is no default label text. +*/ + +void QToolBar::setLabel( const QString & label ) +{ + l = label; + setCaption( l ); +} + +QString QToolBar::label() const +{ + return l; +} + + +/*! + Deletes all the toolbar's child widgets. +*/ + +void QToolBar::clear() +{ + if ( !children() ) + return; + QObjectListIt it( *children() ); + QObject * obj; + while( (obj=it.current()) != 0 ) { + ++it; + if ( obj->isWidgetType() && + qstrcmp( "qt_dockwidget_internal", obj->name() ) != 0 ) + delete obj; + } +} + +/*! + \reimp +*/ + +QSize QToolBar::minimumSize() const +{ + if ( orientation() == Horizontal ) + return QSize( 0, QDockWindow::minimumSize().height() ); + return QSize( QDockWindow::minimumSize().width(), 0 ); +} + +/*! + \reimp +*/ + +QSize QToolBar::minimumSizeHint() const +{ + if ( orientation() == Horizontal ) + return QSize( 0, QDockWindow::minimumSizeHint().height() ); + return QSize( QDockWindow::minimumSizeHint().width(), 0 ); +} + +void QToolBar::createPopup() +{ + if (!d->extensionPopup) { + d->extensionPopup = new QPopupMenu( this, "qt_dockwidget_internal" ); + connect( d->extensionPopup, SIGNAL( aboutToShow() ), this, SLOT( createPopup() ) ); + } + + if (!d->extension) { + d->extension = new QToolBarExtensionWidget( this ); + d->extension->setOrientation(orientation()); + d->extension->button()->setPopup( d->extensionPopup ); + d->extension->button()->setPopupDelay( -1 ); + } + + d->extensionPopup->clear(); + // clear doesn't delete submenus, so do this explicitly + QObjectList *childlist = d->extensionPopup->queryList( "QPopupMenu", 0, FALSE, TRUE ); + childlist->setAutoDelete(TRUE); + delete childlist; + + childlist = queryList( "QWidget", 0, FALSE, TRUE ); + QObjectListIt it( *childlist ); + bool hide = FALSE; + bool doHide = FALSE; + int id; + while ( it.current() ) { + int j = 2; + if ( !it.current()->isWidgetType() || it.current() == d->extension->button() || + qstrcmp( "qt_dockwidget_internal", it.current()->name() ) == 0 ) { + ++it; + continue; + } + QWidget *w = (QWidget*)it.current(); +#ifndef QT_NO_COMBOBOX + if ( ::qt_cast<QComboBox*>(w) ) + j = 1; +#endif + hide = FALSE; + QPoint p = w->parentWidget()->mapTo( this, w->geometry().bottomRight() ); + if ( orientation() == Horizontal ) { + if ( p.x() > ( doHide ? width() - d->extension->width() / j : width() ) ) + hide = TRUE; + } else { + if ( p.y() > ( doHide ? height()- d->extension->height() / j : height() ) ) + hide = TRUE; + } + if ( hide && w->isVisible() ) { + doHide = TRUE; + if ( ::qt_cast<QToolButton*>(w) ) { + QToolButton *b = (QToolButton*)w; + QString s = b->textLabel(); + if ( s.isEmpty() ) + s = b->text(); + if ( b->popup() && b->popupDelay() <= 0 ) + id = d->extensionPopup->insertItem( b->iconSet(), s, b->popup() ); + else + id = d->extensionPopup->insertItem( b->iconSet(), s, b, SLOT( emulateClick() ) ) ; + if ( b->isToggleButton() ) + d->extensionPopup->setItemChecked( id, b->isOn() ); + if ( !b->isEnabled() ) + d->extensionPopup->setItemEnabled( id, FALSE ); + } else if ( ::qt_cast<QButton*>(w) ) { + QButton *b = (QButton*)w; + QString s = b->text(); + if ( s.isEmpty() ) + s = ""; + if ( b->pixmap() ) + id = d->extensionPopup->insertItem( *b->pixmap(), s, b, SLOT( emulateClick() ) ); + else + id = d->extensionPopup->insertItem( s, b, SLOT( emulateClick() ) ); + if ( b->isToggleButton() ) + d->extensionPopup->setItemChecked( id, b->isOn() ); + if ( !b->isEnabled() ) + d->extensionPopup->setItemEnabled( id, FALSE ); +#ifndef QT_NO_COMBOBOX + } else if ( ::qt_cast<QComboBox*>(w) ) { + QComboBox *c = (QComboBox*)w; + if ( c->count() != 0 ) { +#ifndef QT_NO_WIDGET_TOPEXTRA + QString s = c->caption(); +#else + QString s; +#endif + if ( s.isEmpty() ) + s = c->currentText(); + uint maxItems = 0; + QPopupMenu *cp = new QPopupMenu(d->extensionPopup); + cp->setEnabled(c->isEnabled()); + d->extensionPopup->insertItem( s, cp ); + connect( cp, SIGNAL( activated(int) ), c, SLOT( internalActivate(int) ) ); + for ( int i = 0; i < c->count(); ++i ) { + QString tmp = c->text( i ); + cp->insertItem( tmp, i ); + if ( c->currentText() == tmp ) + cp->setItemChecked( i, TRUE ); + if ( !maxItems ) { + if ( cp->count() == 10 ) { + int h = cp->sizeHint().height(); + maxItems = QApplication::desktop()->height() * 10 / h; + } + } else if ( cp->count() >= maxItems - 1 ) { + QPopupMenu* sp = new QPopupMenu(d->extensionPopup); + cp->insertItem( tr( "More..." ), sp ); + cp = sp; + connect( cp, SIGNAL( activated(int) ), c, SLOT( internalActivate(int) ) ); + } + } + } +#endif //QT_NO_COMBOBOX + } + } + ++it; + } + delete childlist; +} + + +/*! + \reimp +*/ + +void QToolBar::resizeEvent( QResizeEvent *e ) +{ + checkForExtension( e->size() ); +} + +void QToolBar::checkForExtension( const QSize &sz ) +{ + if (!isVisible()) + return; + + bool tooSmall; + if ( orientation() == Horizontal ) + tooSmall = sz.width() < sizeHint().width(); + else + tooSmall = sz.height() < sizeHint().height(); + + if ( tooSmall ) { + createPopup(); + if ( d->extensionPopup->count() ) { + if ( orientation() == Horizontal ) + d->extension->setGeometry( width() - 20, 1, 20, height() - 2 ); + else + d->extension->setGeometry( 1, height() - 20, width() - 2, 20 ); + d->extension->show(); + d->extension->raise(); + } else { + delete d->extension; + d->extension = 0; + delete d->extensionPopup; + d->extensionPopup = 0; + } + } else { + delete d->extension; + d->extension = 0; + delete d->extensionPopup; + d->extensionPopup = 0; + } +} + + +/*! + \reimp +*/ + +void QToolBar::setMinimumSize( int, int ) +{ +} + +/* from chaunsee: + +1. Tool Bars should contain only high-frequency functions. Avoid putting +things like About and Exit on a tool bar unless they are frequent functions. + +2. All tool bar buttons must have some keyboard access method (it can be a +menu or shortcut key or a function in a dialog box that can be accessed +through the keyboard). + +3. Make tool bar functions as efficient as possible (the common example is to +Print in Microsoft applications, it doesn't bring up the Print dialog box, it +prints immediately to the default printer). + +4. Avoid turning tool bars into graphical menu bars. To me, a tool bar should +be efficient. Once you make almost all the items in a tool bar into graphical +pull-down menus, you start to lose efficiency. + +5. Make sure that adjacent icons are distinctive. There are some tool bars +where you see a group of 4-5 icons that represent related functions, but they +are so similar that you can't differentiate among them. These tool bars are +often a poor attempt at a "common visual language". + +6. Use any de facto standard icons of your platform (for windows use the +cut, copy and paste icons provided in dev kits rather than designing your +own). + +7. Avoid putting a highly destructive tool bar button (delete database) by a +safe, high-frequency button (Find) -- this will yield 1-0ff errors). + +8. Tooltips in many Microsoft products simply reiterate the menu text even +when that is not explanatory. Consider making your tooltips slightly more +verbose and explanatory than the corresponding menu item. + +9. Keep the tool bar as stable as possible when you click on different +objects. Consider disabling tool bar buttons if they are used in most, but not +all contexts. + +10. If you have multiple tool bars (like the Microsoft MMC snap-ins have), +put the most stable tool bar to at the left with less stable ones to the +right. This arrangement (stable to less stable) makes the tool bar somewhat +more predictable. + +11. Keep a single tool bar to fewer than 20 items divided into 4-7 groups of +items. +*/ +#endif diff --git a/src/widgets/qtoolbar.h b/src/widgets/qtoolbar.h new file mode 100644 index 0000000..f07c1a8 --- /dev/null +++ b/src/widgets/qtoolbar.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Definition of QToolBar class +** +** Created : 980306 +** +** 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. +** +**********************************************************************/ + +#ifndef QTOOLBAR_H +#define QTOOLBAR_H + +#ifndef QT_H +#include "qdockwindow.h" +#endif // QT_H + +#ifndef QT_NO_TOOLBAR + +class QMainWindow; +class QButton; +class QBoxLayout; +class QToolBarPrivate; + +class Q_EXPORT QToolBar: public QDockWindow +{ + Q_OBJECT + Q_PROPERTY( QString label READ label WRITE setLabel ) + +public: + QToolBar( const QString &label, + QMainWindow *, ToolBarDock = DockTop, + bool newLine = FALSE, const char* name=0 ); + QToolBar( const QString &label, QMainWindow *, QWidget *, + bool newLine = FALSE, const char* name=0, WFlags f = 0 ); + QToolBar( QMainWindow* parent=0, const char* name=0 ); + ~QToolBar(); + + void addSeparator(); + + void show(); + void hide(); + + QMainWindow * mainWindow() const; + + virtual void setStretchableWidget( QWidget * ); + + bool event( QEvent * e ); + + virtual void setLabel( const QString & ); + QString label() const; + + virtual void clear(); + + QSize minimumSize() const; + QSize minimumSizeHint() const; + + void setOrientation( Orientation o ); + void setMinimumSize( int minw, int minh ); + +protected: + void resizeEvent( QResizeEvent *e ); + void styleChange( QStyle & ); + +private slots: + void createPopup(); + +private: + void init(); + void checkForExtension( const QSize &sz ); + QToolBarPrivate * d; + QMainWindow * mw; + QWidget * sw; + QString l; + + friend class QMainWindow; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QToolBar( const QToolBar & ); + QToolBar& operator=( const QToolBar & ); +#endif +}; + +#endif // QT_NO_TOOLBAR + +#endif // QTOOLBAR_H diff --git a/src/widgets/qtoolbox.cpp b/src/widgets/qtoolbox.cpp new file mode 100644 index 0000000..64fc148 --- /dev/null +++ b/src/widgets/qtoolbox.cpp @@ -0,0 +1,692 @@ +/**************************************************************************** +** +** Implementation of QToolBox widget class +** +** Created : 961105 +** +** 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 "qtoolbox.h" + +#ifndef QT_NO_TOOLBOX + +#include <qbutton.h> +#include <qlayout.h> +#include <qscrollview.h> +#include <qpainter.h> +#include <qstyle.h> +#include <qobjectlist.h> +#include <qapplication.h> +#include <qwidgetlist.h> +#include <qlayout.h> +#include <qvaluelist.h> +#include <qtooltip.h> +#include <qeventloop.h> +#include <qdatetime.h> + +class QToolBoxButton : public QButton +{ +public: + QToolBoxButton( QWidget *parent, const char *name ) + : QButton( parent, name ), selected( FALSE ) + { + setBackgroundMode(PaletteBackground); + setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + setFocusPolicy(NoFocus); + } + + inline void setSelected( bool b ) { selected = b; update(); } + inline void setTextLabel( const QString &text ) { label = text; update(); } + inline QString textLabel() const { return label; } + inline void setIconSet( const QIconSet &is ) { icon = is; update(); } + inline QIconSet iconSet() const { return icon; } + + QSize sizeHint() const; + QSize minimumSizeHint() const; + +protected: + void drawButton( QPainter * ); + +private: + bool selected; + QString label; + QIconSet icon; +}; + +class QToolBoxPrivate +{ +public: + struct Page + { + QToolBoxButton *button; + QScrollView *sv; + QWidget *widget; + QString toolTip; + + inline void setTextLabel( const QString &text ) { button->setTextLabel(text); } + inline void setIconSet( const QIconSet &is ) { button->setIconSet(is); } + inline void setToolTip( const QString &tip ) + { + toolTip = tip; + QToolTip::remove( button ); + if ( !tip.isNull() ) + QToolTip::add( button, tip ); + } + + inline bool operator==(const Page& other) const + { + return widget == other.widget; + } + }; + typedef QValueList<Page> PageList; + + inline QToolBoxPrivate() + : currentPage( 0 ) + { + } + + Page *page( QWidget *widget ); + Page *page( int index ); + + void updateTabs(); + + PageList pageList; + QVBoxLayout *layout; + Page *currentPage; +}; + +QToolBoxPrivate::Page *QToolBoxPrivate::page( QWidget *widget ) +{ + if ( !widget ) + return 0; + + for ( PageList::ConstIterator i = pageList.constBegin(); i != pageList.constEnd(); ++i ) + if ( (*i).widget == widget ) + return (Page*) &(*i); + return 0; +} + +QToolBoxPrivate::Page *QToolBoxPrivate::page( int index ) +{ + if (index >= 0 && index < (int)pageList.size() ) + return &*pageList.at(index); + return 0; +} + +void QToolBoxPrivate::updateTabs() +{ + QToolBoxButton *lastButton = currentPage ? currentPage->button : 0; + bool after = FALSE; + for ( PageList::ConstIterator i = pageList.constBegin(); i != pageList.constEnd(); ++i ) { + if (after) { + (*i).button->setEraseColor((*i).widget->eraseColor()); + (*i).button->update(); + } else if ( (*i).button->backgroundMode() != Qt::PaletteBackground ) { + (*i).button->setBackgroundMode( Qt::PaletteBackground ); + (*i).button->update(); + } + after = (*i).button == lastButton; + } +} + +QSize QToolBoxButton::sizeHint() const +{ + QSize iconSize(8, 8); + if ( !icon.isNull() ) + iconSize += icon.pixmap( QIconSet::Small, QIconSet::Normal ).size() + QSize( 2, 0 ); + QSize textSize = fontMetrics().size( Qt::ShowPrefix, label ) + QSize(0, 8); + + QSize total(iconSize.width() + textSize.width(), QMAX(iconSize.height(), textSize.height())); + return total.expandedTo(QApplication::globalStrut()); +} + +QSize QToolBoxButton::minimumSizeHint() const +{ + if ( icon.isNull() ) + return QSize(); + return QSize(8, 8) + icon.pixmap( QIconSet::Small, QIconSet::Normal ).size(); +} + +void QToolBoxButton::drawButton( QPainter *p ) +{ + QStyle::SFlags flags = QStyle::Style_Default; + const QColorGroup &cg = colorGroup(); + + if ( isEnabled() ) + flags |= QStyle::Style_Enabled; + if ( selected ) + flags |= QStyle::Style_Selected; + if ( hasFocus() ) + flags |= QStyle::Style_HasFocus; + if (isDown()) + flags |= QStyle::Style_Down; + style().drawControl( QStyle::CE_ToolBoxTab, p, parentWidget(), rect(), cg, flags ); + + QPixmap pm = icon.pixmap( QIconSet::Small, isEnabled() ? QIconSet::Normal : QIconSet::Disabled ); + + QRect cr = style().subRect( QStyle::SR_ToolBoxTabContents, this ); + QRect tr, ir; + int ih = 0; + if ( pm.isNull() ) { + tr = cr; + tr.addCoords( 4, 0, -8, 0 ); + } else { + int iw = pm.width() + 4; + ih = pm.height(); + ir = QRect( cr.left() + 4, cr.top(), iw + 2, ih ); + tr = QRect( ir.right(), cr.top(), cr.width() - ir.right() - 4, cr.height() ); + } + + if ( selected && style().styleHint( QStyle::SH_ToolBox_SelectedPageTitleBold ) ) { + QFont f( p->font() ); + f.setBold( TRUE ); + p->setFont( f ); + } + + QString txt; + if ( p->fontMetrics().width(label) < tr.width() ) { + txt = label; + } else { + txt = label.left( 1 ); + int ew = p->fontMetrics().width( "..." ); + int i = 1; + while ( p->fontMetrics().width( txt ) + ew + + p->fontMetrics().width( label[i] ) < tr.width() ) + txt += label[i++]; + txt += "..."; + } + + if ( ih ) + p->drawPixmap( ir.left(), (height() - ih) / 2, pm ); + + QToolBox *tb = (QToolBox*)parentWidget(); + + const QColor* fill = 0; + if ( selected && + style().styleHint( QStyle::SH_ToolBox_SelectedPageTitleBold ) && + tb->backgroundMode() != NoBackground ) + fill = &cg.color( QPalette::foregroundRoleFromMode( tb->backgroundMode() ) ); + + int alignment = AlignLeft | AlignVCenter | ShowPrefix; + if (!style().styleHint(QStyle::SH_UnderlineAccelerator, this)) + alignment |= NoAccel; + style().drawItem( p, tr, alignment, cg, + isEnabled(), 0, txt, -1, fill ); + + if ( !txt.isEmpty() && hasFocus() ) + style().drawPrimitive( QStyle::PE_FocusRect, p, tr, cg ); +} + +/*! + \class QToolBox + + \brief The QToolBox class provides a column of tabbed widget + items. + + \mainclass + \ingroup advanced + + A toolbox is a widget that displays a column of tabs one above the + other, with the current item displayed below the current tab. + Every tab has an index position within the column of tabs. A tab's + item is a QWidget. + + Each item has an itemLabel(), an optional icon, itemIconSet(), an + optional itemToolTip(), and a \link item() widget\endlink. The + item's attributes can be changed with setItemLabel(), + setItemIconSet() and setItemToolTip(). + + Items are added using addItem(), or inserted at particular + positions using insertItem(). The total number of items is given + by count(). Items can be deleted with delete, or removed from the + toolbox with removeItem(). Combining removeItem() and insertItem() + allows to move items to different positions. + + The current item widget is returned by currentItem() and set with + setCurrentItem(). If you prefer you can work in terms of indexes + using currentIndex(), setCurrentIndex(), indexOf() and item(). + + The currentChanged() signal is emitted when the current item is + changed. + + \sa QTabWidget +*/ + +/*! + \fn void QToolBox::currentChanged( int index ) + + This signal is emitted when the current item changed. The new + current item's index is passed in \a index, or -1 if there is no + current item. +*/ + +/*! + Constructs a toolbox called \a name with parent \a parent and flags \a f. +*/ + +QToolBox::QToolBox( QWidget *parent, const char *name, WFlags f ) + : QFrame( parent, name, f ) +{ + d = new QToolBoxPrivate; + d->layout = new QVBoxLayout( this ); + QWidget::setBackgroundMode( PaletteButton ); +} + +/*! \reimp */ + +QToolBox::~QToolBox() +{ + delete d; +} + +/*! + \fn int QToolBox::addItem( QWidget *w, const QString &label ) + \overload + + Adds the widget \a w in a new tab at bottom of the toolbox. The + new tab's label is set to \a label. Returns the new tab's index. +*/ + +/*! + \fn int QToolBox::addItem( QWidget *item, const QIconSet &iconSet,const QString &label ) + Adds the widget \a item in a new tab at bottom of the toolbox. The + new tab's label is set to \a label, and the \a iconSet is + displayed to the left of the \a label. Returns the new tab's index. +*/ + +/*! + \fn int QToolBox::insertItem( int index, QWidget *item, const QString &label ) + \overload + + Inserts the widget \a item at position \a index, or at the bottom + of the toolbox if \a index is out of range. The new item's label is + set to \a label. Returns the new item's index. +*/ + +/*! + Inserts the widget \a item at position \a index, or at the bottom + of the toolbox if \a index is out of range. The new item's label + is set to \a label, and the \a iconSet is displayed to the left of + the \a label. Returns the new item's index. +*/ + +int QToolBox::insertItem( int index, QWidget *item, const QIconSet &iconSet, + const QString &label ) +{ + if ( !item ) + return -1; + + connect(item, SIGNAL(destroyed(QObject*)), this, SLOT(itemDestroyed(QObject*))); + + QToolBoxPrivate::Page c; + c.widget = item; + c.button = new QToolBoxButton( this, label.latin1() ); + connect( c.button, SIGNAL( clicked() ), this, SLOT( buttonClicked() ) ); + + c.sv = new QScrollView( this ); + c.sv->hide(); + c.sv->setResizePolicy( QScrollView::AutoOneFit ); + c.sv->addChild( item ); + c.sv->setFrameStyle( QFrame::NoFrame ); + + c.setTextLabel( label ); + c.setIconSet( iconSet ); + + if ( index < 0 || index >= (int)d->pageList.count() ) { + index = (int)d->pageList.count(); + d->pageList.append( c ); + d->layout->addWidget( c.button ); + d->layout->addWidget( c.sv ); + if ( index == 0 ) + setCurrentIndex( index ); + } else { + d->pageList.insert( d->pageList.at(index), c ); + relayout(); + if (d->currentPage) { + QWidget *current = d->currentPage->widget; + int oldindex = indexOf(current); + if ( index <= oldindex ) { + d->currentPage = 0; // trigger change + setCurrentIndex(oldindex); + } + } + } + + c.button->show(); + + d->updateTabs(); + itemInserted(index); + return index; +} + +void QToolBox::buttonClicked() +{ + QToolBoxButton *tb = ::qt_cast<QToolBoxButton*>(sender()); + QWidget* item = 0; + for ( QToolBoxPrivate::PageList::ConstIterator i = d->pageList.constBegin(); i != d->pageList.constEnd(); ++i ) + if ( (*i).button == tb ) { + item = (*i).widget; + break; + } + + setCurrentItem( item ); +} + +/*! + \property QToolBox::count + \brief The number of items contained in the toolbox. +*/ + +int QToolBox::count() const +{ + return (int)d->pageList.count(); +} + +void QToolBox::setCurrentIndex( int index ) +{ + setCurrentItem( item( index ) ); +} + +/*! + Sets the current item to be \a item. +*/ + +void QToolBox::setCurrentItem( QWidget *item ) +{ + QToolBoxPrivate::Page *c = d->page( item ); + if ( !c || d->currentPage == c ) + return; + + c->button->setSelected( TRUE ); + if ( d->currentPage ) { + d->currentPage->sv->hide(); + d->currentPage->button->setSelected(FALSE); + } + d->currentPage = c; + d->currentPage->sv->show(); + d->updateTabs(); + emit currentChanged( indexOf(item) ); +} + +void QToolBox::relayout() +{ + delete d->layout; + d->layout = new QVBoxLayout( this ); + for ( QToolBoxPrivate::PageList::ConstIterator i = d->pageList.constBegin(); i != d->pageList.constEnd(); ++i ) { + d->layout->addWidget( (*i).button ); + d->layout->addWidget( (*i).sv ); + } +} + +void QToolBox::itemDestroyed(QObject *object) +{ + // no verification - vtbl corrupted already + QWidget *page = (QWidget*)object; + + QToolBoxPrivate::Page *c = d->page(page); + if ( !page || !c ) + return; + + d->layout->remove( c->sv ); + d->layout->remove( c->button ); + c->sv->deleteLater(); // page might still be a child of sv + delete c->button; + + bool removeCurrent = c == d->currentPage; + d->pageList.remove( *c ); + + if ( !d->pageList.count() ) { + d->currentPage = 0; + emit currentChanged(-1); + } else if ( removeCurrent ) { + d->currentPage = 0; + setCurrentIndex(0); + } +} + +/*! + Removes the widget \a item from the toolbox. Note that the widget + is \e not deleted. Returns the removed widget's index, or -1 if + the widget was not in this tool box. +*/ + +int QToolBox::removeItem( QWidget *item ) +{ + int index = indexOf(item); + if (index >= 0) { + disconnect(item, SIGNAL(destroyed(QObject*)), this, SLOT(itemDestroyed(QObject*))); + item->reparent( this, QPoint(0,0) ); + // destroy internal data + itemDestroyed(item); + } + itemRemoved(index); + return index; +} + + +/*! + Returns the toolbox's current item, or 0 if the toolbox is empty. +*/ + +QWidget *QToolBox::currentItem() const +{ + return d->currentPage ? d->currentPage->widget : 0; +} + +/*! + \property QToolBox::currentIndex + \brief the index of the current item, or -1 if the toolbox is empty. + \sa currentItem(), indexOf(), item() +*/ + + +int QToolBox::currentIndex() const +{ + return d->currentPage ? indexOf( d->currentPage->widget ) : -1; +} + +/*! + Returns the item at position \a index, or 0 if there is no such + item. +*/ + +QWidget *QToolBox::item( int index ) const +{ + if ( index < 0 || index >= (int) d->pageList.size() ) + return 0; + return (*d->pageList.at( index )).widget; +} + +/*! + Returns the index of item \a item, or -1 if the item does not + exist. +*/ + +int QToolBox::indexOf( QWidget *item ) const +{ + QToolBoxPrivate::Page *c = d->page(item); + return c ? d->pageList.findIndex( *c ) : -1; +} + +/*! + If \a enabled is TRUE then the item at position \a index is enabled; otherwise item + \a index is disabled. +*/ + +void QToolBox::setItemEnabled( int index, bool enabled ) +{ + QToolBoxPrivate::Page *c = d->page( index ); + if ( !c ) + return; + + c->button->setEnabled( enabled ); + if ( !enabled && c == d->currentPage ) { + int curIndexUp = index; + int curIndexDown = curIndexUp; + const int count = (int)d->pageList.count(); + while ( curIndexUp > 0 || curIndexDown < count-1 ) { + if ( curIndexDown < count-1 ) { + if (d->page(++curIndexDown)->button->isEnabled()) { + index = curIndexDown; + break; + } + } + if ( curIndexUp > 0 ) { + if (d->page(--curIndexUp)->button->isEnabled()) { + index = curIndexUp; + break; + } + } + } + setCurrentIndex(index); + } +} + + +/*! + Sets the label of the item at position \a index to \a label. +*/ + +void QToolBox::setItemLabel( int index, const QString &label ) +{ + QToolBoxPrivate::Page *c = d->page( index ); + if ( c ) + c->setTextLabel( label ); +} + +/*! + Sets the icon of the item at position \a index to \a iconSet. +*/ + +void QToolBox::setItemIconSet( int index, const QIconSet &iconSet ) +{ + QToolBoxPrivate::Page *c = d->page( index ); + if ( c ) + c->setIconSet( iconSet ); +} + +/*! + Sets the tooltip of the item at position \a index to \a toolTip. +*/ + +void QToolBox::setItemToolTip( int index, const QString &toolTip ) +{ + QToolBoxPrivate::Page *c = d->page( index ); + if ( c ) + c->setToolTip( toolTip ); +} + +/*! + Returns TRUE if the item at position \a index is enabled; otherwise returns FALSE. +*/ + +bool QToolBox::isItemEnabled( int index ) const +{ + QToolBoxPrivate::Page *c = d->page( index ); + return c && c->button->isEnabled(); +} + +/*! + Returns the label of the item at position \a index, or a null string if + \a index is out of range. +*/ + +QString QToolBox::itemLabel( int index ) const +{ + QToolBoxPrivate::Page *c = d->page( index ); + return ( c ? c->button->textLabel() : QString::null ); +} + +/*! + Returns the icon of the item at position \a index, or a null + icon if \a index is out of range. +*/ + +QIconSet QToolBox::itemIconSet( int index ) const +{ + QToolBoxPrivate::Page *c = d->page( index ); + return ( c ? c->button->iconSet() : QIconSet() ); +} + +/*! + Returns the tooltip of the item at position \a index, or a null + string if \a index is out of range. +*/ + +QString QToolBox::itemToolTip( int index ) const +{ + QToolBoxPrivate::Page *c = d->page( index ); + return ( c ? c->toolTip : QString::null ); +} + +/*! \reimp */ +void QToolBox::showEvent( QShowEvent *e ) +{ + QWidget::showEvent( e ); +} + +/*! \reimp */ +void QToolBox::frameChanged() +{ + d->layout->setMargin( frameWidth() ); + QFrame::frameChanged(); +} + +/*! \reimp */ +void QToolBox::styleChange(QStyle &style) +{ + d->updateTabs(); + QFrame::styleChange(style); +} + +/*! + This virtual handler is called after a new item was added or + inserted at position \a index. + */ +void QToolBox::itemInserted( int index ) +{ + Q_UNUSED(index) +} + +/*! + This virtual handler is called after an item was removed from + position \a index. + */ +void QToolBox::itemRemoved( int index ) +{ + Q_UNUSED(index) +} + +#endif //QT_NO_TOOLBOX diff --git a/src/widgets/qtoolbox.h b/src/widgets/qtoolbox.h new file mode 100644 index 0000000..b100cf6 --- /dev/null +++ b/src/widgets/qtoolbox.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Definition of QToolBox widget class +** +** Created : 961105 +** +** 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. +** +**********************************************************************/ + +#ifndef QTOOLBOX_H +#define QTOOLBOX_H + +#ifndef QT_H +#include <qframe.h> +#include <qiconset.h> +#endif // QT_H + +#ifndef QT_NO_TOOLBOX + +class QToolBoxPrivate; +class QWidgetList; + +class Q_EXPORT QToolBox : public QFrame +{ + Q_OBJECT + Q_PROPERTY( int currentIndex READ currentIndex WRITE setCurrentIndex ) + Q_PROPERTY( int count READ count ) + +public: + QToolBox( QWidget *parent = 0, const char *name = 0, WFlags f = 0 ); + ~QToolBox(); + + int addItem( QWidget *item, const QString &label ); + int addItem( QWidget *item, const QIconSet &iconSet, const QString &label ); + int insertItem( int index, QWidget *item, const QString &label ); + int insertItem( int index, QWidget *item, const QIconSet &iconSet, const QString &label ); + + int removeItem( QWidget *item ); + + void setItemEnabled( int index, bool enabled ); + bool isItemEnabled( int index ) const; + + void setItemLabel( int index, const QString &label ); + QString itemLabel( int index ) const; + + void setItemIconSet( int index, const QIconSet &iconSet ); + QIconSet itemIconSet( int index ) const; + + void setItemToolTip( int index, const QString &toolTip ); + QString itemToolTip( int index ) const; + + QWidget *currentItem() const; + void setCurrentItem( QWidget *item ); + + int currentIndex() const; + QWidget *item( int index ) const; + int indexOf( QWidget *item ) const; + int count() const; + +public slots: + void setCurrentIndex( int index ); + +signals: + void currentChanged( int index ); + +private slots: + void buttonClicked(); + void itemDestroyed(QObject*); + +protected: + virtual void itemInserted( int index ); + virtual void itemRemoved( int index ); + void showEvent( QShowEvent *e ); + void frameChanged(); + void styleChange(QStyle&); + +private: + void relayout(); + +private: + QToolBoxPrivate *d; + +}; + + +inline int QToolBox::addItem( QWidget *item, const QString &label ) +{ return insertItem( -1, item, QIconSet(), label ); } +inline int QToolBox::addItem( QWidget *item, const QIconSet &iconSet, + const QString &label ) +{ return insertItem( -1, item, iconSet, label ); } +inline int QToolBox::insertItem( int index, QWidget *item, const QString &label ) +{ return insertItem( index, item, QIconSet(), label ); } + +#endif // QT_NO_TOOLBOX +#endif diff --git a/src/widgets/qtoolbutton.cpp b/src/widgets/qtoolbutton.cpp new file mode 100644 index 0000000..f11b613 --- /dev/null +++ b/src/widgets/qtoolbutton.cpp @@ -0,0 +1,1041 @@ +/**************************************************************************** +** +** Implementation of QToolButton class +** +** Created : 980320 +** +** 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. +** +**********************************************************************/ + +#undef QT_NO_COMPAT +#include "qtoolbutton.h" +#ifndef QT_NO_TOOLBUTTON + +#include "qdrawutil.h" +#include "qpainter.h" +#include "qpixmap.h" +#include "qwmatrix.h" +#include "qapplication.h" +#include "qstyle.h" +#include "qmainwindow.h" +#include "qtooltip.h" +#include "qtoolbar.h" +#include "qimage.h" +#include "qiconset.h" +#include "qtimer.h" +#include "qpopupmenu.h" +#include "qguardedptr.h" + +class QToolButtonPrivate +{ + // ### add tool tip magic here +public: +#ifndef QT_NO_POPUPMENU + QGuardedPtr<QPopupMenu> popup; + QTimer* popupTimer; + int delay; +#endif + Qt::ArrowType arrow; + uint instantPopup : 1; + uint autoraise : 1; + uint repeat : 1; + uint discardNextMouseEvent : 1; + QToolButton::TextPosition textPos; +}; + + +/*! + \class QToolButton qtoolbutton.h + \brief The QToolButton class provides a quick-access button to + commands or options, usually used inside a QToolBar. + + \ingroup basic + \mainclass + + A tool button is a special button that provides quick-access to + specific commands or options. As opposed to a normal command + button, a tool button usually doesn't show a text label, but shows + an icon instead. Its classic usage is to select tools, for example + the "pen" tool in a drawing program. This would be implemented + with a QToolButton as toggle button (see setToggleButton() ). + + QToolButton supports auto-raising. In auto-raise mode, the button + draws a 3D frame only when the mouse points at it. The feature is + automatically turned on when a button is used inside a QToolBar. + Change it with setAutoRaise(). + + A tool button's icon is set as QIconSet. This makes it possible to + specify different pixmaps for the disabled and active state. The + disabled pixmap is used when the button's functionality is not + available. The active pixmap is displayed when the button is + auto-raised because the mouse pointer is hovering over it. + + The button's look and dimension is adjustable with + setUsesBigPixmap() and setUsesTextLabel(). When used inside a + QToolBar in a QMainWindow, the button automatically adjusts to + QMainWindow's settings (see QMainWindow::setUsesTextLabel() and + QMainWindow::setUsesBigPixmaps()). The pixmap set on a QToolButton + will be set to 22x22 if it is bigger than this size. If + usesBigPixmap() is TRUE, then the pixmap will be set to 32x32. + + A tool button can offer additional choices in a popup menu. The + feature is sometimes used with the "Back" button in a web browser. + After pressing and holding the button down for a while, a menu + pops up showing a list of possible pages to jump to. With + QToolButton you can set a popup menu using setPopup(). The default + delay is 600ms; you can adjust it with setPopupDelay(). + + \img qdockwindow.png Toolbar with Toolbuttons \caption A floating + QToolbar with QToolbuttons + + \sa QPushButton QToolBar QMainWindow \link guibooks.html#fowler + GUI Design Handbook: Push Button\endlink +*/ + +/*! + \enum QToolButton::TextPosition + + The position of the tool button's textLabel in relation to the + tool button's icon. + + \value BesideIcon The text appears beside the icon. + \value BelowIcon The text appears below the icon. +*/ + + +/*! + Constructs an empty tool button called \a name, with parent \a + parent. +*/ + +QToolButton::QToolButton( QWidget * parent, const char *name ) + : QButton( parent, name ) +{ + init(); +#ifndef QT_NO_TOOLBAR + QToolBar* tb = ::qt_cast<QToolBar*>(parent); + if ( tb ) { + setAutoRaise( TRUE ); + if ( tb->mainWindow() ) { + connect( tb->mainWindow(), SIGNAL(pixmapSizeChanged(bool)), + this, SLOT(setUsesBigPixmap(bool)) ); + setUsesBigPixmap( tb->mainWindow()->usesBigPixmaps() ); + connect( tb->mainWindow(), SIGNAL(usesTextLabelChanged(bool)), + this, SLOT(setUsesTextLabel(bool)) ); + setUsesTextLabel( tb->mainWindow()->usesTextLabel() ); + } else { + setUsesBigPixmap( FALSE ); + } + } else +#endif + { + setUsesBigPixmap( FALSE ); + } +} + + +/*! + Constructs a tool button as an arrow button. The \c ArrowType \a + type defines the arrow direction. Possible values are \c + LeftArrow, \c RightArrow, \c UpArrow and \c DownArrow. + + An arrow button has auto-repeat turned on by default. + + The \a parent and \a name arguments are sent to the QWidget + constructor. +*/ +QToolButton::QToolButton( ArrowType type, QWidget *parent, const char *name ) + : QButton( parent, name ) +{ + init(); + setUsesBigPixmap( FALSE ); + setAutoRepeat( TRUE ); + d->arrow = type; + hasArrow = TRUE; +} + + +/* Set-up code common to all the constructors */ + +void QToolButton::init() +{ + d = new QToolButtonPrivate; + d->textPos = Under; +#ifndef QT_NO_POPUPMENU + d->delay = 600; + d->popup = 0; + d->popupTimer = 0; +#endif + d->autoraise = FALSE; + d->arrow = LeftArrow; + d->instantPopup = FALSE; + d->discardNextMouseEvent = FALSE; + bpID = bp.serialNumber(); + spID = sp.serialNumber(); + + utl = FALSE; + ubp = TRUE; + hasArrow = FALSE; + + s = 0; + + setFocusPolicy( NoFocus ); + setBackgroundMode( PaletteButton); + setSizePolicy( QSizePolicy( QSizePolicy::Minimum, QSizePolicy::Minimum ) ); +} + +#ifndef QT_NO_TOOLBAR + +/*! + Constructs a tool button called \a name, that is a child of \a + parent (which must be a QToolBar). + + The tool button will display \a iconSet, with its text label and + tool tip set to \a textLabel and its status bar message set to \a + grouptext. It will be connected to the \a slot in object \a + receiver. +*/ + +QToolButton::QToolButton( const QIconSet& iconSet, const QString &textLabel, + const QString& grouptext, + QObject * receiver, const char *slot, + QToolBar * parent, const char *name ) + : QButton( parent, name ) +{ + init(); + setAutoRaise( TRUE ); + setIconSet( iconSet ); + setTextLabel( textLabel ); + if ( receiver && slot ) + connect( this, SIGNAL(clicked()), receiver, slot ); + if ( parent->mainWindow() ) { + connect( parent->mainWindow(), SIGNAL(pixmapSizeChanged(bool)), + this, SLOT(setUsesBigPixmap(bool)) ); + setUsesBigPixmap( parent->mainWindow()->usesBigPixmaps() ); + connect( parent->mainWindow(), SIGNAL(usesTextLabelChanged(bool)), + this, SLOT(setUsesTextLabel(bool)) ); + setUsesTextLabel( parent->mainWindow()->usesTextLabel() ); + } else { + setUsesBigPixmap( FALSE ); + } +#ifndef QT_NO_TOOLTIP + if ( !textLabel.isEmpty() ) { + if ( !grouptext.isEmpty() && parent->mainWindow() ) + QToolTip::add( this, textLabel, + parent->mainWindow()->toolTipGroup(), grouptext ); + else + QToolTip::add( this, textLabel ); + } else if ( !grouptext.isEmpty() && parent->mainWindow() ) + QToolTip::add( this, QString::null, + parent->mainWindow()->toolTipGroup(), grouptext ); +#endif +} + +#endif + + +/*! + Destroys the object and frees any allocated resources. +*/ + +QToolButton::~QToolButton() +{ +#ifndef QT_NO_POPUPMENU + d->popupTimer = 0; + d->popup = 0; +#endif + delete d; + delete s; +} + + +/*! + \property QToolButton::backgroundMode + \brief the toolbutton's background mode + + Get this property with backgroundMode(). + + \sa QWidget::setBackgroundMode() +*/ + +/*! + \property QToolButton::toggleButton + \brief whether this tool button is a toggle button. + + Toggle buttons have an on/off state similar to \link QCheckBox + check boxes. \endlink A tool button is not a toggle button by + default. + + \sa setOn(), toggle() +*/ + +void QToolButton::setToggleButton( bool enable ) +{ + QButton::setToggleButton( enable ); +} + + +/*! + \reimp +*/ +QSize QToolButton::sizeHint() const +{ + constPolish(); + + int w = 0, h = 0; + + if ( iconSet().isNull() && !text().isNull() && !usesTextLabel() ) { + w = fontMetrics().width( text() ); + h = fontMetrics().height(); // boundingRect()? + } else if ( usesBigPixmap() ) { + QPixmap pm = iconSet().pixmap( QIconSet::Large, QIconSet::Normal ); + w = pm.width(); + h = pm.height(); + QSize iconSize = QIconSet::iconSize( QIconSet::Large ); + if ( w < iconSize.width() ) + w = iconSize.width(); + if ( h < iconSize.height() ) + h = iconSize.height(); + } else if ( !iconSet().isNull() ) { + // ### in 3.1, use QIconSet::iconSize( QIconSet::Small ); + QPixmap pm = iconSet().pixmap( QIconSet::Small, QIconSet::Normal ); + w = pm.width(); + h = pm.height(); + if ( w < 16 ) + w = 16; + if ( h < 16 ) + h = 16; + } + + if ( usesTextLabel() ) { + QSize textSize = fontMetrics().size( Qt::ShowPrefix, textLabel() ); + textSize.setWidth( textSize.width() + fontMetrics().width(' ')*2 ); + if ( d->textPos == Under ) { + h += 4 + textSize.height(); + if ( textSize.width() > w ) + w = textSize.width(); + } else { // Right + w += 4 + textSize.width(); + if ( textSize.height() > h ) + h = textSize.height(); + } + } + +#ifndef QT_NO_POPUPMENU + if ( popup() && ! popupDelay() ) + w += style().pixelMetric(QStyle::PM_MenuButtonIndicator, this); +#endif + return (style().sizeFromContents(QStyle::CT_ToolButton, this, QSize(w, h)). + expandedTo(QApplication::globalStrut())); +} + +/*! + \reimp + */ +QSize QToolButton::minimumSizeHint() const +{ + return sizeHint(); +} + +/*! + \property QToolButton::usesBigPixmap + \brief whether this toolbutton uses big pixmaps. + + QToolButton automatically connects this property to the relevant + signal in the QMainWindow in which it resides. We strongly + recommend that you use QMainWindow::setUsesBigPixmaps() instead. + + This property's default is TRUE. + + \warning If you set some buttons (in a QMainWindow) to have big + pixmaps and others to have small pixmaps, QMainWindow may not get + the geometry right. +*/ + +void QToolButton::setUsesBigPixmap( bool enable ) +{ + if ( (bool)ubp == enable ) + return; + + ubp = enable; + if ( isVisible() ) { + update(); + updateGeometry(); + } +} + + +/*! + \property QToolButton::usesTextLabel + \brief whether the toolbutton displays a text label below the button pixmap. + + The default is FALSE. + + QToolButton automatically connects this slot to the relevant + signal in the QMainWindow in which is resides. +*/ + +void QToolButton::setUsesTextLabel( bool enable ) +{ + if ( (bool)utl == enable ) + return; + + utl = enable; + if ( isVisible() ) { + update(); + updateGeometry(); + } +} + + +/*! + \property QToolButton::on + \brief whether this tool button is on. + + This property has no effect on \link isToggleButton() non-toggling + buttons. \endlink The default is FALSE (i.e. off). + + \sa isToggleButton() toggle() +*/ + +void QToolButton::setOn( bool enable ) +{ + if ( !isToggleButton() ) + return; + QButton::setOn( enable ); +} + + +/*! + Toggles the state of this tool button. + + This function has no effect on \link isToggleButton() non-toggling + buttons. \endlink + + \sa isToggleButton() toggled() +*/ + +void QToolButton::toggle() +{ + if ( !isToggleButton() ) + return; + QButton::setOn( !isOn() ); +} + + +/*! + \reimp +*/ +void QToolButton::drawButton( QPainter * p ) +{ + QStyle::SCFlags controls = QStyle::SC_ToolButton; + QStyle::SCFlags active = QStyle::SC_None; + + Qt::ArrowType arrowtype = d->arrow; + + if (isDown()) + active |= QStyle::SC_ToolButton; + +#ifndef QT_NO_POPUPMENU + if (d->popup && !d->delay) { + controls |= QStyle::SC_ToolButtonMenu; + if (d->instantPopup || isDown()) + active |= QStyle::SC_ToolButtonMenu; + } +#endif + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (hasFocus()) + flags |= QStyle::Style_HasFocus; + if (isDown()) + flags |= QStyle::Style_Down; + if (isOn()) + flags |= QStyle::Style_On; + if (autoRaise()) { + flags |= QStyle::Style_AutoRaise; + if (uses3D()) { + flags |= QStyle::Style_MouseOver; + if (! isOn() && ! isDown()) + flags |= QStyle::Style_Raised; + } + } else if (! isOn() && ! isDown()) + flags |= QStyle::Style_Raised; + + style().drawComplexControl(QStyle::CC_ToolButton, p, this, rect(), colorGroup(), + flags, controls, active, + hasArrow ? QStyleOption(arrowtype) : + QStyleOption()); + drawButtonLabel(p); +} + + +/*! + \reimp +*/ +void QToolButton::drawButtonLabel(QPainter *p) +{ + QRect r = + QStyle::visualRect(style().subRect(QStyle::SR_ToolButtonContents, this), this); + + Qt::ArrowType arrowtype = d->arrow; + + QStyle::SFlags flags = QStyle::Style_Default; + if (isEnabled()) + flags |= QStyle::Style_Enabled; + if (hasFocus()) + flags |= QStyle::Style_HasFocus; + if (isDown()) + flags |= QStyle::Style_Down; + if (isOn()) + flags |= QStyle::Style_On; + if (autoRaise()) { + flags |= QStyle::Style_AutoRaise; + if (uses3D()) { + flags |= QStyle::Style_MouseOver; + if (! isOn() && ! isDown()) + flags |= QStyle::Style_Raised; + } + } else if (! isOn() && ! isDown()) + flags |= QStyle::Style_Raised; + + style().drawControl(QStyle::CE_ToolButtonLabel, p, this, r, + colorGroup(), flags, + hasArrow ? QStyleOption(arrowtype) : + QStyleOption()); +} + + +/*! + \reimp + */ +void QToolButton::enterEvent( QEvent * e ) +{ + if ( autoRaise() && isEnabled() ) + repaint(FALSE); + + QButton::enterEvent( e ); +} + + +/*! + \reimp + */ +void QToolButton::leaveEvent( QEvent * e ) +{ + if ( autoRaise() && isEnabled() ) + repaint(FALSE); + + QButton::leaveEvent( e ); +} + +/*! + \reimp + */ +void QToolButton::moveEvent( QMoveEvent * ) +{ + // Reimplemented to handle pseudo transparency in case the toolbars + // has a fancy pixmap background. + if ( parentWidget() && parentWidget()->backgroundPixmap() && + autoRaise() && !uses3D() ) + repaint( FALSE ); +} + +/*! + \reimp +*/ +void QToolButton::mousePressEvent( QMouseEvent *e ) +{ + QRect popupr = + QStyle::visualRect( style().querySubControlMetrics(QStyle::CC_ToolButton, this, + QStyle::SC_ToolButtonMenu), this ); + d->instantPopup = (popupr.isValid() && popupr.contains(e->pos())); + +#ifndef QT_NO_POPUPMENU + if ( d->discardNextMouseEvent ) { + d->discardNextMouseEvent = FALSE; + d->instantPopup = FALSE; + d->popup->removeEventFilter( this ); + return; + } + if ( e->button() == LeftButton && d->delay <= 0 && d->popup && d->instantPopup && !d->popup->isVisible() ) { + openPopup(); + return; + } +#endif + + d->instantPopup = FALSE; + QButton::mousePressEvent( e ); +} + +/*! + \reimp +*/ +bool QToolButton::eventFilter( QObject *o, QEvent *e ) +{ +#ifndef QT_NO_POPUPMENU + if ( o != d->popup ) + return QButton::eventFilter( o, e ); + switch ( e->type() ) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonDblClick: + { + QMouseEvent *me = (QMouseEvent*)e; + QPoint p = me->globalPos(); + if ( QApplication::widgetAt( p, TRUE ) == this ) + d->discardNextMouseEvent = TRUE; + } + break; + default: + break; + } +#endif + return QButton::eventFilter( o, e ); +} + +/*! + Returns TRUE if this button should be drawn using raised edges; + otherwise returns FALSE. + + \sa drawButton() +*/ + +bool QToolButton::uses3D() const +{ + return style().styleHint(QStyle::SH_ToolButton_Uses3D) + && (!autoRaise() || ( hasMouse() && isEnabled() ) +#ifndef QT_NO_POPUPMENU + || ( d->popup && d->popup->isVisible() && d->delay <= 0 ) || d->instantPopup +#endif + ); +} + + +/*! + \property QToolButton::textLabel + \brief the label of this button. + + Setting this property automatically sets the text as a tool tip + too. There is no default text. +*/ + +void QToolButton::setTextLabel( const QString &newLabel ) +{ + setTextLabel( newLabel, TRUE ); +} + +/*! + \overload + + Sets the label of this button to \a newLabel and automatically + sets it as a tool tip if \a tipToo is TRUE. +*/ + +void QToolButton::setTextLabel( const QString &newLabel , bool tipToo ) +{ + if ( tl == newLabel ) + return; + +#ifndef QT_NO_TOOLTIP + if ( tipToo ) { + QToolTip::remove( this ); + QToolTip::add( this, newLabel ); + } +#endif + + tl = newLabel; + if ( usesTextLabel() && isVisible() ) { + update(); + updateGeometry(); + } + +} + +#ifndef QT_NO_COMPAT + +QIconSet QToolButton::onIconSet() const +{ + return iconSet(); +} + +QIconSet QToolButton::offIconSet( ) const +{ + return iconSet(); +} + + +/*! + \property QToolButton::onIconSet + \brief the icon set that is used when the button is in an "on" state + + \obsolete + + Since Qt 3.0, QIconSet contains both the On and Off icons. There is + now an \l QToolButton::iconSet property that replaces both \l + QToolButton::onIconSet and \l QToolButton::offIconSet. + + For ease of porting, this property is a synonym for \l + QToolButton::iconSet. You probably want to go over your application + code and use the QIconSet On/Off mechanism. + + \sa iconSet QIconSet::State +*/ +void QToolButton::setOnIconSet( const QIconSet& set ) +{ + setIconSet( set ); + /* + ### Get rid of all qWarning in this file in 4.0. + Also consider inlining the obsolete functions then. + */ + qWarning( "QToolButton::setOnIconSet(): This function is not supported" + " anymore" ); +} + +/*! + \property QToolButton::offIconSet + \brief the icon set that is used when the button is in an "off" state + + \obsolete + + Since Qt 3.0, QIconSet contains both the On and Off icons. There is + now an \l QToolButton::iconSet property that replaces both \l + QToolButton::onIconSet and \l QToolButton::offIconSet. + + For ease of porting, this property is a synonym for \l + QToolButton::iconSet. You probably want to go over your application + code and use the QIconSet On/Off mechanism. + + \sa iconSet QIconSet::State +*/ +void QToolButton::setOffIconSet( const QIconSet& set ) +{ + setIconSet( set ); +} + +#endif + +/*! \property QToolButton::pixmap + \brief the pixmap of the button + + The pixmap property has no meaning for tool buttons. Use the + iconSet property instead. +*/ + +/*! + \property QToolButton::iconSet + \brief the icon set providing the icon shown on the button + + Setting this property sets \l QToolButton::pixmap to a null + pixmap. There is no default iconset. + + \sa pixmap(), setToggleButton(), isOn() +*/ +void QToolButton::setIconSet( const QIconSet & set ) +{ + if ( s ) + delete s; + setPixmap( QPixmap() ); + s = new QIconSet( set ); + if ( isVisible() ) + update(); +} + +/*! \overload + \obsolete + + Since Qt 3.0, QIconSet contains both the On and Off icons. + + For ease of porting, this function ignores the \a on parameter and + sets the \l iconSet property. If you relied on the \a on parameter, + you probably want to update your code to use the QIconSet On/Off + mechanism. + + \sa iconSet QIconSet::State +*/ + +#ifndef QT_NO_COMPAT + +void QToolButton::setIconSet( const QIconSet & set, bool /* on */ ) +{ + setIconSet( set ); + qWarning( "QToolButton::setIconSet(): 'on' parameter ignored" ); +} + +#endif + +QIconSet QToolButton::iconSet() const +{ + QToolButton *that = (QToolButton *) this; + + if ( pixmap() && !pixmap()->isNull() && + (!that->s || (that->s->pixmap().serialNumber() != + pixmap()->serialNumber())) ) { + if ( that->s ) + delete that->s; + that->s = new QIconSet( *pixmap() ); + } + if ( that->s ) + return *that->s; + /* + In 2.x, we used to return a temporary nonnull QIconSet. If you + revert to the old behavior, you will break calls to + QIconSet::isNull() in this file. + */ + return QIconSet(); +} + +#ifndef QT_NO_COMPAT +/*! \overload + \obsolete + + Since Qt 3.0, QIconSet contains both the On and Off icons. + + For ease of porting, this function ignores the \a on parameter and + returns the \l iconSet property. If you relied on the \a on + parameter, you probably want to update your code to use the QIconSet + On/Off mechanism. +*/ +QIconSet QToolButton::iconSet( bool /* on */ ) const +{ + return iconSet(); +} + +#endif + +#ifndef QT_NO_POPUPMENU +/*! + Associates the popup menu \a popup with this tool button. + + The popup will be shown each time the tool button has been pressed + down for a certain amount of time. A typical application example + is the "back" button in some web browsers's tool bars. If the user + clicks it, the browser simply browses back to the previous page. + If the user presses and holds the button down for a while, the + tool button shows a menu containing the current history list. + + Ownership of the popup menu is not transferred to the tool button. + + \sa popup() +*/ +void QToolButton::setPopup( QPopupMenu* popup ) +{ + if ( popup && !d->popupTimer ) { + connect( this, SIGNAL( pressed() ), this, SLOT( popupPressed() ) ); + d->popupTimer = new QTimer( this ); + connect( d->popupTimer, SIGNAL( timeout() ), this, SLOT( popupTimerDone() ) ); + } + d->popup = popup; + + update(); +} + +/*! + Returns the associated popup menu, or 0 if no popup menu has been + defined. + + \sa setPopup() +*/ +QPopupMenu* QToolButton::popup() const +{ + return d->popup; +} + +/*! + Opens (pops up) the associated popup menu. If there is no such + menu, this function does nothing. This function does not return + until the popup menu has been closed by the user. +*/ +void QToolButton::openPopup() +{ + if ( !d->popup ) + return; + + d->instantPopup = TRUE; + repaint( FALSE ); + if ( d->popupTimer ) + d->popupTimer->stop(); + QGuardedPtr<QToolButton> that = this; + popupTimerDone(); + if ( !that ) + return; + d->instantPopup = FALSE; + repaint( FALSE ); +} + +void QToolButton::popupPressed() +{ + if ( d->popupTimer && d->delay > 0 ) + d->popupTimer->start( d->delay, TRUE ); +} + +void QToolButton::popupTimerDone() +{ + if ( (!isDown() && d->delay > 0 ) || !d->popup ) + return; + + d->popup->installEventFilter( this ); + d->repeat = autoRepeat(); + setAutoRepeat( FALSE ); + bool horizontal = TRUE; +#ifndef QT_NO_TOOLBAR + QToolBar *tb = ::qt_cast<QToolBar*>(parentWidget()); + if ( tb && tb->orientation() == Vertical ) + horizontal = FALSE; +#endif + QPoint p; + QRect screen = qApp->desktop()->availableGeometry( this ); + if ( horizontal ) { + if ( QApplication::reverseLayout() ) { + if ( mapToGlobal( QPoint( 0, rect().bottom() ) ).y() + d->popup->sizeHint().height() <= screen.height() ) { + p = mapToGlobal( rect().bottomRight() ); + } else { + p = mapToGlobal( rect().topRight() - QPoint( 0, d->popup->sizeHint().height() ) ); + } + p.rx() -= d->popup->sizeHint().width(); + } else { + if ( mapToGlobal( QPoint( 0, rect().bottom() ) ).y() + d->popup->sizeHint().height() <= screen.height() ) { + p = mapToGlobal( rect().bottomLeft() ); + } else { + p = mapToGlobal( rect().topLeft() - QPoint( 0, d->popup->sizeHint().height() ) ); + } + } + } else { + if ( QApplication::reverseLayout() ) { + if ( mapToGlobal( QPoint( rect().left(), 0 ) ).x() - d->popup->sizeHint().width() <= screen.x() ) { + p = mapToGlobal( rect().topRight() ); + } else { + p = mapToGlobal( rect().topLeft() ); + p.rx() -= d->popup->sizeHint().width(); + } + } else { + if ( mapToGlobal( QPoint( rect().right(), 0 ) ).x() + d->popup->sizeHint().width() <= screen.width() ) { + p = mapToGlobal( rect().topRight() ); + } else { + p = mapToGlobal( rect().topLeft() - QPoint( d->popup->sizeHint().width(), 0 ) ); + } + } + } + QGuardedPtr<QToolButton> that = this; + d->popup->exec( p, -1 ); + if ( !that ) + return; + + setDown( FALSE ); + if ( d->repeat ) + setAutoRepeat( TRUE ); +} + +/*! + \property QToolButton::popupDelay + \brief the time delay between pressing the button and the appearance of the associated popup menu in milliseconds. + + Usually this is around half a second. A value of 0 draws the down + arrow button to the side of the button which can be used to open + up the popup menu. + + \sa setPopup() +*/ +void QToolButton::setPopupDelay( int delay ) +{ + d->delay = delay; + + update(); +} + +int QToolButton::popupDelay() const +{ + return d->delay; +} +#endif + + +/*! + \property QToolButton::autoRaise + \brief whether auto-raising is enabled or not. + + The default is disabled (i.e. FALSE). +*/ +void QToolButton::setAutoRaise( bool enable ) +{ + d->autoraise = enable; + + update(); +} + +bool QToolButton::autoRaise() const +{ + return d->autoraise; +} + +/*! + \property QToolButton::textPosition + \brief the position of the text label of this button. +*/ + +QToolButton::TextPosition QToolButton::textPosition() const +{ + return d->textPos; +} + +void QToolButton::setTextPosition( TextPosition pos ) +{ + d->textPos = pos; + updateGeometry(); + update(); +} + +/*! \reimp */ + +void QToolButton::setText( const QString &txt ) +{ + QButton::setText( txt ); + if ( !text().isEmpty() ) { + delete s; + s = 0; + } +} + +#ifndef QT_NO_PALETTE +/*! + \reimp +*/ +void QToolButton::paletteChange( const QPalette & ) +{ + if ( s ) + s->clearGenerated(); +} +#endif + +#endif diff --git a/src/widgets/qtoolbutton.h b/src/widgets/qtoolbutton.h new file mode 100644 index 0000000..0642360 --- /dev/null +++ b/src/widgets/qtoolbutton.h @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Definition of QToolButton class +** +** Created : 979899 +** +** 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. +** +**********************************************************************/ + +#ifndef QTOOLBUTTON_H +#define QTOOLBUTTON_H + +#ifndef QT_H +#include "qbutton.h" +#include "qstring.h" +#include "qpixmap.h" +#include "qiconset.h" +#endif // QT_H + +#ifndef QT_NO_TOOLBUTTON + +class QToolButtonPrivate; +class QToolBar; +class QPopupMenu; + +class Q_EXPORT QToolButton : public QButton +{ + Q_OBJECT + Q_ENUMS( TextPosition ) + + Q_PROPERTY( QIconSet iconSet READ iconSet WRITE setIconSet ) + Q_PROPERTY( QIconSet onIconSet READ onIconSet WRITE setOnIconSet DESIGNABLE false STORED false ) + Q_PROPERTY( QIconSet offIconSet READ offIconSet WRITE setOffIconSet DESIGNABLE false STORED false ) + Q_PROPERTY( bool usesBigPixmap READ usesBigPixmap WRITE setUsesBigPixmap ) + Q_PROPERTY( bool usesTextLabel READ usesTextLabel WRITE setUsesTextLabel ) + Q_PROPERTY( QString textLabel READ textLabel WRITE setTextLabel ) + Q_PROPERTY( int popupDelay READ popupDelay WRITE setPopupDelay ) + Q_PROPERTY( bool autoRaise READ autoRaise WRITE setAutoRaise ) + Q_PROPERTY( TextPosition textPosition READ textPosition WRITE setTextPosition ) + + Q_OVERRIDE( bool toggleButton WRITE setToggleButton ) + Q_OVERRIDE( bool on WRITE setOn ) + Q_OVERRIDE( QPixmap pixmap DESIGNABLE false STORED false ) + Q_OVERRIDE( BackgroundMode backgroundMode DESIGNABLE true) + +public: + enum TextPosition { + BesideIcon, + BelowIcon, + Right = BesideIcon, // obsolete + Under = BelowIcon // obsolete + }; + QToolButton( QWidget * parent, const char* name=0 ); +#ifndef QT_NO_TOOLBAR + QToolButton( const QIconSet& s, const QString &textLabel, + const QString& grouptext, + QObject * receiver, const char* slot, + QToolBar * parent, const char* name=0 ); +#endif + QToolButton( ArrowType type, QWidget *parent, const char* name=0 ); + ~QToolButton(); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + +#ifndef QT_NO_COMPAT + void setOnIconSet( const QIconSet& ); + void setOffIconSet( const QIconSet& ); + void setIconSet( const QIconSet &, bool on ); + QIconSet onIconSet() const; + QIconSet offIconSet( ) const; + QIconSet iconSet( bool on ) const; +#endif + virtual void setIconSet( const QIconSet & ); + QIconSet iconSet() const; + + bool usesBigPixmap() const { return ubp; } + bool usesTextLabel() const { return utl; } + QString textLabel() const { return tl; } + +#ifndef QT_NO_POPUPMENU + void setPopup( QPopupMenu* popup ); + QPopupMenu* popup() const; + + void setPopupDelay( int delay ); + int popupDelay() const; + + void openPopup(); +#endif + + void setAutoRaise( bool enable ); + bool autoRaise() const; + TextPosition textPosition() const; + + void setText( const QString &txt ); + +public slots: + virtual void setUsesBigPixmap( bool enable ); + virtual void setUsesTextLabel( bool enable ); + virtual void setTextLabel( const QString &, bool ); + + virtual void setToggleButton( bool enable ); + + virtual void setOn( bool enable ); + void toggle(); + void setTextLabel( const QString & ); + void setTextPosition( TextPosition pos ); + +protected: + void mousePressEvent( QMouseEvent * ); + void drawButton( QPainter * ); + void drawButtonLabel(QPainter *); + + void enterEvent( QEvent * ); + void leaveEvent( QEvent * ); + void moveEvent( QMoveEvent * ); + + // ### Make virtual in 4.0, maybe act like QPushButton with + // regards to setFlat() instead? Andy + bool uses3D() const; +#if (QT_VERSION >= 0x040000) +#error "Some functions need to be changed to virtual for Qt 4.0" +#endif + + bool eventFilter( QObject *o, QEvent *e ); + +#ifndef QT_NO_PALETTE + void paletteChange( const QPalette & ); +#endif + +private slots: + void popupTimerDone(); + void popupPressed(); + +private: + void init(); + + QPixmap bp; + int bpID; + QPixmap sp; + int spID; + + QString tl; + + QToolButtonPrivate *d; + QIconSet *s; + + uint utl : 1; + uint ubp : 1; + uint hasArrow : 1; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QToolButton( const QToolButton & ); + QToolButton& operator=( const QToolButton & ); +#endif +}; + +#endif // QT_NO_TOOLBUTTON + +#endif // QTOOLBUTTON_H diff --git a/src/widgets/qtooltip.cpp b/src/widgets/qtooltip.cpp new file mode 100644 index 0000000..c327859 --- /dev/null +++ b/src/widgets/qtooltip.cpp @@ -0,0 +1,1270 @@ +/**************************************************************************** +** +** Tool Tips (or Balloon Help) for any widget or rectangle +** +** 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 "qtooltip.h" +#ifndef QT_NO_TOOLTIP +#include "qlabel.h" +#include "qptrdict.h" +#include "qapplication.h" +#include "qguardedptr.h" +#include "qtimer.h" +#include "qeffects_p.h" + +static bool globally_enabled = TRUE; + +// Magic value meaning an entire widget - if someone tries to insert a +// tool tip on this part of a widget it will be interpreted as the +// entire widget. + +static inline QRect entireWidget() +{ + return QRect( -QWIDGETSIZE_MAX, -QWIDGETSIZE_MAX, + 2*QWIDGETSIZE_MAX, 2*QWIDGETSIZE_MAX ); +} + +// Internal class - don't touch + +class QTipLabel : public QLabel +{ + Q_OBJECT +public: + QTipLabel( QWidget* parent, const QString& text) : QLabel( parent, "toolTipTip", + WStyle_StaysOnTop | WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WX11BypassWM ) + { + setMargin(1); + setAutoMask( FALSE ); + setFrameStyle( QFrame::Plain | QFrame::Box ); + setLineWidth( 1 ); + setAlignment( AlignAuto | AlignTop ); + setIndent(0); + polish(); + setText(text); + adjustSize(); + x11SetWindowType( X11WindowTypeTooltip ); + } + void setWidth( int w ) { resize( sizeForWidth( w ) ); } +}; + +// Internal class - don't touch + +class QTipManager : public QObject +{ + Q_OBJECT +public: + QTipManager(); + ~QTipManager(); + + struct Tip + { + QRect rect; + QString text; + QString groupText; + QToolTipGroup *group; + QToolTip *tip; + bool autoDelete; + QRect geometry; + Tip *next; + }; + + bool eventFilter( QObject * o, QEvent * e ); + void add( const QRect &gm, QWidget *, const QRect &, const QString& , + QToolTipGroup *, const QString& , QToolTip *, bool ); + void add( QWidget *, const QRect &, const QString& , + QToolTipGroup *, const QString& , QToolTip *, bool ); + void remove( QWidget *, const QRect &, bool delayhide = FALSE ); + void remove( QWidget * ); + + void removeFromGroup( QToolTipGroup * ); + + void hideTipAndSleep(); + + QString find( QWidget *, const QPoint& ); + void setWakeUpDelay(int); + +public slots: + void hideTip(); + +private slots: + void labelDestroyed(); + void clientWidgetDestroyed(); + void showTip(); + void allowAnimation(); + +private: + QTimer wakeUp; + int wakeUpDelay; + QTimer fallAsleep; + + QPtrDict<Tip> *tips; + QTipLabel *label; + QPoint pos; + QGuardedPtr<QWidget> widget; + Tip *currentTip; + Tip *previousTip; + bool preventAnimation; + bool isApplicationFilter; + QTimer *removeTimer; +}; + + +// We have a global, internal QTipManager object + +static QTipManager *tipManager = 0; + +static void initTipManager() +{ + if ( !tipManager ) { + tipManager = new QTipManager; + Q_CHECK_PTR( tipManager ); + } +} + + +QTipManager::QTipManager() + : QObject( qApp, "toolTipManager" ) +{ + wakeUpDelay = 700; + tips = new QPtrDict<QTipManager::Tip>( 313 ); + currentTip = 0; + previousTip = 0; + label = 0; + preventAnimation = FALSE; + isApplicationFilter = FALSE; + connect( &wakeUp, SIGNAL(timeout()), SLOT(showTip()) ); + connect( &fallAsleep, SIGNAL(timeout()), SLOT(hideTip()) ); + removeTimer = new QTimer( this ); +} + + +QTipManager::~QTipManager() +{ + if ( isApplicationFilter && !qApp->closingDown() ) { + qApp->setGlobalMouseTracking( FALSE ); + qApp->removeEventFilter( tipManager ); + } + + delete label; + label = 0; + + if ( tips ) { + QPtrDictIterator<QTipManager::Tip> i( *tips ); + QTipManager::Tip *t, *n; + void *k; + while( (t = i.current()) != 0 ) { + k = i.currentKey(); + ++i; + tips->take( k ); + while ( t ) { + n = t->next; + delete t; + t = n; + } + } + delete tips; + } + + tipManager = 0; +} + +void QTipManager::add( const QRect &gm, QWidget *w, + const QRect &r, const QString &s, + QToolTipGroup *g, const QString& gs, + QToolTip *tt, bool a ) +{ + remove( w, r, TRUE ); + QTipManager::Tip *h = (*tips)[ w ]; + QTipManager::Tip *t = new QTipManager::Tip; + t->next = h; + t->tip = tt; + t->autoDelete = a; + t->text = s; + t->rect = r; + t->groupText = gs; + t->group = g; + t->geometry = gm; + + if ( h ) { + tips->take( w ); + if ( h != currentTip && h->autoDelete ) { + t->next = h->next; + delete h; + } + } else + connect( w, SIGNAL(destroyed()), this, SLOT(clientWidgetDestroyed()) ); + + tips->insert( w, t ); + + if ( a && t->rect.contains( pos ) && (!g || g->enabled()) ) { + removeTimer->stop(); + showTip(); + } + + if ( !isApplicationFilter && qApp ) { + isApplicationFilter = TRUE; + qApp->installEventFilter( tipManager ); + qApp->setGlobalMouseTracking( TRUE ); + } + + if ( t->group ) { + disconnect( removeTimer, SIGNAL( timeout() ), + t->group, SIGNAL( removeTip() ) ); + connect( removeTimer, SIGNAL( timeout() ), + t->group, SIGNAL( removeTip() ) ); + } +} + +void QTipManager::add( QWidget *w, const QRect &r, const QString &s, + QToolTipGroup *g, const QString& gs, + QToolTip *tt, bool a ) +{ + add( QRect( -1, -1, -1, -1 ), w, r, s, g, gs, tt, a ); +} + + +void QTipManager::remove( QWidget *w, const QRect & r, bool delayhide ) +{ + QTipManager::Tip *t = (*tips)[ w ]; + if ( t == 0 ) + return; + + if ( t == currentTip ) + if (!delayhide) + hideTip(); + else + currentTip->autoDelete = TRUE; + + if ( t == previousTip ) + previousTip = 0; + + if ( ( currentTip != t || !delayhide ) && t->rect == r ) { + tips->take( w ); + if ( t->next ) + tips->insert( w, t->next ); + delete t; + } else { + while( t->next && t->next->rect != r && ( currentTip != t->next || !delayhide )) + t = t->next; + if ( t->next ) { + QTipManager::Tip *d = t->next; + t->next = t->next->next; + delete d; + } + } + + if ( (*tips)[ w ] == 0 ) + disconnect( w, SIGNAL(destroyed()), this, SLOT(clientWidgetDestroyed()) ); +#if 0 // not needed, leads sometimes to crashes + if ( tips->isEmpty() ) { + // the manager will be recreated if needed + delete tipManager; + tipManager = 0; + } +#endif +} + + +/* + The label was destroyed in the program cleanup phase. +*/ + +void QTipManager::labelDestroyed() +{ + label = 0; +} + + +/* + Remove sender() from the tool tip data structures. +*/ + +void QTipManager::clientWidgetDestroyed() +{ + const QObject *s = sender(); + if ( s ) + remove( (QWidget*) s ); +} + + +void QTipManager::remove( QWidget *w ) +{ + QTipManager::Tip *t = (*tips)[ w ]; + if ( t == 0 ) + return; + + tips->take( w ); + QTipManager::Tip * d; + while ( t ) { + if ( t == currentTip ) + hideTip(); + d = t->next; + delete t; + t = d; + } + + disconnect( w, SIGNAL(destroyed()), this, SLOT(clientWidgetDestroyed()) ); +#if 0 + if ( tips->isEmpty() ) { + delete tipManager; + tipManager = 0; + } +#endif +} + + +void QTipManager::removeFromGroup( QToolTipGroup *g ) +{ + QPtrDictIterator<QTipManager::Tip> i( *tips ); + QTipManager::Tip *t; + while( (t = i.current()) != 0 ) { + ++i; + while ( t ) { + if ( t->group == g ) { + if ( t->group ) + disconnect( removeTimer, SIGNAL( timeout() ), + t->group, SIGNAL( removeTip() ) ); + t->group = 0; + } + t = t->next; + } + } +} + + + +bool QTipManager::eventFilter( QObject *obj, QEvent *e ) +{ + // avoid dumping core in case of application madness, and return + // quickly for some common but irrelevant events + if ( e->type() == QEvent::WindowDeactivate && + qApp && !qApp->activeWindow() && + label && label->isVisible() ) + hideTipAndSleep(); + + if ( !qApp + || !obj || !obj->isWidgetType() // isWidgetType() catches most stuff + || e->type() == QEvent::Paint + || e->type() == QEvent::Timer + || e->type() == QEvent::SockAct + || !tips ) + return FALSE; + QWidget *w = (QWidget *)obj; + + if ( e->type() == QEvent::FocusOut || e->type() == QEvent::FocusIn ) { + // user moved focus somewhere - hide the tip and sleep + if ( ((QFocusEvent*)e)->reason() != QFocusEvent::Popup ) + hideTipAndSleep(); + return FALSE; + } + + QTipManager::Tip *t = 0; + while( w && !t ) { + t = (*tips)[ w ]; + if ( !t ) + w = w->isTopLevel() ? 0 : w->parentWidget(); + } + if ( !w ) + return FALSE; + + if ( !t && e->type() != QEvent::MouseMove) { + if ( ( e->type() >= QEvent::MouseButtonPress && + e->type() <= QEvent::FocusOut) || e->type() == QEvent::Leave ) + hideTip(); + return FALSE; + } + + // with that out of the way, let's get down to action + + switch( e->type() ) { + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + case QEvent::KeyPress: + case QEvent::KeyRelease: + // input - turn off tool tip mode + hideTipAndSleep(); + break; + case QEvent::MouseMove: + { + QMouseEvent * m = (QMouseEvent *)e; + QPoint mousePos = w->mapFromGlobal( m->globalPos() ); + + if ( currentTip && !currentTip->rect.contains( mousePos ) ) { + hideTip(); + if ( m->state() == 0 ) + return FALSE; + } + + wakeUp.stop(); + if ( m->state() == 0 && + mousePos.x() >= 0 && mousePos.x() < w->width() && + mousePos.y() >= 0 && mousePos.y() < w->height() ) { + if ( label && label->isVisible() ) { + return FALSE; + } else { + if ( fallAsleep.isActive() ) { + wakeUp.start( 1, TRUE ); + } else { + previousTip = 0; + wakeUp.start( wakeUpDelay, TRUE ); + } + if ( t->group && t->group->ena && + !t->group->del && !t->groupText.isEmpty() ) { + removeTimer->stop(); + emit t->group->showTip( t->groupText ); + currentTip = t; + } + } + widget = w; + pos = mousePos; + return FALSE; + } else { + hideTip(); + } + } + break; + case QEvent::Leave: + case QEvent::Hide: + case QEvent::Destroy: + if ( w == widget ) + hideTip(); + break; + default: + break; + } + return FALSE; +} + + + +void QTipManager::showTip() +{ + if ( !widget || !globally_enabled +#ifndef Q_WS_X11 + || !widget->isActiveWindow() +#endif + ) + return; + + QTipManager::Tip *t = (*tips)[ widget ]; + while ( t && !t->rect.contains( pos ) ) + t = t->next; + if ( t == 0 ) + return; + + if ( t == currentTip && label && label->isVisible() ) + return; // nothing to do + + if ( t->tip ) { + t->tip->maybeTip( pos ); + return; + } + + if ( t->group && !t->group->ena ) + return; + + int scr; + if ( QApplication::desktop()->isVirtualDesktop() ) + scr = QApplication::desktop()->screenNumber( widget->mapToGlobal( pos ) ); + else + scr = QApplication::desktop()->screenNumber( widget ); + + if ( label +#if defined(Q_WS_X11) + && label->x11Screen() == widget->x11Screen() +#endif + ) { + // the next two lines are a workaround for QLabel being too intelligent. + // QLabel turns on the wordbreak flag once it gets a richtext. The two lines below + // ensure we get correct textflags when switching back and forth between a richtext and + // non richtext tooltip + label->setText( "" ); + label->setAlignment( AlignAuto | AlignTop ); + label->setText( t->text ); + label->adjustSize(); + if ( t->geometry != QRect( -1, -1, -1, -1 ) ) + label->resize( t->geometry.size() ); + } else { + delete label; + label = new QTipLabel( QApplication::desktop()->screen( scr ), t->text); + if ( t->geometry != QRect( -1, -1, -1, -1 ) ) + label->resize( t->geometry.size() ); + Q_CHECK_PTR( label ); + connect( label, SIGNAL(destroyed()), SLOT(labelDestroyed()) ); + } + // the above deletion and creation of a QTipLabel causes events to be sent. We had reports that the widget + // pointer was 0 after this. This is in principle possible if the wrong kind of events get sent through our event + // filter in this time. So better be safe and check widget once again here. + if (!widget) + return; + +#ifdef Q_WS_X11 + label->x11SetWindowTransient( widget->topLevelWidget()); +#endif + +#ifdef Q_WS_MAC + QRect screen = QApplication::desktop()->availableGeometry( scr ); +#else + QRect screen = QApplication::desktop()->screenGeometry( scr ); +#endif + QPoint p; + if ( t->geometry == QRect( -1, -1, -1, -1 ) ) { + p = widget->mapToGlobal( pos ) + +#ifdef Q_WS_WIN + QPoint( 2, 24 ); +#else + QPoint( 2, 16 ); +#endif + if ( p.x() + label->width() > screen.x() + screen.width() ) + p.rx() -= 4 + label->width(); + if ( p.y() + label->height() > screen.y() + screen.height() ) + p.ry() -= 24 + label->height(); + } else { + p = widget->mapToGlobal( t->geometry.topLeft() ); + label->setAlignment( WordBreak | AlignCenter ); + label->setWidth( t->geometry.width() - 4 ); + } + if ( p.y() < screen.y() ) + p.setY( screen.y() ); + if ( p.x() + label->width() > screen.x() + screen.width() ) + p.setX( screen.x() + screen.width() - label->width() ); + if ( p.x() < screen.x() ) + p.setX( screen.x() ); + if ( p.y() + label->height() > screen.y() + screen.height() ) + p.setY( screen.y() + screen.height() - label->height() ); + if ( label->text().length() ) { + label->move( p ); + +#ifndef QT_NO_EFFECTS + if ( QApplication::isEffectEnabled( UI_AnimateTooltip ) == FALSE || + previousTip || preventAnimation ) + label->show(); + else if ( QApplication::isEffectEnabled( UI_FadeTooltip ) ) + qFadeEffect( label ); + else + qScrollEffect( label ); +#else + label->show(); +#endif + + label->raise(); + fallAsleep.start( 10000, TRUE ); + } + + if ( t->group && t->group->del && !t->groupText.isEmpty() ) { + removeTimer->stop(); + emit t->group->showTip( t->groupText ); + } + + currentTip = t; + previousTip = 0; +} + + +void QTipManager::hideTip() +{ + QTimer::singleShot( 250, this, SLOT(allowAnimation()) ); + preventAnimation = TRUE; + + if ( label && label->isVisible() ) { + label->hide(); + fallAsleep.start( 2000, TRUE ); + wakeUp.stop(); + if ( currentTip && currentTip->group ) + removeTimer->start( 100, TRUE ); + } else if ( wakeUp.isActive() ) { + wakeUp.stop(); + if ( currentTip && currentTip->group && + !currentTip->group->del && !currentTip->groupText.isEmpty() ) + removeTimer->start( 100, TRUE ); + } else if ( currentTip && currentTip->group ) { + removeTimer->start( 100, TRUE ); + } + + previousTip = currentTip; + currentTip = 0; + if ( previousTip && previousTip->autoDelete ) + remove( widget, previousTip->rect ); + widget = 0; +} + +void QTipManager::hideTipAndSleep() +{ + hideTip(); + fallAsleep.stop(); +} + + +void QTipManager::allowAnimation() +{ + preventAnimation = FALSE; +} + +QString QTipManager::find( QWidget *w, const QPoint& pos ) +{ + Tip *t = (*tips)[ w ]; + while ( t && !t->rect.contains( pos ) ) + t = t->next; + + return t ? t->text : QString::null; +} + +void QTipManager::setWakeUpDelay ( int i ) +{ + wakeUpDelay = i; +} + +/*! + \class QToolTip qtooltip.h + \brief The QToolTip class provides tool tips (balloon help) for + any widget or rectangular part of a widget. + + \ingroup helpsystem + \mainclass + + The tip is a short, single line of text reminding the user of the + widget's or rectangle's function. It is drawn immediately below + the region in a distinctive black-on-yellow combination. + + The tip can be any Rich-Text formatted string. + + QToolTipGroup provides a way for tool tips to display another text + elsewhere (most often in a \link QStatusBar status bar\endlink). + + At any point in time, QToolTip is either dormant or active. In + dormant mode the tips are not shown and in active mode they are. + The mode is global, not particular to any one widget. + + QToolTip switches from dormant to active mode when the user hovers + the mouse on a tip-equipped region for a second or so and remains + active until the user either clicks a mouse button, presses a key, + lets the mouse hover for five seconds or moves the mouse outside + \e all tip-equipped regions for at least a second. + + The QToolTip class can be used in three different ways: + \list 1 + \i Adding a tip to an entire widget. + \i Adding a tip to a fixed rectangle within a widget. + \i Adding a tip to a dynamic rectangle within a widget. + \endlist + + To add a tip to a widget, call the \e static function + QToolTip::add() with the widget and tip as arguments: + + \code + QToolTip::add( quitButton, "Leave the application" ); + \endcode + + This is the simplest and most common use of QToolTip. The tip + will be deleted automatically when \e quitButton is deleted, but + you can remove it yourself, too: + + \code + QToolTip::remove( quitButton ); + \endcode + + You can also display another text (typically in a \link QStatusBar + status bar),\endlink courtesy of \l{QToolTipGroup}. This example + assumes that \e grp is a \c{QToolTipGroup *} and is already + connected to the appropriate status bar: + + \code + QToolTip::add( quitButton, "Leave the application", grp, + "Leave the application, prompting to save if necessary" ); + QToolTip::add( closeButton, "Close this window", grp, + "Close this window, prompting to save if necessary" ); + \endcode + + To add a tip to a fixed rectangle within a widget, call the static + function QToolTip::add() with the widget, rectangle and tip as + arguments. (See the \c tooltip/tooltip.cpp example.) Again, you + can supply a \c{QToolTipGroup *} and another text if you want. + + Both of these are one-liners and cover the majority of cases. The + third and most general way to use QToolTip requires you to + reimplement a pure virtual function to decide whether to pop up a + tool tip. The \c tooltip/tooltip.cpp example demonstrates this + too. This mode can be used to implement tips for text that can + move as the user scrolls, for example. + + To use QToolTip like this, you must subclass QToolTip and + reimplement maybeTip(). QToolTip calls maybeTip() when a tip + should pop up, and maybeTip() decides whether to show a tip. + + Tool tips can be globally disabled using + QToolTip::setGloballyEnabled() or disabled in groups with + QToolTipGroup::setEnabled(). + + You can retrieve the text of a tooltip for a given position within + a widget using textFor(). + + The global tooltip font and palette can be set with the static + setFont() and setPalette() functions respectively. + + \sa QStatusBar QWhatsThis QToolTipGroup + \link guibooks.html#fowler GUI Design Handbook: Tool Tip\endlink +*/ + + +/*! + Returns the font common to all tool tips. + + \sa setFont() +*/ + +QFont QToolTip::font() +{ + QTipLabel l(0,""); + return QApplication::font( &l ); +} + + +/*! + Sets the font for all tool tips to \a font. + + \sa font() +*/ + +void QToolTip::setFont( const QFont &font ) +{ + QApplication::setFont( font, TRUE, "QTipLabel" ); +} + + +/*! + Returns the palette common to all tool tips. + + \sa setPalette() +*/ + +QPalette QToolTip::palette() +{ + QTipLabel l(0,""); + return QApplication::palette( &l ); +} + + +/*! + Sets the palette for all tool tips to \a palette. + + \sa palette() +*/ + +void QToolTip::setPalette( const QPalette &palette ) +{ + QApplication::setPalette( palette, TRUE, "QTipLabel" ); +} + +/*! + Constructs a tool tip object. This is only necessary if you need + tool tips on regions that can move within the widget (most often + because the widget's contents can scroll). + + \a widget is the widget you want to add dynamic tool tips to and + \a group (optional) is the tool tip group they should belong to. + + \warning QToolTip is not a subclass of QObject, so the instance of + QToolTip is not deleted when \a widget is deleted. + + \warning If you delete the tool tip before you have deleted + \a widget then you need to make sure you call remove() yourself from + \a widget in your reimplemented QToolTip destructor. + + \code + MyToolTip::~MyToolTip() + { + remove( widget ); + } + \endcode + + \sa maybeTip(). +*/ + +QToolTip::QToolTip( QWidget * widget, QToolTipGroup * group ) +{ + p = widget; + g = group; + initTipManager(); + tipManager->add( p, entireWidget(), + QString::null, g, QString::null, this, FALSE ); +} + + +/*! + Adds a tool tip to \a widget. \a text is the text to be shown in + the tool tip. + + This is the most common entry point to the QToolTip class; it is + suitable for adding tool tips to buttons, checkboxes, comboboxes + and so on. +*/ + +void QToolTip::add( QWidget *widget, const QString &text ) +{ + initTipManager(); + tipManager->add( widget, entireWidget(), + text, 0, QString::null, 0, FALSE ); +} + + +/*! + \overload + + Adds a tool tip to \a widget and to tool tip group \a group. + + \a text is the text shown in the tool tip and \a longText is the + text emitted from \a group. + + Normally, \a longText is shown in a \link QStatusBar status + bar\endlink or similar. +*/ + +void QToolTip::add( QWidget *widget, const QString &text, + QToolTipGroup *group, const QString& longText ) +{ + initTipManager(); + tipManager->add( widget, entireWidget(), text, group, longText, 0, FALSE ); +} + + +/*! + Removes the tool tip from \a widget. + + If there is more than one tool tip on \a widget, only the one + covering the entire widget is removed. +*/ + +void QToolTip::remove( QWidget * widget ) +{ + if ( tipManager ) + tipManager->remove( widget, entireWidget() ); +} + +/*! + \overload + + Adds a tool tip to a fixed rectangle, \a rect, within \a widget. + \a text is the text shown in the tool tip. +*/ + +void QToolTip::add( QWidget * widget, const QRect & rect, const QString &text ) +{ + initTipManager(); + tipManager->add( widget, rect, text, 0, QString::null, 0, FALSE ); +} + + +/*! + \overload + + Adds a tool tip to an entire \a widget and to tool tip group \a + group. The tooltip will disappear when the mouse leaves the \a + rect. + + \a text is the text shown in the tool tip and \a groupText is the + text emitted from \a group. + + Normally, \a groupText is shown in a \link QStatusBar status + bar\endlink or similar. +*/ + +void QToolTip::add( QWidget *widget, const QRect &rect, + const QString& text, + QToolTipGroup *group, const QString& groupText ) +{ + initTipManager(); + tipManager->add( widget, rect, text, group, groupText, 0, FALSE ); +} + + +/*! + \overload + + Removes any tool tip for \a rect from \a widget. + + If there is more than one tool tip on \a widget, only the one + covering rectangle \a rect is removed. +*/ + +void QToolTip::remove( QWidget * widget, const QRect & rect ) +{ + if ( tipManager ) + tipManager->remove( widget, rect ); +} + +/*! + Returns the tool tip text for \a widget at position \a pos, or + QString::null if there is no tool tip for the given widget and + position. +*/ + +QString QToolTip::textFor( QWidget *widget, const QPoint& pos ) +{ + if ( tipManager ) + return tipManager->find( widget, pos ); + return QString::null; +} + +/*! + Hides any tip that is currently being shown. + + Normally, there is no need to call this function; QToolTip takes + care of showing and hiding the tips as the user moves the mouse. +*/ + +void QToolTip::hide() +{ + if ( tipManager ) + tipManager->hideTipAndSleep(); +} + +/*! + \fn virtual void QToolTip::maybeTip( const QPoint & p); + + This pure virtual function is half of the most versatile interface + QToolTip offers. + + It is called when there is a possibility that a tool tip should be + shown and must decide whether there is a tool tip for the point \a + p in the widget that this QToolTip object relates to. If so, + maybeTip() must call tip() with the rectangle the tip applies to, + the tip's text and optionally the QToolTipGroup details and the + geometry in screen coordinates. + + \a p is given in that widget's local coordinates. Most maybeTip() + implementations will be of the form: + + \code + if ( <something> ) { + tip( <something>, <something> ); + } + \endcode + + The first argument to tip() (a rectangle) must encompass \a p, + i.e. the tip must apply to the current mouse position; otherwise + QToolTip's operation is undefined. + + Note that the tip will disappear once the mouse moves outside the + rectangle you give to tip(), and will not reappear if the mouse + moves back in: maybeTip() is called again instead. + + \sa tip() +*/ + + +/*! + Immediately pops up a tip saying \a text and removes the tip once + the cursor moves out of rectangle \a rect (which is given in the + coordinate system of the widget this QToolTip relates to). + + The tip will not reappear if the cursor moves back; your + maybeTip() must reinstate it each time. +*/ + +void QToolTip::tip( const QRect & rect, const QString &text ) +{ + initTipManager(); + tipManager->add( parentWidget(), rect, text, 0, QString::null, 0, TRUE ); +} + +/*! + \overload + + Immediately pops up a tip saying \a text and removes that tip once + the cursor moves out of rectangle \a rect (which is given in the + coordinate system of the widget this QToolTip relates to). \a + groupText is the text emitted from the group. + + The tip will not reappear if the cursor moves back; your + maybeTip() must reinstate it each time. +*/ + +void QToolTip::tip( const QRect & rect, const QString &text, + const QString& groupText ) +{ + initTipManager(); + tipManager->add( parentWidget(), rect, text, group(), groupText, 0, TRUE ); +} + +/*! + \overload + + Immediately pops up a tip within the rectangle \a geometry, saying + \a text and removes the tip once the cursor moves out of rectangle + \a rect. Both rectangles are given in the coordinate system of the + widget this QToolTip relates to. + + The tip will not reappear if the cursor moves back; your + maybeTip() must reinstate it each time. + + If the tip does not fit inside \a geometry, the tip expands. +*/ + +void QToolTip::tip( const QRect &rect, const QString &text, const QRect &geometry ) +{ + initTipManager(); + tipManager->add( geometry, parentWidget(), rect, text, 0, QString::null, 0, TRUE ); +} + +/*! + \overload + + Immediately pops up a tip within the rectangle \a geometry, saying + \a text and removes the tip once the cursor moves out of rectangle + \a rect. \a groupText is the text emitted from the group. Both + rectangles are given in the coordinate system of the widget this + QToolTip relates to. + + The tip will not reappear if the cursor moves back; your + maybeTip() must reinstate it each time. + + If the tip does not fit inside \a geometry, the tip expands. +*/ + +void QToolTip::tip( const QRect &rect, const QString &text, const QString& groupText, const QRect &geometry ) +{ + initTipManager(); + tipManager->add( geometry, parentWidget(), rect, text, group(), groupText, 0, TRUE ); +} + + + +/*! + Immediately removes all tool tips for this tooltip's parent + widget. +*/ + +void QToolTip::clear() +{ + if ( tipManager ) + tipManager->remove( parentWidget() ); +} + + +/*! + \fn QWidget * QToolTip::parentWidget() const + + Returns the widget this QToolTip applies to. + + The tool tip is destroyed automatically when the parent widget is + destroyed. + + \sa group() +*/ + + +/*! + \fn QToolTipGroup * QToolTip::group() const + + Returns the tool tip group this QToolTip is a member of or 0 if it + isn't a member of any group. + + The tool tip group is the object responsible for maintaining + contact between tool tips and a \link QStatusBar status + bar\endlink or something else which can show the longer help text. + + \sa parentWidget(), QToolTipGroup +*/ + + +/*! + \class QToolTipGroup qtooltip.h + \brief The QToolTipGroup class collects tool tips into related groups. + + \ingroup helpsystem + + Tool tips can display \e two texts: one in the tip and + (optionally) one that is typically in a \link QStatusBar status + bar\endlink. QToolTipGroup provides a way to link tool tips to + this status bar. + + QToolTipGroup has practically no API; it is only used as an + argument to QToolTip's member functions, for example like this: + + \code + QToolTipGroup * grp = new QToolTipGroup( this, "tool tip relay" ); + connect( grp, SIGNAL(showTip(const QString&)), + myLabel, SLOT(setText(const QString&)) ); + connect( grp, SIGNAL(removeTip()), + myLabel, SLOT(clear()) ); + QToolTip::add( giraffeButton, "feed giraffe", + grp, "Give the giraffe a meal" ); + QToolTip::add( gorillaButton, "feed gorilla", + grp, "Give the gorilla a meal" ); + \endcode + + This example makes the object myLabel (which you must supply) + display (one assumes, though you can make myLabel do anything, of + course) the strings "Give the giraffe a meal" and "Give the + gorilla a meal" while the relevant tool tips are being displayed. + + Deleting a tool tip group removes the tool tips in it. +*/ + +/*! + \fn void QToolTipGroup::showTip (const QString &longText) + + This signal is emitted when one of the tool tips in the group is + displayed. \a longText is the extra text for the displayed tool + tip. + + \sa removeTip() +*/ + +/*! + \fn void QToolTipGroup::removeTip () + + This signal is emitted when a tool tip in this group is hidden. + See the QToolTipGroup documentation for an example of use. + + \sa showTip() +*/ + + +/*! + Constructs a tool tip group called \a name, with parent \a parent. +*/ + +QToolTipGroup::QToolTipGroup( QObject *parent, const char *name ) + : QObject( parent, name ) +{ + del = TRUE; + ena = TRUE; +} + + +/*! + Destroys this tool tip group and all tool tips in it. +*/ + +QToolTipGroup::~QToolTipGroup() +{ + if ( tipManager ) + tipManager->removeFromGroup( this ); +} + + +/*! + \property QToolTipGroup::delay + \brief whether the display of the group text is delayed. + + If set to TRUE (the default), the group text is displayed at the + same time as the tool tip. Otherwise, the group text is displayed + immediately when the cursor enters the widget. +*/ + +bool QToolTipGroup::delay() const +{ + return del; +} + +void QToolTipGroup::setDelay( bool enable ) +{ +#if 0 + if ( enable && !del ) { + // maybe we should show the text at once? + } +#endif + del = enable; +} + +/*! + \fn static void QToolTip::setEnabled( bool enable ) + + \obsolete +*/ +/*! + \fn static bool QToolTip::enabled() + + \obsolete +*/ +/*! + \property QToolTipGroup::enabled + \brief whether tool tips in the group are enabled. + + This property's default is TRUE. +*/ + +void QToolTipGroup::setEnabled( bool enable ) +{ + ena = enable; +} + +bool QToolTipGroup::enabled() const +{ + return (bool)ena; +} + +/*! + If \a enable is TRUE sets all tool tips to be enabled (shown when + needed); if \a enable is FALSE sets all tool tips to be disabled + (never shown). + + By default, tool tips are enabled. Note that this function affects + all tool tips in the entire application. + + \sa QToolTipGroup::setEnabled() +*/ + +void QToolTip::setGloballyEnabled( bool enable ) +{ + globally_enabled = enable; +} + +/*! + Returns whether tool tips are enabled globally. + + \sa setGloballyEnabled() +*/ +bool QToolTip::isGloballyEnabled() +{ + return globally_enabled; +} + +/*! + Sets the wakeup delay for all tooltips to \a i + milliseconds. +*/ +void QToolTip::setWakeUpDelay ( int i ) +{ + initTipManager(); + tipManager->setWakeUpDelay(i); +} + + +#include "qtooltip.moc" +#endif diff --git a/src/widgets/qtooltip.h b/src/widgets/qtooltip.h new file mode 100644 index 0000000..2cd38c1 --- /dev/null +++ b/src/widgets/qtooltip.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Definition of Tool Tips (or Balloon Help) for any widget or rectangle +** +** 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. +** +**********************************************************************/ + +#ifndef QTOOLTIP_H +#define QTOOLTIP_H + +#ifndef QT_H +#include "qwidget.h" +#endif // QT_H + +#ifndef QT_NO_TOOLTIP + +#if __GNUC__ - 0 > 3 +#pragma GCC system_header +#endif + +class QTipManager; +class QIconViewToolTip; +class QListViewToolTip; + +class Q_EXPORT QToolTipGroup: public QObject +{ + Q_OBJECT + Q_PROPERTY( bool delay READ delay WRITE setDelay ) + Q_PROPERTY( bool enabled READ enabled WRITE setEnabled ) + +public: + QToolTipGroup( QObject *parent, const char *name = 0 ); + ~QToolTipGroup(); + + bool delay() const; + bool enabled() const; + +public slots: + void setDelay( bool ); + void setEnabled( bool ); + +signals: + void showTip( const QString &); + void removeTip(); + +private: + uint del:1; + uint ena:1; + + friend class QTipManager; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QToolTipGroup( const QToolTipGroup & ); + QToolTipGroup& operator=( const QToolTipGroup & ); +#endif +}; + + +class Q_EXPORT QToolTip: public Qt +{ +public: + QToolTip( QWidget *, QToolTipGroup * = 0 ); + //### add virtual d'tor for 4.0 + + static void add( QWidget *, const QString &); + static void add( QWidget *, const QString &, + QToolTipGroup *, const QString& ); + static void remove( QWidget * ); + + static void add( QWidget *, const QRect &, const QString &); + static void add( QWidget *, const QRect &, const QString &, + QToolTipGroup *, const QString& ); + static void remove( QWidget *, const QRect & ); + + static QString textFor( QWidget *, const QPoint & pos = QPoint() ); + + static void hide(); + + static QFont font(); + static void setFont( const QFont & ); + static QPalette palette(); + static void setPalette( const QPalette & ); + +#ifndef QT_NO_COMPAT + static void setEnabled( bool enable ) { setGloballyEnabled( enable ); } + static bool enabled() { return isGloballyEnabled(); } +#endif + static void setGloballyEnabled( bool ); + static bool isGloballyEnabled(); + static void setWakeUpDelay(int); + +protected: + virtual void maybeTip( const QPoint & ) = 0; + void tip( const QRect &, const QString &); + void tip( const QRect &, const QString& , const QString &); + void tip( const QRect &, const QString &, const QRect & ); + void tip( const QRect &, const QString&, const QString &, const QRect &); + + void clear(); + +public: + QWidget *parentWidget() const { return p; } + QToolTipGroup *group() const { return g; } + +private: + QWidget *p; + QToolTipGroup *g; + static QFont *ttFont; + static QPalette *ttPalette; + + friend class QTipManager; +}; + + +#endif // QT_NO_TOOLTIP + +#endif // QTOOLTIP_H diff --git a/src/widgets/qvalidator.cpp b/src/widgets/qvalidator.cpp new file mode 100644 index 0000000..cc0eea4 --- /dev/null +++ b/src/widgets/qvalidator.cpp @@ -0,0 +1,672 @@ +/**************************************************************************** +** +** Implementation of validator classes +** +** Created : 970610 +** +** 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 "qvalidator.h" +#ifndef QT_NO_VALIDATOR + +#include <limits.h> +#include <math.h> + +/*! + \class QValidator + \brief The QValidator class provides validation of input text. + + \ingroup misc + \mainclass + + The class itself is abstract. Two subclasses, \l QIntValidator and + \l QDoubleValidator, provide basic numeric-range checking, and \l + QRegExpValidator provides general checking using a custom regular + expression. + + If the built-in validators aren't sufficient, you can subclass + QValidator. The class has two virtual functions: validate() and + fixup(). + + \l validate() must be implemented by every subclass. It returns + \c Invalid, \c Intermediate or \c Acceptable depending on whether + its argument is valid (for the subclass's definition of valid). + + These three states require some explanation. An \c Invalid string + is \e clearly invalid. \c Intermediate is less obvious: the + concept of validity is slippery when the string is incomplete + (still being edited). QValidator defines \c Intermediate as the + property of a string that is neither clearly invalid nor + acceptable as a final result. \c Acceptable means that the string + is acceptable as a final result. One might say that any string + that is a plausible intermediate state during entry of an \c + Acceptable string is \c Intermediate. + + Here are some examples: + + \list + + \i For a line edit that accepts integers from 0 to 999 inclusive, + 42 and 123 are \c Acceptable, the empty string and 1114 are \c + Intermediate and asdf is \c Invalid. + + \i For an editable combobox that accepts URLs, any well-formed URL + is \c Acceptable, "http://www.trolltech.com/," is \c Intermediate + (it might be a cut and paste operation that accidentally took in a + comma at the end), the empty string is \c Intermediate (the user + might select and delete all of the text in preparation for entering + a new URL), and "http:///./" is \c Invalid. + + \i For a spin box that accepts lengths, "11cm" and "1in" are \c + Acceptable, "11" and the empty string are \c Intermediate and + "http://www.trolltech.com" and "hour" are \c Invalid. + + \endlist + + \l fixup() is provided for validators that can repair some user + errors. The default implementation does nothing. QLineEdit, for + example, will call fixup() if the user presses Enter (or Return) + and the content is not currently valid. This allows the fixup() + function the opportunity of performing some magic to make an \c + Invalid string \c Acceptable. + + QValidator is typically used with QLineEdit, QSpinBox and + QComboBox. +*/ + + +/*! + \enum QValidator::State + + This enum type defines the states in which a validated string can + exist. + + \value Invalid the string is \e clearly invalid. + + \value Intermediate the string is a plausible intermediate value + during editing. + + \value Acceptable the string is acceptable as a final result, + i.e. it is valid. +*/ + + +/*! + Sets up the validator. The \a parent and \a name parameters are + passed on to the QObject constructor. +*/ + +QValidator::QValidator( QObject * parent, const char *name ) + : QObject( parent, name ) +{ +} + + +/*! + Destroys the validator, freeing any storage and other resources + used. +*/ + +QValidator::~QValidator() +{ +} + + +/*! + \fn QValidator::State QValidator::validate( QString& input, int& pos ) const + + This pure virtual function returns \c Invalid if \a input is + invalid according to this validator's rules, \c Intermediate if it + is likely that a little more editing will make the input + acceptable (e.g. the user types '4' into a widget which accepts + integers between 10 and 99) and \c Acceptable if the input is + valid. + + The function can change \a input and \a pos (the cursor position) + if it wants to. +*/ + + +/*! + \fn void QValidator::fixup( QString & input ) const + + This function attempts to change \a input to be valid according to + this validator's rules. It need not result in a valid string: + callers of this function must re-test afterwards; the default does + nothing. + + Reimplementations of this function can change \a input even if + they do not produce a valid string. For example, an ISBN validator + might want to delete every character except digits and "-", even + if the result is still not a valid ISBN; a surname validator might + want to remove whitespace from the start and end of the string, + even if the resulting string is not in the list of accepted + surnames. +*/ + +void QValidator::fixup( QString & ) const +{ +} + + +/*! + \class QIntValidator + \brief The QIntValidator class provides a validator which ensures + that a string contains a valid integer within a specified range. + + \ingroup misc + + Example of use: + + \code + QValidator* validator = new QIntValidator( 100, 999, this ); + QLineEdit* edit = new QLineEdit( this ); + + // the edit lineedit will only accept integers between 100 and 999 + edit->setValidator( validator ); + \endcode + + Below we present some examples of validators. In practice they would + normally be associated with a widget as in the example above. + + \code + QString str; + int pos = 0; + QIntValidator v( 100, 999, this ); + + str = "1"; + v.validate( str, pos ); // returns Intermediate + str = "12"; + v.validate( str, pos ); // returns Intermediate + + str = "123"; + v.validate( str, pos ); // returns Acceptable + str = "678"; + v.validate( str, pos ); // returns Acceptable + + str = "1234"; + v.validate( str, pos ); // returns Invalid + str = "-123"; + v.validate( str, pos ); // returns Invalid + str = "abc"; + v.validate( str, pos ); // returns Invalid + str = "12cm"; + v.validate( str, pos ); // returns Invalid + \endcode + + The minimum and maximum values are set in one call with setRange() + or individually with setBottom() and setTop(). + + \sa QDoubleValidator QRegExpValidator +*/ + + +/*! + Constructs a validator called \a name with parent \a parent, that + accepts all integers. +*/ + +QIntValidator::QIntValidator( QObject * parent, const char *name ) + : QValidator( parent, name ) +{ + b = INT_MIN; + t = INT_MAX; +} + + +/*! + Constructs a validator called \a name with parent \a parent, that + accepts integers from \a minimum to \a maximum inclusive. +*/ + +QIntValidator::QIntValidator( int minimum, int maximum, + QObject * parent, const char* name ) + : QValidator( parent, name ) +{ + b = minimum; + t = maximum; +} + + +/*! + Destroys the validator, freeing any resources allocated. +*/ + +QIntValidator::~QIntValidator() +{ + // nothing +} + + +/*! + Returns \c Acceptable if the \a input is an integer within the + valid range, \c Intermediate if the \a input is an integer outside + the valid range and \c Invalid if the \a input is not an integer. + + Note: If the valid range consists of just positive integers (e.g. 32 - 100) + and \a input is a negative integer then Invalid is returned. + + \code + int pos = 0; + s = "abc"; + v.validate( s, pos ); // returns Invalid + + s = "5"; + v.validate( s, pos ); // returns Intermediate + + s = "50"; + v.validate( s, pos ); // returns Valid + \endcode +*/ + +QValidator::State QIntValidator::validate( QString & input, int & ) const +{ + QString stripped = input.stripWhiteSpace(); + if ( stripped.isEmpty() || (b < 0 && stripped == "-") ) + return Intermediate; + bool ok; + long entered = input.toLong( &ok ); + if ( !ok || (entered < 0 && b >= 0) ) { + return Invalid; + } else if ( entered >= b && entered <= t ) { + return Acceptable; + } else { + if ( entered >= 0 ) + return ( entered > t ) ? Invalid : Intermediate; + else + return ( entered < b ) ? Invalid : Intermediate; + } +} + + +/*! + Sets the range of the validator to only accept integers between \a + bottom and \a top inclusive. +*/ + +void QIntValidator::setRange( int bottom, int top ) +{ + b = bottom; + t = top; +} + + +/*! + \property QIntValidator::bottom + \brief the validator's lowest acceptable value + + \sa setRange() +*/ +void QIntValidator::setBottom( int bottom ) +{ + setRange( bottom, top() ); +} + +/*! + \property QIntValidator::top + \brief the validator's highest acceptable value + + \sa setRange() +*/ +void QIntValidator::setTop( int top ) +{ + setRange( bottom(), top ); +} + + +#ifndef QT_NO_REGEXP + +/*! + \class QDoubleValidator + + \brief The QDoubleValidator class provides range checking of + floating-point numbers. + + \ingroup misc + + QDoubleValidator provides an upper bound, a lower bound and a + limit on the number of digits after the decimal point. It does not + provide a fixup() function. + + You can set the acceptable range in one call with setRange(), or + with setBottom() and setTop(). Set the number of decimal places + with setDecimals(). The validate() function returns the validation + state. + + \sa QIntValidator QRegExpValidator +*/ + +/*! + Constructs a validator object with parent \a parent, called \a + name, which accepts any double. +*/ + +QDoubleValidator::QDoubleValidator( QObject * parent, const char *name ) + : QValidator( parent, name ) +{ + b = -HUGE_VAL; + t = HUGE_VAL; + d = 1000; +} + + +/*! + Constructs a validator object with parent \a parent, called \a + name. This validator will accept doubles from \a bottom to \a top + inclusive, with up to \a decimals digits after the decimal point. +*/ + +QDoubleValidator::QDoubleValidator( double bottom, double top, int decimals, + QObject * parent, const char* name ) + : QValidator( parent, name ) +{ + b = bottom; + t = top; + d = decimals; +} + + +/*! + Destroys the validator, freeing any resources used. +*/ + +QDoubleValidator::~QDoubleValidator() +{ +} + + +/*! + Returns \c Acceptable if the string \a input contains a double + that is within the valid range and is in the correct format. + + Returns \c Intermediate if \a input contains a double that is + outside the range or is in the wrong format, e.g. with too many + digits after the decimal point or is empty. + + Returns \c Invalid if the \a input is not a double. + + Note: If the valid range consists of just positive doubles (e.g. 0.0 - 100.0) + and \a input is a negative double then Invalid is returned. +*/ + +QValidator::State QDoubleValidator::validate( QString & input, int & ) const +{ + QRegExp empty( QString::fromLatin1(" *-?\\.? *") ); + if ( b >= 0 && + input.stripWhiteSpace().startsWith(QString::fromLatin1("-")) ) + return Invalid; + if ( empty.exactMatch(input) ) + return Intermediate; + bool ok = TRUE; + double entered = input.toDouble( &ok ); + int nume = input.contains( 'e', FALSE ); + if ( !ok ) { + // explicit exponent regexp + QRegExp expexpexp( QString::fromLatin1("[Ee][+-]?\\d*$") ); + int eeePos = expexpexp.search( input ); + if ( eeePos > 0 && nume == 1 ) { + QString mantissa = input.left( eeePos ); + entered = mantissa.toDouble( &ok ); + if ( !ok ) + return Invalid; + } else if ( eeePos == 0 ) { + return Intermediate; + } else { + return Invalid; + } + } + + int i = input.find( '.' ); + if ( i >= 0 && nume == 0 ) { + // has decimal point (but no E), now count digits after that + i++; + int j = i; + while( input[j].isDigit() ) + j++; + if ( j - i > d ) + return Intermediate; + } + + if ( entered < b || entered > t ) + return Intermediate; + else + return Acceptable; +} + + +/*! + Sets the validator to accept doubles from \a minimum to \a maximum + inclusive, with at most \a decimals digits after the decimal + point. +*/ + +void QDoubleValidator::setRange( double minimum, double maximum, int decimals ) +{ + b = minimum; + t = maximum; + d = decimals; +} + +/*! + \property QDoubleValidator::bottom + \brief the validator's minimum acceptable value + + \sa setRange() +*/ + +void QDoubleValidator::setBottom( double bottom ) +{ + setRange( bottom, top(), decimals() ); +} + + +/*! + \property QDoubleValidator::top + \brief the validator's maximum acceptable value + + \sa setRange() +*/ + +void QDoubleValidator::setTop( double top ) +{ + setRange( bottom(), top, decimals() ); +} + +/*! + \property QDoubleValidator::decimals + \brief the validator's maximum number of digits after the decimal point + + \sa setRange() +*/ + +void QDoubleValidator::setDecimals( int decimals ) +{ + setRange( bottom(), top(), decimals ); +} + + +/*! + \class QRegExpValidator + \brief The QRegExpValidator class is used to check a string + against a regular expression. + + \ingroup misc + + QRegExpValidator contains a regular expression, "regexp", used to + determine whether an input string is \c Acceptable, \c + Intermediate or \c Invalid. + + The regexp is treated as if it begins with the start of string + assertion, <b>^</b>, and ends with the end of string assertion + <b>$</b> so the match is against the entire input string, or from + the given position if a start position greater than zero is given. + + For a brief introduction to Qt's regexp engine see \l QRegExp. + + Example of use: + \code + // regexp: optional '-' followed by between 1 and 3 digits + QRegExp rx( "-?\\d{1,3}" ); + QValidator* validator = new QRegExpValidator( rx, this ); + + QLineEdit* edit = new QLineEdit( this ); + edit->setValidator( validator ); + \endcode + + Below we present some examples of validators. In practice they would + normally be associated with a widget as in the example above. + + \code + // integers 1 to 9999 + QRegExp rx( "[1-9]\\d{0,3}" ); + // the validator treats the regexp as "^[1-9]\\d{0,3}$" + QRegExpValidator v( rx, 0 ); + QString s; + int pos = 0; + + s = "0"; v.validate( s, pos ); // returns Invalid + s = "12345"; v.validate( s, pos ); // returns Invalid + s = "1"; v.validate( s, pos ); // returns Acceptable + + rx.setPattern( "\\S+" ); // one or more non-whitespace characters + v.setRegExp( rx ); + s = "myfile.txt"; v.validate( s, pos ); // Returns Acceptable + s = "my file.txt"; v.validate( s, pos ); // Returns Invalid + + // A, B or C followed by exactly five digits followed by W, X, Y or Z + rx.setPattern( "[A-C]\\d{5}[W-Z]" ); + v.setRegExp( rx ); + s = "a12345Z"; v.validate( s, pos ); // Returns Invalid + s = "A12345Z"; v.validate( s, pos ); // Returns Acceptable + s = "B12"; v.validate( s, pos ); // Returns Intermediate + + // match most 'readme' files + rx.setPattern( "read\\S?me(\.(txt|asc|1st))?" ); + rx.setCaseSensitive( FALSE ); + v.setRegExp( rx ); + s = "readme"; v.validate( s, pos ); // Returns Acceptable + s = "README.1ST"; v.validate( s, pos ); // Returns Acceptable + s = "read me.txt"; v.validate( s, pos ); // Returns Invalid + s = "readm"; v.validate( s, pos ); // Returns Intermediate + \endcode + + \sa QRegExp QIntValidator QDoubleValidator +*/ + +/*! + Constructs a validator that accepts any string (including an empty + one) as valid. The object's parent is \a parent and its name is \a + name. +*/ + +QRegExpValidator::QRegExpValidator( QObject *parent, const char *name ) + : QValidator( parent, name ), r( QString::fromLatin1(".*") ) +{ +} + +/*! + Constructs a validator which accepts all strings that match the + regular expression \a rx. The object's parent is \a parent and its + name is \a name. + + The match is made against the entire string, e.g. if the regexp is + <b>[A-Fa-f0-9]+</b> it will be treated as <b>^[A-Fa-f0-9]+$</b>. +*/ + +QRegExpValidator::QRegExpValidator( const QRegExp& rx, QObject *parent, + const char *name ) + : QValidator( parent, name ), r( rx ) +{ +} + +/*! + Destroys the validator, freeing any resources allocated. +*/ + +QRegExpValidator::~QRegExpValidator() +{ +} + +/*! + Returns \c Acceptable if \a input is matched by the regular + expression for this validator, \c Intermediate if it has matched + partially (i.e. could be a valid match if additional valid + characters are added), and \c Invalid if \a input is not matched. + + The \a pos parameter is set to the length of the \a input parameter. + + For example, if the regular expression is <b>\\w\\d\\d</b> (that + is, word-character, digit, digit) then "A57" is \c Acceptable, + "E5" is \c Intermediate and "+9" is \c Invalid. + + \sa QRegExp::match() QRegExp::search() +*/ + +QValidator::State QRegExpValidator::validate( QString& input, int& pos ) const +{ + if ( r.exactMatch(input) ) { + return Acceptable; + } else { + if ( ((QRegExp&) r).matchedLength() == (int) input.length() ) { + return Intermediate; + } else { + pos = input.length(); + return Invalid; + } + } +} + +/*! + Sets the regular expression used for validation to \a rx. + + \sa regExp() +*/ + +void QRegExpValidator::setRegExp( const QRegExp& rx ) +{ + r = rx; +} + +/*! + \fn const QRegExp& QRegExpValidator::regExp() const + + Returns the regular expression used for validation. + + \sa setRegExp() +*/ + +#endif + +#endif diff --git a/src/widgets/qvalidator.h b/src/widgets/qvalidator.h new file mode 100644 index 0000000..1e4310c --- /dev/null +++ b/src/widgets/qvalidator.h @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** Definition of validator classes +** +** Created : 970610 +** +** 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. +** +**********************************************************************/ + +#ifndef QVALIDATOR_H +#define QVALIDATOR_H + +#ifndef QT_H +#include "qobject.h" +#include "qstring.h" // char*->QString conversion +#include "qregexp.h" // QString->QRegExp conversion +#endif // QT_H + +#ifndef QT_NO_VALIDATOR + + +class Q_EXPORT QValidator : public QObject +{ + Q_OBJECT +public: + QValidator( QObject * parent, const char *name = 0 ); + ~QValidator(); + + enum State { Invalid, Intermediate, Valid=Intermediate, Acceptable }; + + virtual State validate( QString &, int & ) const = 0; + virtual void fixup( QString & ) const; + +private: +#if defined(Q_DISABLE_COPY) + QValidator( const QValidator & ); + QValidator& operator=( const QValidator & ); +#endif +}; + + +class Q_EXPORT QIntValidator : public QValidator +{ + Q_OBJECT + Q_PROPERTY( int bottom READ bottom WRITE setBottom ) + Q_PROPERTY( int top READ top WRITE setTop ) + +public: + QIntValidator( QObject * parent, const char *name = 0 ); + QIntValidator( int bottom, int top, + QObject * parent, const char *name = 0 ); + ~QIntValidator(); + + QValidator::State validate( QString &, int & ) const; + + void setBottom( int ); + void setTop( int ); + virtual void setRange( int bottom, int top ); + + int bottom() const { return b; } + int top() const { return t; } + +private: +#if defined(Q_DISABLE_COPY) + QIntValidator( const QIntValidator & ); + QIntValidator& operator=( const QIntValidator & ); +#endif + + int b, t; +}; + +#ifndef QT_NO_REGEXP + +class Q_EXPORT QDoubleValidator : public QValidator +{ + Q_OBJECT + Q_PROPERTY( double bottom READ bottom WRITE setBottom ) + Q_PROPERTY( double top READ top WRITE setTop ) + Q_PROPERTY( int decimals READ decimals WRITE setDecimals ) + +public: + QDoubleValidator( QObject * parent, const char *name = 0 ); + QDoubleValidator( double bottom, double top, int decimals, + QObject * parent, const char *name = 0 ); + ~QDoubleValidator(); + + QValidator::State validate( QString &, int & ) const; + + virtual void setRange( double bottom, double top, int decimals = 0 ); + void setBottom( double ); + void setTop( double ); + void setDecimals( int ); + + double bottom() const { return b; } + double top() const { return t; } + int decimals() const { return d; } + +private: +#if defined(Q_DISABLE_COPY) + QDoubleValidator( const QDoubleValidator & ); + QDoubleValidator& operator=( const QDoubleValidator & ); +#endif + + double b, t; + int d; +}; + + +class Q_EXPORT QRegExpValidator : public QValidator +{ + Q_OBJECT + // Q_PROPERTY( QRegExp regExp READ regExp WRITE setRegExp ) + +public: + QRegExpValidator( QObject *parent, const char *name = 0 ); + QRegExpValidator( const QRegExp& rx, QObject *parent, + const char *name = 0 ); + ~QRegExpValidator(); + + virtual QValidator::State validate( QString& input, int& pos ) const; + + void setRegExp( const QRegExp& rx ); + const QRegExp& regExp() const { return r; } + +private: +#if defined(Q_DISABLE_COPY) + QRegExpValidator( const QRegExpValidator& ); + QRegExpValidator& operator=( const QRegExpValidator& ); +#endif + + QRegExp r; +}; +#endif // QT_NO_REGEXP + + +#endif // QT_NO_VALIDATOR + +#endif // QVALIDATOR_H diff --git a/src/widgets/qvbox.cpp b/src/widgets/qvbox.cpp new file mode 100644 index 0000000..82d1e12 --- /dev/null +++ b/src/widgets/qvbox.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Implementation of vertical box layout widget class +** +** Created : 990124 +** +** 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 "qvbox.h" +#ifndef QT_NO_VBOX + +/*! + \class QVBox qvbox.h + \brief The QVBox widget provides vertical geometry management of + its child widgets. + + \ingroup geomanagement + \ingroup appearance + \ingroup organizers + + All its child widgets will be placed vertically and sized + according to their sizeHint()s. + + \img qvbox-m.png QVBox + + \sa QHBox +*/ + + +/*! + Constructs a vbox widget called \a name with parent \a parent and + widget flags \a f. + */ +QVBox::QVBox( QWidget *parent, const char *name, WFlags f ) + :QHBox( FALSE, parent, name, f ) +{ +} +#endif diff --git a/src/widgets/qvbox.h b/src/widgets/qvbox.h new file mode 100644 index 0000000..8500f11 --- /dev/null +++ b/src/widgets/qvbox.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Definition of vertical box layout widget class +** +** Created : 990124 +** +** Copyright (C) 1999-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. +** +**********************************************************************/ + +#ifndef QVBOX_H +#define QVBOX_H + +#ifndef QT_H +#include "qhbox.h" +#endif // QT_H + +#ifndef QT_NO_VBOX + +class Q_EXPORT QVBox : public QHBox +{ + Q_OBJECT +public: + QVBox( QWidget* parent=0, const char* name=0, WFlags f=0 ); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QVBox( const QVBox & ); + QVBox& operator=( const QVBox & ); +#endif +}; + +#endif // QT_NO_VBOX + +#endif // QVBOX_H diff --git a/src/widgets/qvbuttongroup.cpp b/src/widgets/qvbuttongroup.cpp new file mode 100644 index 0000000..70247fd --- /dev/null +++ b/src/widgets/qvbuttongroup.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Implementation of QVButtonGroup class +** +** Created : 990602 +** +** 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 "qvbuttongroup.h" +#ifndef QT_NO_VBUTTONGROUP + +/*! + \class QVButtonGroup qvbuttongroup.h + \brief The QVButtonGroup widget organizes QButton widgets in a + vertical column. + + \ingroup geomanagement + \ingroup organizers + \ingroup appearance + + QVButtonGroup is a convenience class that offers a thin layer on top + of QButtonGroup. Think of it as a QVBox that offers a frame with a + title and is specifically designed for buttons. + + \img qbuttongroup-v.png QButtonGroup + + \sa QHButtonGroup +*/ + +/*! + Constructs a vertical button group with no title. + + The \a parent and \a name arguments are passed on to the QWidget + constructor. +*/ +QVButtonGroup::QVButtonGroup( QWidget *parent, const char *name ) + : QButtonGroup( 1, Horizontal /* sic! */, parent, name ) +{ +} + +/*! + Constructs a vertical button group with the title \a title. + + The \a parent and \a name arguments are passed on to the QWidget + constructor. +*/ + +QVButtonGroup::QVButtonGroup( const QString &title, QWidget *parent, + const char *name ) + : QButtonGroup( 1, Horizontal /* sic! */, title, parent, name ) +{ +} + +/*! + Destroys the vertical button group, deleting its child widgets. +*/ +QVButtonGroup::~QVButtonGroup() +{ +} +#endif diff --git a/src/widgets/qvbuttongroup.h b/src/widgets/qvbuttongroup.h new file mode 100644 index 0000000..9aab80e --- /dev/null +++ b/src/widgets/qvbuttongroup.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Definition of QVButtonGroup class +** +** Created : 990602 +** +** Copyright (C) 1999-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. +** +**********************************************************************/ + +#ifndef QVBUTTONGROUP_H +#define QVBUTTONGROUP_H + +#ifndef QT_H +#include "qbuttongroup.h" +#endif // QT_H + +#ifndef QT_NO_VBUTTONGROUP + +class Q_EXPORT QVButtonGroup : public QButtonGroup +{ + Q_OBJECT +public: + QVButtonGroup( QWidget* parent=0, const char* name=0 ); + QVButtonGroup( const QString &title, QWidget* parent=0, const char* name=0 ); + + ~QVButtonGroup(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QVButtonGroup( const QVButtonGroup & ); + QVButtonGroup &operator=( const QVButtonGroup & ); +#endif +}; + + +#endif // QT_NO_VBUTTONGROUP + +#endif // QVBUTTONGROUP_H diff --git a/src/widgets/qvgroupbox.cpp b/src/widgets/qvgroupbox.cpp new file mode 100644 index 0000000..6ef633b --- /dev/null +++ b/src/widgets/qvgroupbox.cpp @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Implementation of QVGroupBox class +** +** Created : 990602 +** +** 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 "qvgroupbox.h" +#ifndef QT_NO_VGROUPBOX + +/*! + \class QVGroupBox qvgroupbox.h + \brief The QVGroupBox widget organizes a group of widgets in a + vertical column. + + \ingroup geomanagement + \ingroup appearance + \ingroup organizers + + QVGroupBox is a convenience class that offers a thin layer on top of + QGroupBox. Think of it as a QVBox that offers a frame with a title. + + \img qgroupboxes.png Group Boxes + + \sa QHGroupBox +*/ + +/*! + Constructs a vertical group box with no title. + + The \a parent and \a name arguments are passed on to the QWidget + constructor. +*/ +QVGroupBox::QVGroupBox( QWidget *parent, const char *name ) + : QGroupBox( 1, Horizontal /* sic! */, parent, name ) +{ +} + +/*! + Constructs a vertical group box with the title \a title. + + The \a parent and \a name arguments are passed on to the QWidget + constructor. +*/ + +QVGroupBox::QVGroupBox( const QString &title, QWidget *parent, + const char *name ) + : QGroupBox( 1, Horizontal /* sic! */, title, parent, name ) +{ +} + +/*! + Destroys the vertical group box, deleting its child widgets. +*/ +QVGroupBox::~QVGroupBox() +{ +} +#endif diff --git a/src/widgets/qvgroupbox.h b/src/widgets/qvgroupbox.h new file mode 100644 index 0000000..ca9eb0a --- /dev/null +++ b/src/widgets/qvgroupbox.h @@ -0,0 +1,68 @@ +/********************************************************************** +** +** Definition of QVGroupBox widget class +** +** Created : 990602 +** +** Copyright (C) 1999-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. +** +**********************************************************************/ + +#ifndef QVGROUPBOX_H +#define QVGROUPBOX_H + +#ifndef QT_H +#include "qgroupbox.h" +#endif // QT_H + +#ifndef QT_NO_VGROUPBOX + +class Q_EXPORT QVGroupBox : public QGroupBox +{ + Q_OBJECT +public: + QVGroupBox( QWidget* parent=0, const char* name=0 ); + QVGroupBox( const QString &title, QWidget* parent=0, const char* name=0 ); + + ~QVGroupBox(); + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QVGroupBox( const QVGroupBox & ); + QVGroupBox &operator=( const QVGroupBox & ); +#endif +}; + +#endif // QT_NO_VGROUPBOX + +#endif // QVGROUPBOX_H diff --git a/src/widgets/qwhatsthis.cpp b/src/widgets/qwhatsthis.cpp new file mode 100644 index 0000000..c6e8f04 --- /dev/null +++ b/src/widgets/qwhatsthis.cpp @@ -0,0 +1,1001 @@ +/**************************************************************************** +** +** Implementation of QWhatsThis class +** +** 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 "qwhatsthis.h" +#ifndef QT_NO_WHATSTHIS +#include "qapplication.h" +#include "qpaintdevicemetrics.h" +#include "qpixmap.h" +#include "qpainter.h" +#include "qtimer.h" +#include "qptrdict.h" +#include "qtoolbutton.h" +#include "qshared.h" +#include "qcursor.h" +#include "qbitmap.h" +#include "qtooltip.h" +#include "qsimplerichtext.h" +#include "qstylesheet.h" +#if defined(QT_ACCESSIBILITY_SUPPORT) +#include "qaccessible.h" +#endif +#if defined(Q_WS_WIN) +#include "qt_windows.h" +#ifndef SPI_GETDROPSHADOW +#define SPI_GETDROPSHADOW 0x1024 +#endif +#endif + +/*! + \class QWhatsThis qwhatsthis.h + \brief The QWhatsThis class provides a simple description of any + widget, i.e. answering the question "What's this?". + + \ingroup helpsystem + \mainclass + + "What's this?" help is part of an application's online help system + that provides users with information about functionality, usage, + background etc., in various levels of detail from short tool tips + to full text browsing help windows. + + QWhatsThis provides a single window with an explanatory text that + pops up when the user asks "What's this?". The default way to do + this is to focus the relevant widget and press Shift+F1. The help + text appears immediately; it goes away as soon as the user does + something else. + + (Note that if there is an accelerator for Shift+F1, this mechanism + will not work.) + + To add "What's this?" text to a widget you simply call + QWhatsThis::add() for the widget. For example, to assign text to a + menu item, call QMenuData::setWhatsThis(); for a global + accelerator key, call QAccel::setWhatsThis() and If you're using + actions, use QAction::setWhatsThis(). + + The text can be either rich text or plain text. If you specify a + rich text formatted string, it will be rendered using the default + stylesheet. This makes it possible to embed images. See + QStyleSheet::defaultSheet() for details. + + \quotefile action/application.cpp + \skipto fileOpenText + \printuntil setWhatsThis + + An alternative way to enter "What's this?" mode is to use the + ready-made tool bar tool button from + QWhatsThis::whatsThisButton(). By invoking this context help + button (in the picture below the first one from the right) the + user switches into "What's this?" mode. If they now click on a + widget the appropriate help text is shown. The mode is left when + help is given or when the user presses Esc. + + \img whatsthis.png + + If you are using QMainWindow you can also use the + QMainWindow::whatsThis() slot to invoke the mode from a menu item. + + For more control you can create a dedicated QWhatsThis object for + a special widget. By subclassing and reimplementing + QWhatsThis::text() it is possible to have different help texts, + depending on the position of the mouse click. By reimplementing + QWhatsThis::clicked() it is possible to have hyperlinks inside the + help texts. + + If you wish to control the "What's this?" behavior of a widget + manually see QWidget::customWhatsThis(). + + The What's This object can be removed using QWhatsThis::remove(), + although this is rarely necessary because it is automatically + removed when the widget is destroyed. + + \sa QToolTip +*/ + +// a special button +class QWhatsThisButton: public QToolButton +{ + Q_OBJECT + +public: + QWhatsThisButton( QWidget * parent, const char * name ); + ~QWhatsThisButton(); + +public slots: + void mouseReleased(); + +}; + + +class QWhatsThat : public QWidget +{ + Q_OBJECT +public: + QWhatsThat( QWidget* w, const QString& txt, QWidget* parent, const char* name ); + ~QWhatsThat() ; + +public slots: + void hide(); + inline void widgetDestroyed() { widget = 0; } + +protected: + void mousePressEvent( QMouseEvent* ); + void mouseReleaseEvent( QMouseEvent* ); + void mouseMoveEvent( QMouseEvent* ); + void keyPressEvent( QKeyEvent* ); + void paintEvent( QPaintEvent* ); + +private: + QString text; +#ifndef QT_NO_RICHTEXT + QSimpleRichText* doc; +#endif + QString anchor; + bool pressed; + QWidget* widget; +}; + + +class QWhatsThisPrivate: public QObject +{ + Q_OBJECT +public: + + // an item for storing texts + struct WhatsThisItem: public QShared + { + WhatsThisItem(): QShared() { whatsthis = 0; } + ~WhatsThisItem(); + QString s; + QWhatsThis* whatsthis; + }; + + // the (these days pretty small) state machine + enum State { Inactive, Waiting }; + + QWhatsThisPrivate(); + ~QWhatsThisPrivate(); + + bool eventFilter( QObject *, QEvent * ); + + WhatsThisItem* newItem( QWidget * widget ); + void add( QWidget * widget, QWhatsThis* special ); + void add( QWidget * widget, const QString& text ); + + // say it. + void say( QWidget *, const QString&, const QPoint& ); + + // setup and teardown + static void setUpWhatsThis(); + + void enterWhatsThisMode(); + void leaveWhatsThisMode(); + + // variables + QWhatsThat * whatsThat; + QPtrDict<WhatsThisItem> * dict; + QPtrDict<QWidget> * tlw; + QPtrDict<QWhatsThisButton> * buttons; + State state; + +private slots: + void cleanupWidget() + { + const QObject* o = sender(); + if ( o->isWidgetType() ) // sanity + QWhatsThis::remove((QWidget*)o); + } + +}; + +// static, but static the less-typing way +static QWhatsThisPrivate * wt = 0; + +// shadowWidth not const, for XP drop-shadow-fu turns it to 0 +static int shadowWidth = 6; // also used as '5' and '6' and even '8' below +const int vMargin = 8; +const int hMargin = 12; + +// Lets QPopupMenu destroy the QWhatsThat. +void qWhatsThisBDH() +{ + if ( wt && wt->whatsThat ) + wt->whatsThat->hide(); +} + + +QWhatsThat::QWhatsThat( QWidget* w, const QString& txt, QWidget* parent, const char* name ) + : QWidget( parent, name, WType_Popup ), text( txt ), pressed( FALSE ), widget( w ) +{ + + setBackgroundMode( NoBackground ); + setPalette( QToolTip::palette() ); + setMouseTracking( TRUE ); +#ifndef QT_NO_CURSOR + setCursor( arrowCursor ); +#endif + + if ( widget ) + connect( widget, SIGNAL( destroyed() ), this, SLOT( widgetDestroyed() ) ); + + + QRect r; +#ifndef QT_NO_RICHTEXT + doc = 0; + if ( QStyleSheet::mightBeRichText( text ) ) { + QFont f = QApplication::font( this ); + doc = new QSimpleRichText( text, f ); + doc->adjustSize(); + r.setRect( 0, 0, doc->width(), doc->height() ); + } + else +#endif + { + int sw = QApplication::desktop()->width() / 3; + if ( sw < 200 ) + sw = 200; + else if ( sw > 300 ) + sw = 300; + + r = fontMetrics().boundingRect( 0, 0, sw, 1000, + AlignAuto + AlignTop + WordBreak + ExpandTabs, + text ); + } +#if defined(Q_WS_WIN) + if ( (qWinVersion()&WV_NT_based) > WV_2000 ) { + BOOL shadow; + SystemParametersInfo( SPI_GETDROPSHADOW, 0, &shadow, 0 ); + shadowWidth = shadow ? 0 : 6; + } +#endif + resize( r.width() + 2*hMargin + shadowWidth, r.height() + 2*vMargin + shadowWidth ); +} + +QWhatsThat::~QWhatsThat() +{ + if ( wt && wt->whatsThat == this ) + wt->whatsThat = 0; +#ifndef QT_NO_RICHTEXT + if ( doc ) + delete doc; +#endif +} + +void QWhatsThat::hide() +{ + QWidget::hide(); +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ContextHelpEnd ); +#endif +} + +void QWhatsThat::mousePressEvent( QMouseEvent* e ) +{ + pressed = TRUE; + if ( e->button() == LeftButton && rect().contains( e->pos() ) ) { +#ifndef QT_NO_RICHTEXT + if ( doc ) + anchor = doc->anchorAt( e->pos() - QPoint( hMargin, vMargin) ); +#endif + return; + } + hide(); +} + +void QWhatsThat::mouseReleaseEvent( QMouseEvent* e ) +{ + if ( !pressed ) + return; +#ifndef QT_NO_RICHTEXT + if ( e->button() == LeftButton && doc && rect().contains( e->pos() ) ) { + QString a = doc->anchorAt( e->pos() - QPoint( hMargin, vMargin ) ); + QString href; + if ( anchor == a ) + href = a; + anchor = QString::null; + if ( widget && wt && wt->dict ) { + QWhatsThisPrivate::WhatsThisItem * i = wt->dict->find( widget ); + if ( i && i->whatsthis && !i->whatsthis->clicked( href ) ) + return; + } + } +#endif + hide(); +} + +void QWhatsThat::mouseMoveEvent( QMouseEvent* e) +{ +#ifndef QT_NO_RICHTEXT +#ifndef QT_NO_CURSOR + if ( !doc ) + return; + QString a = doc->anchorAt( e->pos() - QPoint( hMargin, vMargin ) ); + if ( !a.isEmpty() ) + setCursor( pointingHandCursor ); + else + setCursor( arrowCursor ); +#endif +#endif +} + + +void QWhatsThat::keyPressEvent( QKeyEvent* ) +{ + hide(); +} + + + +void QWhatsThat::paintEvent( QPaintEvent* ) +{ + bool drawShadow = TRUE; +#if defined(Q_WS_WIN) + if ( (qWinVersion()&WV_NT_based) > WV_2000 ) { + BOOL shadow; + SystemParametersInfo( SPI_GETDROPSHADOW, 0, &shadow, 0 ); + drawShadow = !shadow; + } +#elif defined(Q_WS_MACX) + drawShadow = FALSE; //never draw it on OS X we get it for free +#endif + + QRect r = rect(); + if ( drawShadow ) + r.addCoords( 0, 0, -shadowWidth, -shadowWidth ); + QPainter p( this); + p.setPen( colorGroup().foreground() ); + p.drawRect( r ); + p.setPen( colorGroup().mid() ); + p.setBrush( colorGroup().brush( QColorGroup::Background ) ); + int w = r.width(); + int h = r.height(); + p.drawRect( 1, 1, w-2, h-2 ); + if ( drawShadow ) { + p.setPen( colorGroup().shadow() ); + p.drawPoint( w + 5, 6 ); + p.drawLine( w + 3, 6, w + 5, 8 ); + p.drawLine( w + 1, 6, w + 5, 10 ); + int i; + for( i=7; i < h; i += 2 ) + p.drawLine( w, i, w + 5, i + 5 ); + for( i = w - i + h; i > 6; i -= 2 ) + p.drawLine( i, h, i + 5, h + 5 ); + for( ; i > 0 ; i -= 2 ) + p.drawLine( 6, h + 6 - i, i + 5, h + 5 ); + } + p.setPen( colorGroup().foreground() ); + r.addCoords( hMargin, vMargin, -hMargin, -vMargin ); + +#ifndef QT_NO_RICHTEXT + if ( doc ) { + doc->draw( &p, r.x(), r.y(), r, colorGroup(), 0 ); + } + else +#endif + { + p.drawText( r, AlignAuto + AlignTop + WordBreak + ExpandTabs, text ); + } +} + +// the item +QWhatsThisPrivate::WhatsThisItem::~WhatsThisItem() +{ + if ( count ) + qFatal( "QWhatsThis: Internal error (%d)", count ); + delete whatsthis; +} + + +static const char * const button_image[] = { +"16 16 3 1", +" c None", +"o c #000000", +"a c #000080", +"o aaaaa ", +"oo aaa aaa ", +"ooo aaa aaa", +"oooo aa aa", +"ooooo aa aa", +"oooooo a aaa", +"ooooooo aaa ", +"oooooooo aaa ", +"ooooooooo aaa ", +"ooooo aaa ", +"oo ooo ", +"o ooo aaa ", +" ooo aaa ", +" ooo ", +" ooo ", +" ooo "}; + +// the button class +QWhatsThisButton::QWhatsThisButton( QWidget * parent, const char * name ) + : QToolButton( parent, name ) +{ + QPixmap p( (const char**)button_image ); + setPixmap( p ); + setToggleButton( TRUE ); + setAutoRaise( TRUE ); + setFocusPolicy( NoFocus ); + setTextLabel( tr( "What's this?" ) ); + wt->buttons->insert( (void *)this, this ); + connect( this, SIGNAL( released() ), + this, SLOT( mouseReleased() ) ); +} + + +QWhatsThisButton::~QWhatsThisButton() +{ + if ( wt && wt->buttons ) + wt->buttons->take( (void *)this ); +} + + +void QWhatsThisButton::mouseReleased() +{ + if ( wt->state == QWhatsThisPrivate::Inactive && isOn() ) { + QWhatsThisPrivate::setUpWhatsThis(); +#ifndef QT_NO_CURSOR + QApplication::setOverrideCursor( whatsThisCursor, FALSE ); +#endif + wt->state = QWhatsThisPrivate::Waiting; + qApp->installEventFilter( wt ); + } +} + +static void qWhatsThisPrivateCleanup() +{ + if( wt ) { + delete wt; + wt = 0; + } +} + +// the what's this manager class +QWhatsThisPrivate::QWhatsThisPrivate() + : QObject( 0, "global what's this object" ) +{ + whatsThat = 0; + dict = new QPtrDict<QWhatsThisPrivate::WhatsThisItem>; + tlw = new QPtrDict<QWidget>; + wt = this; + buttons = new QPtrDict<QWhatsThisButton>; + state = Inactive; +} + +QWhatsThisPrivate::~QWhatsThisPrivate() +{ +#ifndef QT_NO_CURSOR + if ( state == Waiting && qApp ) + QApplication::restoreOverrideCursor(); +#endif + // the two straight-and-simple dicts + delete tlw; + delete buttons; + + // then delete the complex one. + QPtrDictIterator<WhatsThisItem> it( *dict ); + WhatsThisItem * i; + QWidget * w; + while( (i=it.current()) != 0 ) { + w = (QWidget *)it.currentKey(); + ++it; + dict->take( w ); + if ( i->deref() ) + delete i; + } + delete dict; + if ( whatsThat && !whatsThat->parentWidget() ) { + delete whatsThat; + } + // and finally lose wt + wt = 0; +} + +bool QWhatsThisPrivate::eventFilter( QObject * o, QEvent * e ) +{ + switch( state ) { + case Waiting: + if ( e->type() == QEvent::MouseButtonPress && o->isWidgetType() ) { + QWidget * w = (QWidget *) o; + if ( ( (QMouseEvent*)e)->button() == RightButton ) + return FALSE; // ignore RMB + if ( w->customWhatsThis() ) + return FALSE; + QWhatsThisPrivate::WhatsThisItem * i = 0; + QMouseEvent* me = (QMouseEvent*) e; + QPoint p = me->pos(); + while( w && !i ) { + i = dict->find( w ); + if ( !i ) { + p += w->pos(); + w = w->parentWidget( TRUE ); + } + } + leaveWhatsThisMode(); + if (!i ) { +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ContextHelpEnd ); +#endif + return TRUE; + } + if ( i->whatsthis ) + say( w, i->whatsthis->text( p ), me->globalPos() ); + else + say( w, i->s, me->globalPos() ); + return TRUE; + } else if ( e->type() == QEvent::MouseButtonRelease ) { + if ( ( (QMouseEvent*)e)->button() == RightButton ) + return FALSE; // ignore RMB + return !o->isWidgetType() || !((QWidget*)o)->customWhatsThis(); + } else if ( e->type() == QEvent::MouseMove ) { + return !o->isWidgetType() || !((QWidget*)o)->customWhatsThis(); + } else if ( e->type() == QEvent::KeyPress ) { + QKeyEvent* kev = (QKeyEvent*)e; + + if ( kev->key() == Qt::Key_Escape ) { + leaveWhatsThisMode(); + return TRUE; + } else if ( o->isWidgetType() && ((QWidget*)o)->customWhatsThis() ) { + return FALSE; + } else if ( kev->key() == Key_Menu || + ( kev->key() == Key_F10 && + kev->state() == ShiftButton ) ) { + // we don't react to these keys, they are used for context menus + return FALSE; + } else if ( kev->state() == kev->stateAfter() && + kev->key() != Key_Meta ) { // not a modifier key + leaveWhatsThisMode(); + } + } else if ( e->type() == QEvent::MouseButtonDblClick ) { + return TRUE; + } + break; + case Inactive: + if ( e->type() == QEvent::Accel && + ((QKeyEvent *)e)->key() == Key_F1 && + o->isWidgetType() && + ((QKeyEvent *)e)->state() == ShiftButton ) { + QWidget * w = ((QWidget *)o)->focusWidget(); + if ( !w ) + break; + QString s = QWhatsThis::textFor( w, QPoint(0,0), TRUE ); + if ( !s.isNull() ) { + say ( w, s, w->mapToGlobal( w->rect().center() ) ); + ((QKeyEvent *)e)->accept(); + return TRUE; + } + } + break; + } + return FALSE; +} + + + +void QWhatsThisPrivate::setUpWhatsThis() +{ + if ( !wt ) { + wt = new QWhatsThisPrivate(); + + // It is necessary to use a post routine, because + // the destructor deletes pixmaps and other stuff that + // needs a working X connection under X11. + qAddPostRoutine( qWhatsThisPrivateCleanup ); + } +} + + +void QWhatsThisPrivate::enterWhatsThisMode() +{ +#if defined(QT_ACCESSIBILITY_SUPPORT) + QAccessible::updateAccessibility( this, 0, QAccessible::ContextHelpStart ); +#endif +} + + +void QWhatsThisPrivate::leaveWhatsThisMode() +{ + if ( state == Waiting ) { + QPtrDictIterator<QWhatsThisButton> it( *(wt->buttons) ); + QWhatsThisButton * b; + while( (b=it.current()) != 0 ) { + ++it; + b->setOn( FALSE ); + } +#ifndef QT_NO_CURSOR + QApplication::restoreOverrideCursor(); +#endif + state = Inactive; + qApp->removeEventFilter( this ); + } +} + + + +void QWhatsThisPrivate::say( QWidget * widget, const QString &text, const QPoint& ppos) +{ + if ( text.isEmpty() ) + return; + // make a fresh widget, and set it up + delete whatsThat; + whatsThat = new QWhatsThat( + widget, text, +#if defined(Q_WS_X11) + QApplication::desktop()->screen( widget ? + widget->x11Screen() : + QCursor::x11Screen() ), +#else + 0, +#endif + "automatic what's this? widget" ); + + + // okay, now to find a suitable location + + int scr = ( widget ? + QApplication::desktop()->screenNumber( widget ) : +#if defined(Q_WS_X11) + QCursor::x11Screen() +#else + QApplication::desktop()->screenNumber( ppos ) +#endif // Q_WS_X11 + ); + QRect screen = QApplication::desktop()->screenGeometry( scr ); + + int x; + int w = whatsThat->width(); + int h = whatsThat->height(); + int sx = screen.x(); + int sy = screen.y(); + + // first try locating the widget immediately above/below, + // with nice alignment if possible. + QPoint pos; + if ( widget ) + pos = widget->mapToGlobal( QPoint( 0,0 ) ); + + if ( widget && w > widget->width() + 16 ) + x = pos.x() + widget->width()/2 - w/2; + else + x = ppos.x() - w/2; + + // squeeze it in if that would result in part of what's this + // being only partially visible + if ( x + w + shadowWidth > sx+screen.width() ) + x = (widget? (QMIN(screen.width(), + pos.x() + widget->width()) + ) : screen.width() ) + - w; + + if ( x < sx ) + x = sx; + + int y; + if ( widget && h > widget->height() + 16 ) { + y = pos.y() + widget->height() + 2; // below, two pixels spacing + // what's this is above or below, wherever there's most space + if ( y + h + 10 > sy+screen.height() ) + y = pos.y() + 2 - shadowWidth - h; // above, overlap + } + y = ppos.y() + 2; + + // squeeze it in if that would result in part of what's this + // being only partially visible + if ( y + h + shadowWidth > sy+screen.height() ) + y = ( widget ? (QMIN(screen.height(), + pos.y() + widget->height()) + ) : screen.height() ) + - h; + if ( y < sy ) + y = sy; + + whatsThat->move( x, y ); + whatsThat->show(); +} + +QWhatsThisPrivate::WhatsThisItem* QWhatsThisPrivate::newItem( QWidget * widget ) +{ + WhatsThisItem * i = dict->find( (void *)widget ); + if ( i ) + QWhatsThis::remove( widget ); + i = new WhatsThisItem; + dict->insert( (void *)widget, i ); + QWidget * t = widget->topLevelWidget(); + if ( !tlw->find( (void *)t ) ) { + tlw->insert( (void *)t, t ); + t->installEventFilter( this ); + } + connect( widget, SIGNAL(destroyed()), this, SLOT(cleanupWidget()) ); + return i; +} + +void QWhatsThisPrivate::add( QWidget * widget, QWhatsThis* special ) +{ + newItem( widget )->whatsthis = special; +} + +void QWhatsThisPrivate::add( QWidget * widget, const QString &text ) +{ + newItem( widget )->s = text; +} + + +// and finally the What's This class itself + +/*! + Adds \a text as "What's this" help for \a widget. If the text is + rich text formatted (i.e. it contains markup) it will be rendered + with the default stylesheet QStyleSheet::defaultSheet(). + + The text is destroyed if the widget is later destroyed, so it need + not be explicitly removed. + + \sa remove() +*/ +void QWhatsThis::add( QWidget * widget, const QString &text ) +{ + if ( text.isEmpty() ) + return; // pointless + QWhatsThisPrivate::setUpWhatsThis(); + wt->add(widget,text); +} + + +/*! + Removes the "What's this?" help associated with the \a widget. + This happens automatically if the widget is destroyed. + + \sa add() +*/ +void QWhatsThis::remove( QWidget * widget ) +{ + QWhatsThisPrivate::setUpWhatsThis(); + QWhatsThisPrivate::WhatsThisItem * i = wt->dict->find( (void *)widget ); + if ( !i ) + return; + + wt->dict->take( (void *)widget ); + + i->deref(); + if ( !i->count ) + delete i; +} + + +/*! + Returns the what's this text for widget \a w or QString::null if + there is no "What's this?" help for the widget. \a pos contains + the mouse position; this is useful, for example, if you've + subclassed to make the text that is displayed position dependent. + + If \a includeParents is TRUE, parent widgets are taken into + consideration as well when looking for what's this help text. + + \sa add() +*/ +QString QWhatsThis::textFor( QWidget * w, const QPoint& pos, bool includeParents ) +{ + QWhatsThisPrivate::setUpWhatsThis(); + QWhatsThisPrivate::WhatsThisItem * i = 0; + QPoint p = pos; + while( w && !i ) { + i = wt->dict->find( w ); + if ( !includeParents ) + break; + if ( !i ) { + p += w->pos(); + w = w->parentWidget( TRUE ); + } + } + if (!i) + return QString::null; + if ( i->whatsthis ) + return i->whatsthis->text( p ); + return i->s; +} + + +/*! + Creates a QToolButton preconfigured to enter "What's this?" mode + when clicked. You will often use this with a tool bar as \a + parent: + \code + (void) QWhatsThis::whatsThisButton( my_help_tool_bar ); + \endcode +*/ +QToolButton * QWhatsThis::whatsThisButton( QWidget * parent ) +{ + QWhatsThisPrivate::setUpWhatsThis(); + return new QWhatsThisButton( parent, + "automatic what's this? button" ); +} + +/*! + Constructs a dynamic "What's this?" object for \a widget. The + object is deleted when the \a widget is destroyed. + + When the widget is queried by the user the text() function of this + QWhatsThis will be called to provide the appropriate text, rather + than using the text assigned by add(). +*/ +QWhatsThis::QWhatsThis( QWidget * widget) +{ + QWhatsThisPrivate::setUpWhatsThis(); + wt->add(widget,this); +} + + +/*! + Destroys the object and frees any allocated resources. +*/ +QWhatsThis::~QWhatsThis() +{ +} + + +/*! + This virtual function returns the text for position \e p in the + widget that this "What's this?" object documents. If there is no + "What's this?" text for the position, QString::null is returned. + + The default implementation returns QString::null. +*/ +QString QWhatsThis::text( const QPoint & ) +{ + return QString::null; +} + +/*! + \fn bool QWhatsThis::clicked( const QString& href ) + + This virtual function is called when the user clicks inside the + "What's this?" window. \a href is the link the user clicked on, or + QString::null if there was no link. + + If the function returns TRUE (the default), the "What's this?" + window is closed, otherwise it remains visible. + + The default implementation ignores \a href and returns TRUE. +*/ +bool QWhatsThis::clicked( const QString& ) +{ + return TRUE; +} + + +/*! + Enters "What's this?" mode and returns immediately. + + Qt will install a special cursor and take over mouse input until + the user clicks somewhere. It then shows any help available and + ends "What's this?" mode. Finally, Qt removes the special cursor + and help window and then restores ordinary event processing, at + which point the left mouse button is no longer pressed. + + The user can also use the Esc key to leave "What's this?" mode. + + \sa inWhatsThisMode(), leaveWhatsThisMode() +*/ + +void QWhatsThis::enterWhatsThisMode() +{ + QWhatsThisPrivate::setUpWhatsThis(); + if ( wt->state == QWhatsThisPrivate::Inactive ) { + wt->enterWhatsThisMode(); +#ifndef QT_NO_CURSOR + QApplication::setOverrideCursor( whatsThisCursor, FALSE ); +#endif + wt->state = QWhatsThisPrivate::Waiting; + qApp->installEventFilter( wt ); + } +} + + +/*! + Returns TRUE if the application is in "What's this?" mode; + otherwise returns FALSE. + + \sa enterWhatsThisMode(), leaveWhatsThisMode() +*/ +bool QWhatsThis::inWhatsThisMode() +{ + if (!wt) + return FALSE; + return wt->state == QWhatsThisPrivate::Waiting; +} + + +/*! + Leaves "What's this?" question mode. + + This function is used internally by widgets that support + QWidget::customWhatsThis(); applications do not usually call it. + An example of such a widget is QPopupMenu: menus still work + normally in "What's this?" mode but also provide help texts for + individual menu items. + + If \a text is not QString::null, a "What's this?" help window is + displayed at the global screen position \a pos. If widget \a w is + not 0 and has its own dedicated QWhatsThis object, this object + will receive clicked() messages when the user clicks on hyperlinks + inside the help text. + + \sa inWhatsThisMode(), enterWhatsThisMode(), QWhatsThis::clicked() +*/ +void QWhatsThis::leaveWhatsThisMode( const QString& text, const QPoint& pos, QWidget* w ) +{ + if ( !inWhatsThisMode() ) + return; + + wt->leaveWhatsThisMode(); + if ( !text.isNull() ) + wt->say( w, text, pos ); +} + +/*! + Display \a text in a help window at the global screen position \a + pos. + + If widget \a w is not 0 and has its own dedicated QWhatsThis + object, this object will receive clicked() messages when the user + clicks on hyperlinks inside the help text. + + \sa QWhatsThis::clicked() +*/ +void QWhatsThis::display( const QString& text, const QPoint& pos, QWidget* w ) +{ + if ( inWhatsThisMode() ) { + leaveWhatsThisMode( text, pos, w ); + return; + } + QWhatsThisPrivate::setUpWhatsThis(); + wt->say( w, text, pos ); +} + +/*! + Sets the font for all "What's this?" helps to \a font. +*/ +void QWhatsThis::setFont( const QFont &font ) +{ + QApplication::setFont( font, TRUE, "QWhatsThat" ); +} + +#include "qwhatsthis.moc" +#endif diff --git a/src/widgets/qwhatsthis.h b/src/widgets/qwhatsthis.h new file mode 100644 index 0000000..6ad19d9 --- /dev/null +++ b/src/widgets/qwhatsthis.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Definition of QWhatsThis class +** +** 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. +** +**********************************************************************/ + +#ifndef QWHATSTHIS_H +#define QWHATSTHIS_H + +#ifndef QT_H +#include "qobject.h" +#endif // QT_H + +#ifndef QT_NO_WHATSTHIS + +#include "qcursor.h" + +class QToolButton; +class QPopupMenu; +class QStyleSheet; + +class Q_EXPORT QWhatsThis: public Qt +{ +public: + QWhatsThis( QWidget *); + virtual ~QWhatsThis(); + + virtual QString text( const QPoint & ); + virtual bool clicked( const QString& href ); + + // the common static functions + static void setFont( const QFont &font ); + + static void add( QWidget *, const QString &); + static void remove( QWidget * ); + static QString textFor( QWidget *, const QPoint & pos = QPoint(), bool includeParents = FALSE ); + + static QToolButton * whatsThisButton( QWidget * parent ); + + static void enterWhatsThisMode(); + static bool inWhatsThisMode(); + static void leaveWhatsThisMode( const QString& = QString::null, const QPoint& pos = QCursor::pos(), QWidget* w = 0 ); + + static void display( const QString& text, const QPoint& pos = QCursor::pos(), QWidget* w = 0 ); +}; + +#endif // QT_NO_WHATSTHIS + +#endif // QWHATSTHIS_H diff --git a/src/widgets/qwidgetinterface_p.h b/src/widgets/qwidgetinterface_p.h new file mode 100644 index 0000000..5a45ebc --- /dev/null +++ b/src/widgets/qwidgetinterface_p.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** ... +** +** Copyright (C) 2000-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. +** +**********************************************************************/ + +#ifndef QWIDGETINTERFACE_P_H +#define QWIDGETINTERFACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of a number of Qt sources files. This header file may change from +// version to version without notice, or even be removed. +// +// We mean it. +// +// + +#ifndef QT_H +#include <private/qcom_p.h> +#include "qiconset.h" +#endif // QT_H + +#ifndef QT_NO_WIDGETPLUGIN + +class QWidget; + +// {55184143-f18f-42c0-a8eb-71c01516019a} +#ifndef IID_QWidgetFactory +#define IID_QWidgetFactory QUuid( 0x55184143, 0xf18f, 0x42c0, 0xa8, 0xeb, 0x71, 0xc0, 0x15, 0x16, 0x1, 0x9a ) +#endif + +/*! To add custom widgets to the Qt Designer, implement that interface + in your custom widget plugin. + + You also have to implement the function featureList() (\sa + QFeatureListInterface) and return there all widgets (names of it) + which this interface provides. +*/ + +struct QWidgetFactoryInterface : public QFeatureListInterface +{ +public: + + /*! In the implementation create and return the widget \a widget + here, use \a parent and \a name when creating the widget */ + virtual QWidget* create( const QString &widget, QWidget* parent = 0, const char* name = 0 ) = 0; + + /*! In the implementation return the name of the group of the + widget \a widget */ + virtual QString group( const QString &widget ) const = 0; + + /*! In the implementation return the iconset, which should be used + in the Qt Designer menubar and toolbar to represent the widget + \a widget */ + virtual QIconSet iconSet( const QString &widget ) const = 0; + + /*! In the implementation return the include file which is needed + for the widget \a widget in the generated code which uic + generates. */ + virtual QString includeFile( const QString &widget ) const = 0; + + /*! In the implementation return the text which should be + displayed as tooltip for the widget \a widget */ + virtual QString toolTip( const QString &widget ) const = 0; + + /*! In the implementation return the text which should be used for + what's this help for the widget \a widget. */ + virtual QString whatsThis( const QString &widget ) const = 0; + + /*! In the implementation return TRUE here, of the \a widget + should be able to contain other widget in the Qt Designer, else + FALSE. */ + virtual bool isContainer( const QString &widget ) const = 0; +}; + +#ifdef QT_CONTAINER_CUSTOM_WIDGETS +// {15976628-e3c3-47f4-b525-d124a3caf30e} +#ifndef IID_QWidgetContainer +#define IID_QWidgetContainer QUuid( 0x15976628, 0xe3c3, 0x47f4, 0xb5, 0x25, 0xd1, 0x24, 0xa3, 0xca, 0xf3, 0x0e ) +#endif + +struct QWidgetContainerInterfacePrivate : public QUnknownInterface +{ +public: + virtual QWidget *containerOfWidget( const QString &f, QWidget *container ) const = 0; + virtual bool isPassiveInteractor( const QString &f, QWidget *container ) const = 0; + + virtual bool supportsPages( const QString &f ) const = 0; + + virtual QWidget *addPage( const QString &f, QWidget *container, + const QString &name, int index ) const = 0; + virtual void insertPage( const QString &f, QWidget *container, + const QString &name, int index, QWidget *page ) const = 0; + virtual void removePage( const QString &f, QWidget *container, int index ) const = 0; + virtual void movePage( const QString &f, QWidget *container, int fromIndex, int toIndex ) const = 0; + virtual int count( const QString &key, QWidget *container ) const = 0; + virtual int currentIndex( const QString &key, QWidget *container ) const = 0; + virtual QString pageLabel( const QString &key, QWidget *container, int index ) const = 0; + virtual QWidget *page( const QString &key, QWidget *container, int index ) const = 0; + virtual void renamePage( const QString &key, QWidget *container, + int index, const QString &newName ) const = 0; + virtual QWidgetList pages( const QString &f, QWidget *container ) const = 0; + virtual QString createCode( const QString &f, const QString &container, + const QString &page, const QString &pageName ) const = 0; +}; + +#endif // QT_CONTAINER_CUSTOM_WIDGETS +#endif // QT_NO_WIDGETPLUGIN +#endif // QWIDGETINTERFACE_P_H diff --git a/src/widgets/qwidgetplugin.cpp b/src/widgets/qwidgetplugin.cpp new file mode 100644 index 0000000..77b633b --- /dev/null +++ b/src/widgets/qwidgetplugin.cpp @@ -0,0 +1,729 @@ +/**************************************************************************** +** +** Implementation of QWidgetPlugin class +** +** Created : 010920 +** +** Copyright (C) 2001-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 "qwidgetplugin.h" + +#ifndef QT_NO_WIDGETPLUGIN +#include "qwidgetinterface_p.h" +#include "qobjectcleanuphandler.h" +#include "qwidget.h" +#ifdef QT_CONTAINER_CUSTOM_WIDGETS +#include "qwidgetlist.h" +#endif + +/*! + \class QWidgetPlugin qwidgetplugin.h + \brief The QWidgetPlugin class provides an abstract base for custom QWidget plugins. + + \ingroup plugins + + The widget plugin is a simple plugin interface that makes it easy + to create custom widgets that can be included in forms using \link + designer-manual.book Qt Designer\endlink and used by applications. + + Writing a widget plugin is achieved by subclassing this base + class, reimplementing the pure virtual functions keys(), create(), + group(), iconSet(), includeFile(), toolTip(), whatsThis() and + isContainer(), and exporting the class with the \c Q_EXPORT_PLUGIN + macro. + + See the \link designer-manual.book Qt Designer manual's\endlink, + 'Creating Custom Widgets' section in the 'Creating Custom Widgets' + chapter, for a complete example of a QWidgetPlugin. + + See also the \link plugins-howto.html Plugins + documentation\endlink and the \l{QWidgetFactory} class that is + supplied with \link designer-manual.book Qt Designer\endlink. +*/ + +class QWidgetPluginPrivate : public QWidgetFactoryInterface, +#ifdef QT_CONTAINER_CUSTOM_WIDGETS + public QWidgetContainerInterfacePrivate, +#endif + private QLibraryInterface +{ +public: + QWidgetPluginPrivate( QWidgetPlugin *p ) + : plugin( p ) + { + } + + virtual ~QWidgetPluginPrivate(); + + QRESULT queryInterface( const QUuid &iid, QUnknownInterface **iface ); + Q_REFCOUNT; + + QStringList featureList() const; + QWidget *create( const QString &key, QWidget *parent, const char *name ); + QString group( const QString &widget ) const; + QIconSet iconSet( const QString &widget ) const; + QString includeFile( const QString &widget ) const; + QString toolTip( const QString &widget ) const; + QString whatsThis( const QString &widget ) const; + bool isContainer( const QString &widget ) const; +#ifdef QT_CONTAINER_CUSTOM_WIDGETS + QWidget* containerOfWidget( const QString &key, QWidget *widget ) const; + bool isPassiveInteractor( const QString &key, QWidget *widget ) const; + bool supportsPages( const QString &key ) const; + QWidget *addPage( const QString &key, QWidget *container, const QString &name, int index ) const; + void insertPage( const QString &key, QWidget *container, + const QString &name, int index, QWidget *page ) const; + void Page( const QString &key, QWidget *container, + const QString &name, int index, QWidget *page ) const; + void removePage( const QString &key, QWidget *container, int index ) const; + void movePage( const QString &key, QWidget *container, int fromIndex, int toIndex ) const; + int count( const QString &key, QWidget *container ) const; + int currentIndex( const QString &key, QWidget *container ) const; + QString pageLabel( const QString &key, QWidget *container, int index ) const; + QWidget *page( const QString &key, QWidget *container, int index ) const; + void renamePage( const QString &key, QWidget *container, int index, const QString &newName ) const; + QWidgetList pages( const QString &key, QWidget *container ) const; + QString createCode( const QString &key, const QString &container, + const QString &page, const QString &pageName ) const; +#endif // QT_CONTAINER_CUSTOM_WIDGETS + bool init(); + void cleanup(); + bool canUnload() const; + +private: + QWidgetPlugin *plugin; + QObjectCleanupHandler widgets; +}; + +QRESULT QWidgetPluginPrivate::queryInterface( const QUuid &iid, QUnknownInterface **iface ) +{ + *iface = 0; + + if ( iid == IID_QUnknown ) + *iface = (QWidgetFactoryInterface*)this; + else if ( iid == IID_QFeatureList ) + *iface = (QFeatureListInterface*)this; + else if ( iid == IID_QWidgetFactory ) + *iface = (QWidgetFactoryInterface*)this; + else if ( iid == IID_QLibrary ) + *iface = (QLibraryInterface*)this; +#ifdef QT_CONTAINER_CUSTOM_WIDGETS + else if ( iid == IID_QWidgetContainer ) + *iface = (QWidgetContainerInterfacePrivate*)this; +#endif + else + return QE_NOINTERFACE; + + (*iface)->addRef(); + return QS_OK; +} + +/*! + \fn QStringList QWidgetPlugin::keys() const + + Returns the list of widget keys this plugin supports. + + These keys must be the class names of the custom widgets that are + implemented in the plugin. + + \sa create() +*/ + +/*! + \fn QWidget *QWidgetPlugin::create( const QString &, QWidget *, const char * ) + + Creates and returns a QWidget object for the widget key \a key. + The widget key is the class name of the required widget. The \a + name and \a parent arguments are passed to the custom widget's + constructor. + + \sa keys() +*/ + +QWidgetPluginPrivate::~QWidgetPluginPrivate() +{ + delete plugin; +} + +QStringList QWidgetPluginPrivate::featureList() const +{ + return plugin->keys(); +} + +QWidget *QWidgetPluginPrivate::create( const QString &key, QWidget *parent, const char *name ) +{ + QWidget *w = plugin->create( key, parent, name ); + widgets.add( w ); + return w; +} + +QString QWidgetPluginPrivate::group( const QString &widget ) const +{ + return plugin->group( widget ); +} + +QIconSet QWidgetPluginPrivate::iconSet( const QString &widget ) const +{ + return plugin->iconSet( widget ); +} + +QString QWidgetPluginPrivate::includeFile( const QString &widget ) const +{ + return plugin->includeFile( widget ); +} + +QString QWidgetPluginPrivate::toolTip( const QString &widget ) const +{ + return plugin->toolTip( widget ); +} + +QString QWidgetPluginPrivate::whatsThis( const QString &widget ) const +{ + return plugin->whatsThis( widget ); +} + +bool QWidgetPluginPrivate::isContainer( const QString &widget ) const +{ + return plugin->isContainer( widget ); +} + +bool QWidgetPluginPrivate::init() +{ + return TRUE; +} + +void QWidgetPluginPrivate::cleanup() +{ + widgets.clear(); +} + +bool QWidgetPluginPrivate::canUnload() const +{ + return widgets.isEmpty(); +} + +#ifdef QT_CONTAINER_CUSTOM_WIDGETS +QWidget* QWidgetPluginPrivate::containerOfWidget( const QString &key, QWidget *widget ) const +{ + QWidgetContainerPlugin *p = (QWidgetContainerPlugin*)plugin->qt_cast( "QWidgetContainerPlugin" ); + if ( p ) + return p->containerOfWidget( key, widget ); + return widget; +} + +int QWidgetPluginPrivate::count( const QString &key, QWidget *container ) const +{ + QWidgetContainerPlugin *p = (QWidgetContainerPlugin*)plugin->qt_cast( "QWidgetContainerPlugin" ); + if ( p ) + return p->count( key, container ); + return 0; +} + +int QWidgetPluginPrivate::currentIndex( const QString &key, QWidget *container ) const +{ + QWidgetContainerPlugin *p = (QWidgetContainerPlugin*)plugin->qt_cast( "QWidgetContainerPlugin" ); + if ( p ) + return p->currentIndex( key, container ); + return -1; +} + +QString QWidgetPluginPrivate::pageLabel( const QString &key, QWidget *container, int index ) const +{ + QWidgetContainerPlugin *p = (QWidgetContainerPlugin*)plugin->qt_cast( "QWidgetContainerPlugin" ); + if ( p ) + return p->pageLabel( key, container, index ); + return QString::null; +} + +QWidget *QWidgetPluginPrivate::page( const QString &key, QWidget *container, int index ) const +{ + QWidgetContainerPlugin *p = (QWidgetContainerPlugin*)plugin->qt_cast( "QWidgetContainerPlugin" ); + if ( p ) + return p->page( key, container, index ); + return 0; +} + +bool QWidgetPluginPrivate::isPassiveInteractor( const QString &key, QWidget *widget ) const +{ + QWidgetContainerPlugin *p = (QWidgetContainerPlugin*)plugin->qt_cast( "QWidgetContainerPlugin" ); + if ( p ) + return p->isPassiveInteractor( key, widget ); + return FALSE; +} + +bool QWidgetPluginPrivate::supportsPages( const QString &key ) const +{ + QWidgetContainerPlugin *p = (QWidgetContainerPlugin*)plugin->qt_cast( "QWidgetContainerPlugin" ); + if ( p ) + return p->supportsPages( key ); + return 0; +} + +QWidget *QWidgetPluginPrivate::addPage( const QString &key, QWidget *container, + const QString &name, int index ) const +{ + QWidgetContainerPlugin *p = (QWidgetContainerPlugin*)plugin->qt_cast( "QWidgetContainerPlugin" ); + if ( p ) + return p->addPage( key, container, name, index ); + return 0; +} + +void QWidgetPluginPrivate::insertPage( const QString &key, QWidget *container, + const QString &name, int index, QWidget *page ) const +{ + QWidgetContainerPlugin *p = (QWidgetContainerPlugin*)plugin->qt_cast( "QWidgetContainerPlugin" ); + if ( p ) + p->insertPage( key, container, name, index, page ); +} + +void QWidgetPluginPrivate::removePage( const QString &key, QWidget *container, int index ) const +{ + QWidgetContainerPlugin *p = (QWidgetContainerPlugin*)plugin->qt_cast( "QWidgetContainerPlugin" ); + if ( p ) + p->removePage( key, container, index ); +} + +void QWidgetPluginPrivate::movePage( const QString &key, QWidget *container, + int fromIndex, int toIndex ) const +{ + QWidgetContainerPlugin *p = (QWidgetContainerPlugin*)plugin->qt_cast( "QWidgetContainerPlugin" ); + if ( p ) + p->movePage( key, container, fromIndex, toIndex ); +} + +void QWidgetPluginPrivate::renamePage( const QString &key, QWidget *container, + int index, const QString &newName ) const +{ + QWidgetContainerPlugin *p = (QWidgetContainerPlugin*)plugin->qt_cast( "QWidgetContainerPlugin" ); + if ( p ) + p->renamePage( key, container, index, newName ); +} + +QWidgetList QWidgetPluginPrivate::pages( const QString &key, QWidget *container ) const +{ + QWidgetContainerPlugin *p = (QWidgetContainerPlugin*)plugin->qt_cast( "QWidgetContainerPlugin" ); + if ( p ) + return p->pages( key, container ); + return QWidgetList(); +} + +QString QWidgetPluginPrivate::createCode( const QString &key, const QString &container, + const QString &page, const QString &pageName ) const +{ + QWidgetContainerPlugin *p = (QWidgetContainerPlugin*)plugin->qt_cast( "QWidgetContainerPlugin" ); + if ( p ) + return p->createCode( key, container, page, pageName ); + return QString::null; +} +#endif // QT_CONTAINER_CUSTOM_WIDGETS + +/*! + Constructs a widget plugin. This is invoked automatically by the + \c Q_EXPORT_PLUGIN macro. +*/ +QWidgetPlugin::QWidgetPlugin() + : QGPlugin( (QWidgetFactoryInterface*)(d = new QWidgetPluginPrivate( this )) ) +{ +} + +/*! + Destroys the widget plugin. + + You never have to call this explicitly. Qt destroys a plugin + automatically when it is no longer used. +*/ +QWidgetPlugin::~QWidgetPlugin() +{ + // don't delete d, as this is deleted by d +} + +/*! + Returns the group (toolbar name) that the custom widget of class + \a key should be part of when \e{Qt Designer} loads it. + + The default implementation returns QString::null. +*/ +QString QWidgetPlugin::group( const QString & ) const +{ + return QString::null; +} + +/*! + Returns the iconset that \e{Qt Designer} should use to represent + the custom widget of class \a key in the toolbar. + + The default implementation returns an null iconset. +*/ +QIconSet QWidgetPlugin::iconSet( const QString & ) const +{ + return QIconSet(); +} + +/*! + Returns the name of the include file that \e{Qt Designer} and \c + uic should use to include the custom widget of class \a key in + generated code. + + The default implementation returns QString::null. +*/ +QString QWidgetPlugin::includeFile( const QString & ) const +{ + return QString::null; +} + +/*! + Returns the text of the tooltip that \e{Qt Designer} should use + for the custom widget of class \a key's toolbar button. + + The default implementation returns QString::null. +*/ +QString QWidgetPlugin::toolTip( const QString & ) const +{ + return QString::null; +} + +/*! + Returns the text of the whatsThis text that \e{Qt Designer} should + use when the user requests whatsThis help for the custom widget of + class \a key. + + The default implementation returns QString::null. +*/ +QString QWidgetPlugin::whatsThis( const QString & ) const +{ + return QString::null; +} + +/*! + Returns TRUE if the custom widget of class \a key can contain + other widgets, e.g. like QFrame; otherwise returns FALSE. + + The default implementation returns FALSE. +*/ +bool QWidgetPlugin::isContainer( const QString & ) const +{ + return FALSE; +} + +#ifdef QT_CONTAINER_CUSTOM_WIDGETS + +/*! + \class QWidgetContainerPlugin qwidgetplugin.h + \brief The QWidgetContainerPlugin class provides an abstract base + for complex custom container QWidget plugins. + + \internal + + \ingroup plugins + + The widget container plugin is a subclass of QWidgetPlugin and + extends the interface with functions necessary for supporting + complex container widgets via plugins. These container widgets are + widgets that have one or multiple sub widgets which act as the + widget's containers. If the widget has multiple container + subwidgets, they are referred to as "pages", and only one can be + active at a time. Examples of complex container widgets include: + QTabWidget, QWidgetStack and QToolBox. + + Writing a complex container widget plugin is achieved by + subclassing this base class. First by reimplementing + QWidgetPlugin's pure virtual functions keys(), create(), group(), + iconSet(), includeFile(), toolTip(), whatsThis() and + isContainer(), and exporting the class with the \c Q_EXPORT_PLUGIN + macro. In addition containerOfWidget(), isPassiveInteractor() and + supportsPages() must be reimplemented. If the widget + supportsPages(), count(), currentIndex(), pageLabel(), page(), + pages() and createCode() must be implemented. If the widget + supportsPages() and you want to allow the containers pages to be + modified, you must also reimplement addPage(), insertPage(), + removePage(), movePage() and renamePage(). + + \sa QWidgetPlugin +*/ + +/*! + Constructs a complex container widget plugin. This is invoked + automatically by the \c Q_EXPORT_PLUGIN macro. +*/ + +QWidgetContainerPlugin::QWidgetContainerPlugin() + : QWidgetPlugin() +{ +} + +/*! + Destroys the complex container widget plugin. + + You never have to call this explicitly. Qt destroys a plugin + automatically when it is no longer used. +*/ + +QWidgetContainerPlugin::~QWidgetContainerPlugin() +{ +} + +/*! + Operates on the plugin's \a key class. + + Returns the current \a container's custom widget. If the custom + widget is a tab widget, this function takes the \a container as + input and returns the widget's current page. + + The default implementation returns \a container. +*/ + +QWidget* QWidgetContainerPlugin::containerOfWidget( const QString &, + QWidget *container ) const +{ + return container; +} + +/*! + Operates on the plugin's \a key class. + + Returns the \a container custom widget's number of pages. If the + custom widget is a tab widget, this function returns the number of + tabs. + + The default implementation returns 0. +*/ + +int QWidgetContainerPlugin::count( const QString &, QWidget * ) const +{ + return 0; +} + +/*! + Operates on the plugin's \a key class. + + Returns the \a container custom widget's current page index. If + the custom widget is a tab widget, this function returns the + current tab's index. + + The default implementation returns -1. +*/ + +int QWidgetContainerPlugin::currentIndex( const QString &, QWidget * ) const +{ + return -1; +} + +/*! + Operates on the plugin's \a key class. + + Returns the \a container custom widget's label at position \a + index. If the custom widget is a tab widget, this function returns + the current tab's label. + + The default implementation returns a null string. +*/ + +QString QWidgetContainerPlugin::pageLabel( const QString &, QWidget *, int ) const +{ + return QString::null; +} + +/*! + Operates on the plugin's \a key class. + + Returns the \a container custom widget's page at position \a + index. If the custom widget is a tab widget, this function returns + the tab at index position \e index. + + + The default implementation returns 0. +*/ + +QWidget *QWidgetContainerPlugin::page( const QString &, QWidget *, int ) const +{ + return 0; +} + +/*! + Operates on the plugin's \a key class. + + Returns TRUE if the \a container custom widget is a passive + interactor for class \e key; otherwise returns FALSE. The \a + container is a child widget of the actual custom widget. + + Usually, when a custom widget is used in \e{Qt Designer}'s design + mode, no widget receives any mouse or key events, since \e{Qt + Designer} filters and processes them itself. If one or more + widgets of a custom widget still need to receive such events, for + example, because the widget needs to change pages, this function + must return TRUE for the widget. In such cases \e{Qt Designer} + will not filter out key and mouse events destined for the widget. + + If the custom widget is a tab widget, the tab bar is the passive + interactor, since that's what the user will use to change pages. + + The default implementation returns FALSE. +*/ + +bool QWidgetContainerPlugin::isPassiveInteractor( const QString &, + QWidget *container ) const +{ + Q_UNUSED( container ) + return FALSE; +} + +/*! + Operates on the plugin's \a key class. + + Returns TRUE if the widget supports pages; otherwise returns + FALSE. If the custom widget is a tab widget this function should + return TRUE. + + The default implementation returns FALSE. +*/ + +bool QWidgetContainerPlugin::supportsPages( const QString & ) const +{ + return FALSE; +} + +/*! + Operates on the plugin's \a key class. + + This function is called when a new page with the given \a name + should be added to the \a container custom widget at position \a + index. + + The default implementation does nothing. +*/ + +QWidget* QWidgetContainerPlugin::addPage( const QString &, QWidget *, + const QString &, int ) const +{ + return 0; +} + +/*! + Operates on the plugin's \a key class. + + This function is called when a new page, \a page, with the given + \a name should be added to the \a container custom widget at + position \a index. + + The default implementation does nothing. +*/ + +void QWidgetContainerPlugin::insertPage( const QString &, QWidget *, + const QString &, int, QWidget * ) const +{ +} + +/*! + Operates on the plugin's \a key class. + + This function is called when the page at position \a index should + be removed from the \a container custom widget. + + The default implementation does nothing. +*/ + +void QWidgetContainerPlugin::removePage( const QString &, QWidget *, int ) const +{ +} + +/*! + Operates on the plugin's \a key class. + + This function is called when the page at position \a fromIndex should + be moved to position \a toIndex in the \a container custom widget. + + The default implementation does nothing. +*/ + +void QWidgetContainerPlugin::movePage( const QString &, QWidget *, int, int ) const +{ +} + +/*! + Operates on the plugin's \a key class. + + This function is called when the page at position \a index should + be renamed (have its label changed) to \a newName in the \a + container custom widget. + + The default implementation does nothing. +*/ + +void QWidgetContainerPlugin::renamePage( const QString &, QWidget *, + int, const QString & ) const +{ +} + +/*! + Operates on the plugin's \a key class. + + This function should return a list of the \a container custom + widget's pages. +*/ + +QWidgetList QWidgetContainerPlugin::pages( const QString &, QWidget * ) const +{ + return QWidgetList(); +} + +/*! + Operates on the plugin's \a key class. + + This function is called from \e{Qt Designer}'s User Interface + Compiler \c uic, when generating C++ code for inserting a page in + the \a container custom widget. The name of the page widget which + should be inserted at the end of the container is \a page, and the + label of the page should be \a pageName. + + If the custom widget was a QTabWidget, the implementation of this + function should return: + + \code + return widget + "->addTab( " + page + ", \"" + pageName + "\" )"; + \endcode + + Warning: If the code returned by this function contains invalid + C++ syntax, the generated \c uic code will not compile. +*/ + +QString QWidgetContainerPlugin::createCode( const QString &, const QString &, + const QString &, const QString & ) const +{ + return QString::null; +} + +#endif // QT_CONTAINER_CUSTOM_WIDGETS + +#endif //QT_NO_WIDGETPLUGIN diff --git a/src/widgets/qwidgetplugin.h b/src/widgets/qwidgetplugin.h new file mode 100644 index 0000000..94e1ec7 --- /dev/null +++ b/src/widgets/qwidgetplugin.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Definition of QWidgetPlugin class +** +** Created : 010920 +** +** Copyright (C) 2001-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. +** +**********************************************************************/ + +#ifndef QWIDGETPLUGIN_H +#define QWIDGETPLUGIN_H + +#ifndef QT_H +#include "qgplugin.h" +#include "qstringlist.h" +#include "qiconset.h" +#endif // QT_H +#ifndef QT_NO_WIDGETPLUGIN + +#ifdef Q_WS_WIN +#ifdef QT_PLUGIN +#define QT_WIDGET_PLUGIN_EXPORT __declspec(dllexport) +#else +#define QT_WIDGET_PLUGIN_EXPORT __declspec(dllimport) +#endif +#else +#define QT_WIDGET_PLUGIN_EXPORT +#endif + +class QWidgetPluginPrivate; +class QWidget; + +class Q_EXPORT QWidgetPlugin : public QGPlugin +{ + Q_OBJECT +public: + QWidgetPlugin(); + ~QWidgetPlugin(); + + virtual QStringList keys() const = 0; + virtual QWidget *create( const QString &key, QWidget *parent = 0, const char *name = 0 ) = 0; + + virtual QString group( const QString &key ) const; + virtual QIconSet iconSet( const QString &key ) const; + virtual QString includeFile( const QString &key ) const; + virtual QString toolTip( const QString &key ) const; + virtual QString whatsThis( const QString &key ) const; + virtual bool isContainer( const QString &key ) const; + +private: + QWidgetPluginPrivate *d; +}; + +#ifdef QT_CONTAINER_CUSTOM_WIDGETS + +class QWidgetContainerPluginPrivate; + +class Q_EXPORT QWidgetContainerPlugin : public QWidgetPlugin +{ + +public: + QWidgetContainerPlugin(); + ~QWidgetContainerPlugin(); + + virtual QWidget* containerOfWidget( const QString &key, QWidget *container ) const; + virtual bool isPassiveInteractor( const QString &key, QWidget *container ) const; + + virtual bool supportsPages( const QString &key ) const; + + virtual QWidget *addPage( const QString &key, QWidget *container, + const QString &name, int index ) const; + virtual void insertPage( const QString &key, QWidget *container, + const QString &name, int index, QWidget *page ) const; + virtual void removePage( const QString &key, QWidget *container, int index ) const; + virtual void movePage( const QString &key, QWidget *container, int fromIndex, int toIndex ) const; + virtual int count( const QString &key, QWidget *container ) const; + virtual int currentIndex( const QString &key, QWidget *container ) const; + virtual QString pageLabel( const QString &key, QWidget *container, int index ) const; + virtual QWidget *page( const QString &key, QWidget *container, int index ) const; + virtual void renamePage( const QString &key, QWidget *container, + int index, const QString &newName ) const; + virtual QWidgetList pages( const QString &key, QWidget *container ) const; + virtual QString createCode( const QString &key, const QString &container, + const QString &page, const QString &pageName ) const; +}; + +#endif // QT_CONTAINER_CUSTOM_WIDGETS +#endif // QT_NO_WIDGETPLUGIN +#endif // QWIDGETPLUGIN_H diff --git a/src/widgets/qwidgetresizehandler.cpp b/src/widgets/qwidgetresizehandler.cpp new file mode 100644 index 0000000..44b414b --- /dev/null +++ b/src/widgets/qwidgetresizehandler.cpp @@ -0,0 +1,516 @@ +/**************************************************************************** +** +** Implementation of the QWidgetResizeHandler class +** +** Created : 001010 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the workspace 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 "qwidgetresizehandler_p.h" + +#ifndef QT_NO_RESIZEHANDLER +#include "qframe.h" +#include "qapplication.h" +#include "qcursor.h" +#include "qsizegrip.h" +#if defined(Q_WS_WIN) +#include "qt_windows.h" +#endif + +#define RANGE 4 + +static bool resizeHorizontalDirectionFixed = FALSE; +static bool resizeVerticalDirectionFixed = FALSE; + +QWidgetResizeHandler::QWidgetResizeHandler( QWidget *parent, QWidget *cw, const char *name ) + : QObject( parent, name ), widget( parent ), childWidget( cw ? cw : parent ), + extrahei( 0 ), buttonDown( FALSE ), moveResizeMode( FALSE ), sizeprotect( TRUE ), moving( TRUE ) +{ + mode = Nowhere; + widget->setMouseTracking( TRUE ); + QFrame *frame = ::qt_cast<QFrame*>(widget); + range = frame ? frame->frameWidth() : RANGE; + range = QMAX( RANGE, range ); + activeForMove = activeForResize = TRUE; + qApp->installEventFilter( this ); +} + +void QWidgetResizeHandler::setActive( Action ac, bool b ) +{ + if ( ac & Move ) + activeForMove = b; + if ( ac & Resize ) + activeForResize = b; + + if ( !isActive() ) + setMouseCursor( Nowhere ); +} + +bool QWidgetResizeHandler::isActive( Action ac ) const +{ + bool b = FALSE; + if ( ac & Move ) b = activeForMove; + if ( ac & Resize ) b |= activeForResize; + + return b; +} + +static QWidget *childOf( QWidget *w, QWidget *child ) +{ + while ( child ) { + if ( child == w ) + return child; + if ( child->isTopLevel() ) + break; + child = child->parentWidget(); + } + return 0; +} + +bool QWidgetResizeHandler::eventFilter( QObject *o, QEvent *ee ) +{ + if ( !isActive() || !o->isWidgetType() || !ee->spontaneous()) + return FALSE; + + if ( ee->type() != QEvent::MouseButtonPress && + ee->type() != QEvent::MouseButtonRelease && + ee->type() != QEvent::MouseMove && + ee->type() != QEvent::KeyPress && + ee->type() != QEvent::AccelOverride ) + return FALSE; + + QWidget *w = childOf( widget, (QWidget*)o ); + if ( !w +#ifndef QT_NO_SIZEGRIP + || ::qt_cast<QSizeGrip*>(o) +#endif + || qApp->activePopupWidget() ) { + if ( buttonDown && ee->type() == QEvent::MouseButtonRelease ) + buttonDown = FALSE; + return FALSE; + } + + QMouseEvent *e = (QMouseEvent*)ee; + switch ( e->type() ) { + case QEvent::MouseButtonPress: { + if ( w->isMaximized() ) + break; + if ( !widget->rect().contains( widget->mapFromGlobal( e->globalPos() ) ) ) + return FALSE; + if ( e->button() == LeftButton ) { + emit activate(); + bool me = isMovingEnabled(); + setMovingEnabled( me && o == widget ); + mouseMoveEvent( e ); + setMovingEnabled( me ); + buttonDown = TRUE; + moveOffset = widget->mapFromGlobal( e->globalPos() ); + invertedMoveOffset = widget->rect().bottomRight() - moveOffset; + } + } break; + case QEvent::MouseButtonRelease: + if ( w->isMaximized() ) + break; + if ( e->button() == LeftButton ) { + moveResizeMode = FALSE; + buttonDown = FALSE; + widget->releaseMouse(); + widget->releaseKeyboard(); + } + break; + case QEvent::MouseMove: { + if ( w->isMaximized() ) + break; + bool me = isMovingEnabled(); + setMovingEnabled( me && o == widget ); + mouseMoveEvent( e ); + setMovingEnabled( me ); + if ( buttonDown && mode != Center ) + return TRUE; + } break; + case QEvent::KeyPress: + keyPressEvent( (QKeyEvent*)e ); + break; + case QEvent::AccelOverride: + if ( buttonDown ) { + ((QKeyEvent*)ee)->accept(); + return TRUE; + } + break; + default: + break; + } + return FALSE; +} + +void QWidgetResizeHandler::mouseMoveEvent( QMouseEvent *e ) +{ + QPoint pos = widget->mapFromGlobal( e->globalPos() ); + if ( !moveResizeMode && ( !buttonDown || ( e->state() & LeftButton ) == 0 ) ) { + if ( pos.y() <= range && pos.x() <= range) + mode = TopLeft; + else if ( pos.y() >= widget->height()-range && pos.x() >= widget->width()-range) + mode = BottomRight; + else if ( pos.y() >= widget->height()-range && pos.x() <= range) + mode = BottomLeft; + else if ( pos.y() <= range && pos.x() >= widget->width()-range) + mode = TopRight; + else if ( pos.y() <= range ) + mode = Top; + else if ( pos.y() >= widget->height()-range ) + mode = Bottom; + else if ( pos.x() <= range ) + mode = Left; + else if ( pos.x() >= widget->width()-range ) + mode = Right; + else + mode = Center; + + if ( widget->isMinimized() || !isActive(Resize) ) + mode = Center; +#ifndef QT_NO_CURSOR + setMouseCursor( mode ); +#endif + return; + } + + if ( buttonDown && !isMovingEnabled() && mode == Center && !moveResizeMode ) + return; + + if ( widget->testWState( WState_ConfigPending ) ) + return; + + QPoint globalPos = widget->parentWidget( TRUE ) ? + widget->parentWidget( TRUE )->mapFromGlobal( e->globalPos() ) : e->globalPos(); + if ( widget->parentWidget( TRUE ) && !widget->parentWidget( TRUE )->rect().contains( globalPos ) ) { + if ( globalPos.x() < 0 ) + globalPos.rx() = 0; + if ( globalPos.y() < 0 ) + globalPos.ry() = 0; + if ( sizeprotect && globalPos.x() > widget->parentWidget()->width() ) + globalPos.rx() = widget->parentWidget()->width(); + if ( sizeprotect && globalPos.y() > widget->parentWidget()->height() ) + globalPos.ry() = widget->parentWidget()->height(); + } + + QPoint p = globalPos + invertedMoveOffset; + QPoint pp = globalPos - moveOffset; + + int fw = 0; + int mw = QMAX( childWidget->minimumSizeHint().width(), + childWidget->minimumWidth() ); + int mh = QMAX( childWidget->minimumSizeHint().height(), + childWidget->minimumHeight() ); + if ( childWidget != widget ) { + QFrame *frame = ::qt_cast<QFrame*>(widget); + if ( frame ) + fw = frame->frameWidth(); + mw += 2 * fw; + mh += 2 * fw + extrahei; + } + + QSize mpsize( widget->geometry().right() - pp.x() + 1, + widget->geometry().bottom() - pp.y() + 1 ); + mpsize = mpsize.expandedTo( widget->minimumSize() ).expandedTo( QSize(mw, mh) ); + QPoint mp( widget->geometry().right() - mpsize.width() + 1, + widget->geometry().bottom() - mpsize.height() + 1 ); + + QRect geom = widget->geometry(); + + switch ( mode ) { + case TopLeft: + geom = QRect( mp, widget->geometry().bottomRight() ) ; + break; + case BottomRight: + geom = QRect( widget->geometry().topLeft(), p ) ; + break; + case BottomLeft: + geom = QRect( QPoint(mp.x(), widget->geometry().y() ), QPoint( widget->geometry().right(), p.y()) ) ; + break; + case TopRight: + geom = QRect( QPoint( widget->geometry().x(), mp.y() ), QPoint( p.x(), widget->geometry().bottom()) ) ; + break; + case Top: + geom = QRect( QPoint( widget->geometry().left(), mp.y() ), widget->geometry().bottomRight() ) ; + break; + case Bottom: + geom = QRect( widget->geometry().topLeft(), QPoint( widget->geometry().right(), p.y() ) ) ; + break; + case Left: + geom = QRect( QPoint( mp.x(), widget->geometry().top() ), widget->geometry().bottomRight() ) ; + break; + case Right: + geom = QRect( widget->geometry().topLeft(), QPoint( p.x(), widget->geometry().bottom() ) ) ; + break; + case Center: + if ( isMovingEnabled() || moveResizeMode ) + geom.moveTopLeft( pp ); + break; + default: + break; + } + + QSize maxsize( childWidget->maximumSize() ); + if ( childWidget != widget ) + maxsize += QSize( 2 * fw, 2 * fw + extrahei ); + + geom = QRect( geom.topLeft(), + geom.size().expandedTo( widget->minimumSize() ) + .expandedTo( QSize(mw, mh) ) + .boundedTo( maxsize ) ); + + if ( geom != widget->geometry() && + ( widget->isTopLevel() || widget->parentWidget()->rect().intersects( geom ) ) ) { + if ( widget->isMinimized() ) + widget->move( geom.topLeft() ); + else + widget->setGeometry( geom ); + } + +#if defined(Q_WS_WIN) + MSG msg; + QT_WA( { + while(PeekMessageW( &msg, widget->winId(), WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE )) + ; + } , { + while(PeekMessageA( &msg, widget->winId(), WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE )) + ; + } ); +#endif + + QApplication::syncX(); +} + +void QWidgetResizeHandler::setMouseCursor( MousePosition m ) +{ +#ifndef QT_NO_CURSOR + switch ( m ) { + case TopLeft: + case BottomRight: + widget->setCursor( sizeFDiagCursor ); + break; + case BottomLeft: + case TopRight: + widget->setCursor( sizeBDiagCursor ); + break; + case Top: + case Bottom: + widget->setCursor( sizeVerCursor ); + break; + case Left: + case Right: + widget->setCursor( sizeHorCursor ); + break; + default: + widget->setCursor( arrowCursor ); + break; + } +#endif +} + +void QWidgetResizeHandler::keyPressEvent( QKeyEvent * e ) +{ + if ( !isMove() && !isResize() ) + return; + bool is_control = e->state() & ControlButton; + int delta = is_control?1:8; + QPoint pos = QCursor::pos(); + switch ( e->key() ) { + case Key_Left: + pos.rx() -= delta; + if ( pos.x() <= QApplication::desktop()->geometry().left() ) { + if ( mode == TopLeft || mode == BottomLeft ) { + moveOffset.rx() += delta; + invertedMoveOffset.rx() += delta; + } else { + moveOffset.rx() -= delta; + invertedMoveOffset.rx() -= delta; + } + } + if ( isResize() && !resizeHorizontalDirectionFixed ) { + resizeHorizontalDirectionFixed = TRUE; + if ( mode == BottomRight ) + mode = BottomLeft; + else if ( mode == TopRight ) + mode = TopLeft; +#ifndef QT_NO_CURSOR + setMouseCursor( mode ); + widget->grabMouse( widget->cursor() ); +#else + widget->grabMouse(); +#endif + } + break; + case Key_Right: + pos.rx() += delta; + if ( pos.x() >= QApplication::desktop()->geometry().right() ) { + if ( mode == TopRight || mode == BottomRight ) { + moveOffset.rx() += delta; + invertedMoveOffset.rx() += delta; + } else { + moveOffset.rx() -= delta; + invertedMoveOffset.rx() -= delta; + } + } + if ( isResize() && !resizeHorizontalDirectionFixed ) { + resizeHorizontalDirectionFixed = TRUE; + if ( mode == BottomLeft ) + mode = BottomRight; + else if ( mode == TopLeft ) + mode = TopRight; +#ifndef QT_NO_CURSOR + setMouseCursor( mode ); + widget->grabMouse( widget->cursor() ); +#else + widget->grabMouse(); +#endif + } + break; + case Key_Up: + pos.ry() -= delta; + if ( pos.y() <= QApplication::desktop()->geometry().top() ) { + if ( mode == TopLeft || mode == TopRight ) { + moveOffset.ry() += delta; + invertedMoveOffset.ry() += delta; + } else { + moveOffset.ry() -= delta; + invertedMoveOffset.ry() -= delta; + } + } + if ( isResize() && !resizeVerticalDirectionFixed ) { + resizeVerticalDirectionFixed = TRUE; + if ( mode == BottomLeft ) + mode = TopLeft; + else if ( mode == BottomRight ) + mode = TopRight; +#ifndef QT_NO_CURSOR + setMouseCursor( mode ); + widget->grabMouse( widget->cursor() ); +#else + widget->grabMouse(); +#endif + } + break; + case Key_Down: + pos.ry() += delta; + if ( pos.y() >= QApplication::desktop()->geometry().bottom() ) { + if ( mode == BottomLeft || mode == BottomRight ) { + moveOffset.ry() += delta; + invertedMoveOffset.ry() += delta; + } else { + moveOffset.ry() -= delta; + invertedMoveOffset.ry() -= delta; + } + } + if ( isResize() && !resizeVerticalDirectionFixed ) { + resizeVerticalDirectionFixed = TRUE; + if ( mode == TopLeft ) + mode = BottomLeft; + else if ( mode == TopRight ) + mode = BottomRight; +#ifndef QT_NO_CURSOR + setMouseCursor( mode ); + widget->grabMouse( widget->cursor() ); +#else + widget->grabMouse(); +#endif + } + break; + case Key_Space: + case Key_Return: + case Key_Enter: + case Key_Escape: + moveResizeMode = FALSE; + widget->releaseMouse(); + widget->releaseKeyboard(); + buttonDown = FALSE; + break; + default: + return; + } + QCursor::setPos( pos ); +} + + +void QWidgetResizeHandler::doResize() +{ + if ( !activeForResize ) + return; + + moveResizeMode = TRUE; + buttonDown = TRUE; + moveOffset = widget->mapFromGlobal( QCursor::pos() ); + if ( moveOffset.x() < widget->width()/2) { + if ( moveOffset.y() < widget->height()/2) + mode = TopLeft; + else + mode = BottomLeft; + } else { + if ( moveOffset.y() < widget->height()/2) + mode = TopRight; + else + mode = BottomRight; + } + invertedMoveOffset = widget->rect().bottomRight() - moveOffset; +#ifndef QT_NO_CURSOR + setMouseCursor( mode ); + widget->grabMouse( widget->cursor() ); +#else + widget->grabMouse(); +#endif + widget->grabKeyboard(); + resizeHorizontalDirectionFixed = FALSE; + resizeVerticalDirectionFixed = FALSE; +} + +void QWidgetResizeHandler::doMove() +{ + if ( !activeForMove ) + return; + + mode = Center; + moveResizeMode = TRUE; + buttonDown = TRUE; + moveOffset = widget->mapFromGlobal( QCursor::pos() ); + invertedMoveOffset = widget->rect().bottomRight() - moveOffset; +#ifndef QT_NO_CURSOR + widget->grabMouse( SizeAllCursor ); +#else + widget->grabMouse(); +#endif + widget->grabKeyboard(); +} + +#endif //QT_NO_RESIZEHANDLER diff --git a/src/widgets/qwidgetresizehandler_p.h b/src/widgets/qwidgetresizehandler_p.h new file mode 100644 index 0000000..3d3b6df --- /dev/null +++ b/src/widgets/qwidgetresizehandler_p.h @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Definition of the QWidgetResizeHandler class +** +** Created : 001010 +** +** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved. +** +** This file is part of the workspace 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. +** +**********************************************************************/ + +#ifndef QWIDGETRESIZEHANDLER_P_H +#define QWIDGETRESIZEHANDLER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. This header file may +// change from version to version without notice, or even be +// removed. +// +// We mean it. +// +// + +#ifndef QT_H +#include "qobject.h" +#endif // QT_H +#ifndef QT_NO_RESIZEHANDLER +class QMouseEvent; +class QKeyEvent; + +class Q_EXPORT QWidgetResizeHandler : public QObject +{ + Q_OBJECT + +public: + enum Action { + Move = 0x01, + Resize = 0x02, + Any = Move|Resize + }; + + QWidgetResizeHandler( QWidget *parent, QWidget *cw = 0, const char *name = 0 ); + void setActive( bool b ) { setActive( Any, b ); } + void setActive( Action ac, bool b ); + bool isActive() const { return isActive( Any ); } + bool isActive( Action ac ) const; + void setMovingEnabled( bool b ) { moving = b; } + bool isMovingEnabled() const { return moving; } + + bool isButtonDown() const { return buttonDown; } + + void setExtraHeight( int h ) { extrahei = h; } + void setSizeProtection( bool b ) { sizeprotect = b; } + + void doResize(); + void doMove(); + +signals: + void activate(); + +protected: + bool eventFilter( QObject *o, QEvent *e ); + void mouseMoveEvent( QMouseEvent *e ); + void keyPressEvent( QKeyEvent *e ); + +private: + enum MousePosition { + Nowhere, + TopLeft, BottomRight, BottomLeft, TopRight, + Top, Bottom, Left, Right, + Center + }; + + QWidget *widget; + QWidget *childWidget; + QPoint moveOffset; + QPoint invertedMoveOffset; + MousePosition mode; + int extrahei; + int range; + uint buttonDown :1; + uint moveResizeMode :1; + uint activeForResize :1; + uint sizeprotect :1; + uint moving :1; + uint activeForMove :1; + + void setMouseCursor( MousePosition m ); + bool isMove() const { + return moveResizeMode && mode == Center; + } + bool isResize() const { + return moveResizeMode && !isMove(); + } + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QWidgetResizeHandler( const QWidgetResizeHandler & ); + QWidgetResizeHandler& operator=( const QWidgetResizeHandler & ); +#endif + +}; + +#endif //QT_NO_RESIZEHANDLER +#endif diff --git a/src/widgets/qwidgetstack.cpp b/src/widgets/qwidgetstack.cpp new file mode 100644 index 0000000..76dfc0b --- /dev/null +++ b/src/widgets/qwidgetstack.cpp @@ -0,0 +1,610 @@ +/**************************************************************************** +** +** Implementation of QWidgetStack class +** +** Created : 980128 +** +** 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 "qwidgetstack.h" +#include "qlayout.h" +#include "../kernel/qlayoutengine_p.h" +#ifndef QT_NO_WIDGETSTACK + +#include "qobjectlist.h" +#include "qfocusdata.h" +#include "qbutton.h" +#include "qbuttongroup.h" + +#include "qapplication.h" + +class QWidgetStackPrivate { +public: + class Invisible: public QWidget + { + public: + Invisible( QWidgetStack * parent ): QWidget( parent, "qt_invisible_widgetstack" ) + { + setBackgroundMode( NoBackground ); + } + const char * className() const + { + return "QWidgetStackPrivate::Invisible"; + } + }; +}; + + +#if (QT_VERSION-0 >= 0x040000) +#if defined(Q_CC_GNU) +#warning "Remove QWidgetStackEventFilter" +#endif +#endif +class QWidgetStackEventFilter : public QObject +{ + /* For binary compatibility, since we cannot implement virtual + functions and rely on them being called. This is what we should + have + + bool QWidgetStack::event( QEvent* e ) + { + if ( e->type() == QEvent::LayoutHint ) + updateGeometry(); // propgate layout hints to parent + return QFrame::event( e ); + } + */ +public: + + QWidgetStackEventFilter( QObject *parent = 0, const char * name = 0 ) + : QObject( parent, name ) {} + bool eventFilter( QObject *o, QEvent * e ) { + if ( e->type() == QEvent::LayoutHint && o->isWidgetType() ) + ((QWidget*)o)->updateGeometry(); + return FALSE; + } +}; + + +/*! + \class QWidgetStack + \brief The QWidgetStack class provides a stack of widgets of which + only the top widget is user-visible. + + \ingroup organizers + \mainclass + + The application programmer can move any widget to the top of the + stack at any time using raiseWidget(), and add or remove widgets + using addWidget() and removeWidget(). It is not sufficient to pass + the widget stack as parent to a widget which should be inserted into + the widgetstack. + + visibleWidget() is the \e get equivalent of raiseWidget(); it + returns a pointer to the widget that is currently at the top of + the stack. + + QWidgetStack also provides the ability to manipulate widgets + through application-specified integer IDs. You can also translate + from widget pointers to IDs using id() and from IDs to widget + pointers using widget(). These numeric IDs are unique (per + QWidgetStack, not globally), but QWidgetStack does not attach any + additional meaning to them. + + The default widget stack is frameless, but you can use the usual + QFrame functions (such as setFrameStyle()) to add a frame. + + QWidgetStack provides a signal, aboutToShow(), which is emitted + just before a managed widget is shown. + + \sa QTabDialog QTabBar QFrame +*/ + + +/*! + Constructs an empty widget stack. + + The \a parent and \a name arguments are passed to the QFrame + constructor. +*/ + +QWidgetStack::QWidgetStack( QWidget * parent, const char *name ) + : QFrame( parent, name ) +{ + init(); +} + +/*! + Constructs an empty widget stack. + + The \a parent, \a name and \a f arguments are passed to the QFrame + constructor. +*/ +QWidgetStack::QWidgetStack( QWidget * parent, const char *name, WFlags f ) + : QFrame( parent, name, f ) //## merge constructors in 4.0 +{ + init(); +} + +void QWidgetStack::init() +{ + d = 0; + QWidgetStackEventFilter *ef = new QWidgetStackEventFilter( this ); + installEventFilter( ef ); + dict = new QIntDict<QWidget>; + focusWidgets = 0; + topWidget = 0; + invisible = new QWidgetStackPrivate::Invisible( this ); + invisible->hide(); +} + + +/*! + Destroys the object and frees any allocated resources. +*/ + +QWidgetStack::~QWidgetStack() +{ + delete focusWidgets; + delete d; + delete dict; +} + + +/*! + Adds widget \a w to this stack of widgets, with ID \a id. + + If you pass an id \>= 0 this ID is used. If you pass an \a id of + -1 (the default), the widgets will be numbered automatically. If + you pass -2 a unique negative integer will be generated. No widget + has an ID of -1. Returns the ID or -1 on failure (e.g. \a w is 0). + + If you pass an id that is already used, then a unique negative + integer will be generated to prevent two widgets having the same + id. + + If \a w already exists in the stack the widget will be removed first. + + If \a w is not a child of this QWidgetStack moves it using + reparent(). +*/ + +int QWidgetStack::addWidget( QWidget * w, int id ) +{ + static int nseq_no = -2; + static int pseq_no = 0; + + if ( !w || w == invisible ) + return -1; + + // prevent duplicates + removeWidget( w ); + + if ( id >= 0 && dict->find( id ) ) + id = -2; + if ( id < -1 ) + id = nseq_no--; + else if ( id == -1 ) + id = pseq_no++; + else + pseq_no = QMAX(pseq_no, id + 1); + // use id >= 0 as-is + + dict->insert( id, w ); + + // preserve existing focus + QWidget * f = w->focusWidget(); + while( f && f != w ) + f = f->parentWidget(); + if ( f ) { + if ( !focusWidgets ) + focusWidgets = new QPtrDict<QWidget>( 17 ); + focusWidgets->replace( w, w->focusWidget() ); + } + + w->hide(); + if ( w->parent() != this ) + w->reparent( this, contentsRect().topLeft(), FALSE ); + w->setGeometry( contentsRect() ); + updateGeometry(); + return id; +} + + +/*! + Removes widget \a w from this stack of widgets. Does not delete \a + w. If \a w is the currently visible widget, no other widget is + substituted. + + \sa visibleWidget() raiseWidget() +*/ + +void QWidgetStack::removeWidget( QWidget * w ) +{ + if ( !w ) + return; + int i = id( w ); + if ( i != -1 ) + dict->take( i ); + + if ( w == topWidget ) + topWidget = 0; + if ( dict->isEmpty() ) + invisible->hide(); // let background shine through again + updateGeometry(); +} + + +/*! + Raises the widget with ID \a id to the top of the widget stack. + + \sa visibleWidget() +*/ + +void QWidgetStack::raiseWidget( int id ) +{ + if ( id == -1 ) + return; + QWidget * w = dict->find( id ); + if ( w ) + raiseWidget( w ); +} + +static bool isChildOf( QWidget* child, QWidget *parent ) +{ + const QObjectList *list = parent->children(); + if ( !child || !list ) + return FALSE; + QObjectListIt it(*list); + QObject *obj; + while ( (obj = it.current()) ) { + ++it; + if ( !obj->isWidgetType() || ((QWidget *)obj)->isTopLevel() ) + continue; + QWidget *widget = (QWidget *)obj; + if ( widget == child || isChildOf( child, widget ) ) + return TRUE; + } + return FALSE; +} + +/*! + \overload + + Raises widget \a w to the top of the widget stack. +*/ + +void QWidgetStack::raiseWidget( QWidget *w ) +{ + if ( !w || w == invisible || w->parent() != this || w == topWidget ) + return; + + if ( id(w) == -1 ) + addWidget( w ); + if ( !isVisible() ) { + topWidget = w; + return; + } + + if (w->maximumSize().width() < invisible->width() + || w->maximumSize().height() < invisible->height()) + invisible->setBackgroundMode(backgroundMode()); + else if (invisible->backgroundMode() != NoBackground) + invisible->setBackgroundMode(NoBackground); + + if ( invisible->isHidden() ) { + invisible->setGeometry( contentsRect() ); + invisible->lower(); + invisible->show(); + QApplication::sendPostedEvents( invisible, QEvent::ShowWindowRequest ); + } + + // try to move focus onto the incoming widget if focus + // was somewhere on the outgoing widget. + if ( topWidget ) { + QWidget * fw = focusWidget(); + QWidget* p = fw; + while ( p && p != topWidget ) + p = p->parentWidget(); + if ( p == topWidget ) { // focus was on old page + if ( !focusWidgets ) + focusWidgets = new QPtrDict<QWidget>( 17 ); + focusWidgets->replace( topWidget, fw ); + fw->clearFocus(); + // look for the best focus widget we can find + // best == what we had (which may be deleted) + fw = focusWidgets->take( w ); + if ( isChildOf( fw, w ) ) { + fw->setFocus(); + } else { + // second best == first child widget in the focus chain + QFocusData *f = focusData(); + QWidget* home = f->home(); + QWidget *i = home; + do { + if ( ( ( i->focusPolicy() & TabFocus ) == TabFocus ) + && !i->focusProxy() && i->isVisibleTo(w) && i->isEnabled() ) { + p = i; + while ( p && p != w ) + p = p->parentWidget(); + if ( p == w ) { + i->setFocus(); + break; + } + } + i = f->next(); + } while( i != home ); + } + } + } + + if ( isVisible() ) { + emit aboutToShow( w ); + int i = id( w ); + if ( i != -1 ) + emit aboutToShow( i ); + } + + topWidget = w; + + const QObjectList * c = children(); + QObjectListIt it( *c ); + QObject * o; + + while( (o=it.current()) != 0 ) { + ++it; + if ( o->isWidgetType() && o != w && o != invisible ) + ((QWidget *)o)->hide(); + } + + w->setGeometry( invisible->geometry() ); + w->show(); +} + +/*! + \reimp +*/ + +void QWidgetStack::frameChanged() +{ + QFrame::frameChanged(); + setChildGeometries(); +} + + +/*! + \reimp +*/ + +void QWidgetStack::setFrameRect( const QRect & r ) +{ + QFrame::setFrameRect( r ); + setChildGeometries(); +} + + +/*! + Fixes up the children's geometries. +*/ + +void QWidgetStack::setChildGeometries() +{ + invisible->setGeometry( contentsRect() ); + if ( topWidget ) + topWidget->setGeometry( invisible->geometry() ); +} + + +/*! + \reimp +*/ +void QWidgetStack::show() +{ + // Reimplemented in order to set the children's geometries + // appropriately and to pick the first widget as topWidget if no + // topwidget was defined + if ( !isVisible() && children() ) { + const QObjectList * c = children(); + QObjectListIt it( *c ); + QObject * o; + + while( (o=it.current()) != 0 ) { + ++it; + if ( o->isWidgetType() ) { + if ( !topWidget && o != invisible ) + topWidget = (QWidget*)o; + if ( o == topWidget ) + ((QWidget *)o)->show(); + else + ((QWidget *)o)->hide(); + } + } + setChildGeometries(); + } + QFrame::show(); +} + + +/*! + Returns the widget with ID \a id. Returns 0 if this widget stack + does not manage a widget with ID \a id. + + \sa id() addWidget() +*/ + +QWidget * QWidgetStack::widget( int id ) const +{ + return id != -1 ? dict->find( id ) : 0; +} + + +/*! + Returns the ID of the \a widget. Returns -1 if \a widget is 0 or + is not being managed by this widget stack. + + \sa widget() addWidget() +*/ + +int QWidgetStack::id( QWidget * widget ) const +{ + if ( !widget ) + return -1; + + QIntDictIterator<QWidget> it( *dict ); + while ( it.current() && it.current() != widget ) + ++it; + return it.current() == widget ? it.currentKey() : -1; +} + + +/*! + Returns the currently visible widget (the one at the top of the + stack), or 0 if nothing is currently being shown. + + \sa aboutToShow() id() raiseWidget() +*/ + +QWidget * QWidgetStack::visibleWidget() const +{ + return topWidget; +} + + +/*! + \fn void QWidgetStack::aboutToShow( int ) + + This signal is emitted just before a managed widget is shown if + that managed widget has an ID != -1. The argument is the numeric + ID of the widget. + + If you call visibleWidget() in a slot connected to aboutToShow(), + the widget it returns is the one that is currently visible, not + the one that is about to be shown. +*/ + + +/*! + \fn void QWidgetStack::aboutToShow( QWidget * ) + + \overload + + This signal is emitted just before a managed widget is shown. The + argument is a pointer to the widget. + + If you call visibleWidget() in a slot connected to aboutToShow(), + the widget returned is the one that is currently visible, not the + one that is about to be shown. +*/ + + +/*! + \reimp +*/ + +void QWidgetStack::resizeEvent( QResizeEvent * e ) +{ + QFrame::resizeEvent( e ); + setChildGeometries(); +} + + +/*! + \reimp +*/ + +QSize QWidgetStack::sizeHint() const +{ + constPolish(); + + QSize size( 0, 0 ); + + QIntDictIterator<QWidget> it( *dict ); + QWidget *w; + + while ( (w = it.current()) != 0 ) { + ++it; + QSize sh = w->sizeHint(); + if ( w->sizePolicy().horData() == QSizePolicy::Ignored ) + sh.rwidth() = 0; + if ( w->sizePolicy().verData() == QSizePolicy::Ignored ) + sh.rheight() = 0; +#ifndef QT_NO_LAYOUT + size = size.expandedTo( sh ).expandedTo( qSmartMinSize(w) ); +#endif + } + if ( size.isNull() ) + size = QSize( 128, 64 ); + size += QSize( 2*frameWidth(), 2*frameWidth() ); + return size; +} + + +/*! + \reimp +*/ +QSize QWidgetStack::minimumSizeHint() const +{ + constPolish(); + + QSize size( 0, 0 ); + + QIntDictIterator<QWidget> it( *dict ); + QWidget *w; + + while ( (w = it.current()) != 0 ) { + ++it; + QSize sh = w->minimumSizeHint(); + if ( w->sizePolicy().horData() == QSizePolicy::Ignored ) + sh.rwidth() = 0; + if ( w->sizePolicy().verData() == QSizePolicy::Ignored ) + sh.rheight() = 0; +#ifndef QT_NO_LAYOUT + size = size.expandedTo( sh ).expandedTo( w->minimumSize() ); +#endif + } + if ( size.isNull() ) + size = QSize( 64, 32 ); + size += QSize( 2*frameWidth(), 2*frameWidth() ); + return size; +} + +/*! + \reimp +*/ +void QWidgetStack::childEvent( QChildEvent * e) +{ + if ( e->child()->isWidgetType() && e->removed() ) + removeWidget( (QWidget*) e->child() ); +} +#endif diff --git a/src/widgets/qwidgetstack.h b/src/widgets/qwidgetstack.h new file mode 100644 index 0000000..b87d668 --- /dev/null +++ b/src/widgets/qwidgetstack.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Definition of QWidgetStack class +** +** Created : 980306 +** +** 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. +** +**********************************************************************/ + +#ifndef QWIDGETSTACK_H +#define QWIDGETSTACK_H + +#ifndef QT_H +#include "qframe.h" +#include "qintdict.h" +#include "qptrdict.h" +#endif // QT_H + +#ifndef QT_NO_WIDGETSTACK + + +class QWidgetStackPrivate; + + +class Q_EXPORT QWidgetStack: public QFrame +{ + Q_OBJECT +public: + QWidgetStack( QWidget* parent=0, const char* name=0 ); + QWidgetStack( QWidget* parent, const char* name, WFlags f); + + ~QWidgetStack(); + + int addWidget( QWidget *, int = -1 ); + void removeWidget( QWidget * ); + + QSize sizeHint() const; + QSize minimumSizeHint() const; + void show(); + + QWidget * widget( int ) const; + int id( QWidget * ) const; + + QWidget * visibleWidget() const; + + void setFrameRect( const QRect & ); + +signals: + void aboutToShow( int ); + void aboutToShow( QWidget * ); + +public slots: + void raiseWidget( int ); + void raiseWidget( QWidget * ); + +protected: + void frameChanged(); + void resizeEvent( QResizeEvent * ); + + virtual void setChildGeometries(); + void childEvent( QChildEvent * ); + +private: + void init(); + + QWidgetStackPrivate * d; + QIntDict<QWidget> * dict; + QPtrDict<QWidget> * focusWidgets; + QWidget * topWidget; + QWidget * invisible; + +private: // Disabled copy constructor and operator= +#if defined(Q_DISABLE_COPY) + QWidgetStack( const QWidgetStack & ); + QWidgetStack& operator=( const QWidgetStack & ); +#endif +}; + +#endif // QT_NO_WIDGETSTACK + +#endif // QWIDGETSTACK_H |